Rosserial - связь двух Arduino через ROS

Начал изучать ROS по этим материалам >> (спасибо за перевод — по английским мануалам точно бы не смог )

Есть задумка с платформой iRobot, пока iRobot в пути поупражняюсь в простеньком.
Решил наладить передачу между двумя платами Arduino через ROS.
Библиотека rosserial (см. топик) устанавливает соединение точка-точка (point-to-point connection) через последовательный порт с недорогими контроллерами (типа Arduino) так, что вы можете посылать сообщения ROS туда и обратно.
ROS установлена на нетбуке ASUS (простенький, слабенький), Ubuntu 11.10, дистрибутив Electric
Для начала устанавливаем библиотеку rosserial.
Пробовал как рекомендовано на сайте ros.org —
hg clone https://kforge.ros.org/rosserial/hg rosserial
rosdep install rosserial_arduino
rosmake rosserial_arduino

Сразу возникли проблемы — ошибки — проблема общая как показал поиск по google
Решил так
sudo apt-get install ros-electric-rosserial 
rosdep install rosserial_arduino
rosmake rosserial_arduino

Все получилось

Rosserial состоит из общего p2p-протокола, библиотеки для работы с Arduino и узлов для ПК.
Библиотека для работы с Arduino находится в папке проекта serial, в каталоге serial_arduino/libraries. Копируем папку ros_lib в библиотечную директорию (libraries) Arduino IDE.

Две платы Arduino подключены по двум последовательным портам к нетбуку с ROS.

К одной плате Arduino (один узел ROS ) подключен датчик температуры DS18B20. Показания температуры будем отправлять будем публиковать в ROS (в тему temperature), другая плата Arduino (еще один узел ROS) будет получать показания температуры из темы temperature и отображать на дисплее WH0802.
В ROS создаем два узла serial_node.py пакета rosserial_python, который соединяет наши Arduino платы с остальной частью ROS (необходимо выставить используемый последовательный порт):
Создается узел так:
rosrun rosserial_python serial_node.py /dev/порт

Запустить два узла serial_node с одним именем не получится. Одна из особенностей ROS состоит в том, что вы можете переназначить имена (Names) узлов из командной строки:
$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 __name:=serial1
$ rosrun rosserial_python serial_node.py /dev/ttyACM0 __name:=serial2

Теперь необходимо написать скетчи на Arduino для узла publisher (отправляющего показания температуры в тему temperature) и узла subscriber (получающего показания из темы отображающего их на дисплее WH0802)
Используем библиотеку ros_lib

Скетч для publisher
В рамках каждой программы ROS Arduino необходимо включить заголовочный файл ros.h и файлы заголовков для всех типов сообщений, которые мы будем использовать, в нашем случае std_msgs/Float32.h:
#include <ros.h>
#include <std_msgs/Float32.h>
Далее, нам необходимо создать экземпляр объекта узла serial_node, что позволяет нашей программе выступать в качестве подписчика(subscriber), либо публиковать сообщения (publisher):
ros::NodeHandle nh;
Создаем экземпляр publisher для нашего узла serial_node, публикующий сообщения типа std_msgs::Float32 в тему temperature:
std_msgs::Float32 float32_msg;
ros::Publisher chatter(«temperature», &float32_msg);

В подпрограмме setup() необходимо инициализировать узел и объявить о роли узла chatter в качестве publisher:
nh.initNode();
nh.advertise(chatter);
В цикле loop() после считывания данных с датчика температуры публикуем сообщение в тему и вызываем ros::spinOnce(), где обрабатываются все функции обратного вызова соединения.
chatter.publish( &float32_msg );
nh.spinOnce();

Добавляем код для считывания данных с датчика температуры, и получается так:

#include <OneWire.h>
OneWire  ds(10);  // линия 1-Wire будет на pin 10

#include <ros.h>                    
#include <std_msgs/Float32.h>       

ros::NodeHandle  nh;
std_msgs::Float32 float32_msg;
ros::Publisher chatter("temperature", &float32_msg);

void setup(void) 
{
  nh.initNode();
  nh.advertise(chatter);
}
void loop(void) 
{
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  
  if ( !ds.search(addr)) {
      ds.reset_search();
      return;
  }
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // запускаем конвертацию
  delay(1000);     // скорее всего достаточно 750ms 
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // считываем ОЗУ датчика

  for ( i = 0; i < 9; i++) {           // обрабатываем 9 байт
    data[i] = ds.read();
  }
  Serial.print(" CRC=");
  Serial.print( OneWire::crc8( data, 8), HEX);
  Serial.println();
  // высчитываем температуру 
  int HighByte, LowByte, Temp;
  float Tempf1,Tempf2;
  LowByte = data[0];
  HighByte = data[1];
  Temp = (HighByte << 8) + LowByte;
  Tempf1=Temp/16; 
  Tempf2=(Temp%16)*100/16;
  float32_msg.data=Tempf1+Tempf2/100;
  // публикуем сообщение
  chatter.publish( &float32_msg );
  nh.spinOnce();
}


Скетч для subscriber

Включаем заголовочный файл ros.h и файлы заголовков для всех типа сообщений, которые мы будем использовать, в нашем случае std_msgs/Float32.h
#include <ros.h>
#include <std_msgs/Float32.h>
Далее, нам необходимо создать экземпляр объекта узла serial_node, что позволяет нашей программе выступать в качестве подписчика(subscriber), либо публиковать сообщения (publisher):
ros::NodeHandle nh;
Создаем экземпляр subscriber для нашего узла, получающийй сообщения типа std_msgs::Float32.h из темы temperature:
ros::Subscriber<std_msgs::Float32> sub(«temperature», &messageCb );

Создаем функцию обратного вызова messageCb для нашего узла. Функция обратного вызова должна постоянно получать сообщение в качестве аргумента. В нашем messageCb обратного вызова, тип сообщения std_msgs::Float32.
void messageCb( const std_msgs::Float32& toggle_msg){
digitalWrite(13, HIGH-digitalRead(13)); // blink the led
lcd.setCursor(0, 0);
lcd.print(«Temp=»);
lcd.setCursor(0, 1);
lcd.print(toggle_msg.data);}

В подпрограмме setup() необходимо инициализировать узел и объявить о роли узла в качестве подписчика на сообщения:
nh.initNode();
nh.subscribe(sub);
И наконец, в цикле loop() и вызываем ros::spinOnce(), где обрабатываются все функции обратного вызова соединения.

nh.spinOnce();
Добавляем код вывода сообщений на дисплей и получаем:

// подключить библиотеку LiquidCrystal
#include <LiquidCrystal.h>
// создание экземпляра объекта LiquidCrystal
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// rosserial 
#include <ros.h>
#include <std_msgs/Float32.h>

ros::NodeHandle  nh;
void messageCb( const std_msgs::Float32& toggle_msg){
  digitalWrite(13, HIGH-digitalRead(13));   // blink the led
  lcd.setCursor(0, 0);
  lcd.print("Temp=");
  lcd.setCursor(0, 1);
  lcd.print(toggle_msg.data);}

ros::Subscriber<std_msgs::Float32> sub("temperature", &messageCb );

void setup() {
  lcd.begin(8, 2);
  pinMode(13, OUTPUT);
  nh.initNode();
  nh.subscribe(sub);
}

void loop() {
  nh.spinOnce();
  delay(1000);
}


Теперь проверяем как все работает:
$ roscore
$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 __name:=serial1
$ rosrun rosserial_python serial_node.py /dev/ttyACM0 __name:=serial2

Проверяем список активных узлов
$ rosnode list

Ответ должен быть
/rosout
/serial1
/serial2

Зупускаем утилиту rxgraph
$ rxgraph



Видим на дисплее отображение температуры, дотрагиваемся до датчика, температура растет, показания на дисплее изменяется

Можно посмотреть публикацию сообщений узлом serial1
rostopic echo temperature

И видим
  • +3
  • 17 сентября 2012, 14:23
  • victoruni

Комментарии (1)

RSS свернуть / развернуть
+
0
Отличная работа!
avatar

admin

  • 17 сентября 2012, 19:44

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.