Добрый день, уважаемые читатели Хабра!

Я продолжаю серию статей о практическом использовании ROS на Raspberry Pi (первая статья, вторая статья).
В данной статье мы будем использовать пакет teleop_twist_keyboard из стека ros-teleop для управления роботом с помощью нажатия клавиш на клавиатуре. Стек ros-teleop включает помимо данного пакета еще несколько пакетов для различных способов управления, например с помощью джойстика. Кто готов приступить к изучению teleop_twist_keyboard, прошу под кат.

Мультитерминал с tmux


Между делом, я вам хочу рассказать об одном приеме, который позволяет работать удаленно на Raspberry Pi через SSH одновременно в нескольких терминалах. Для этого на RPi нужно установить утилиту tmux.

$ sudo apt-get install tmux

После этого запускаем утилиту:

$ tmux

Внизу окна терминала должна появиться зеленая полоса с номером окна — 0.
tmux — это очень удобный менеджер терминальных окон, который позволяет создать в одном окне терминала любое число окон, размещая их несколькими различными способами (отдельное окно-терминал (window), панель окна (pane)) и удобно переключаться между ними.

Нажмите на клавиатуре Ctrl + B и C. Должно открыться еще одно окно с номером 1. Также попробуйте комбинацию Ctrl + B и %. Текущее окно терминала будет разделено посередине вертикальной зеленой полосой на два окна (pane). Если нажать комбинацию Ctrl + B,: и ввести “split window”, то окно будет разделено горизонтально на два одинаковых окна. Чтобы удалить панель (pane) воспользуйтесь комбинацией Ctrl + B, X и затем нажмите Y. Чтобы переключиться на другую панель в том же окне, используйте комбинацию Ctrl + B, O. Чтобы переключиться между окнами-терминалами по номеру окна, используйте комбинацию Ctrl + B, <номер окна>.

Теперь добавим запуск программы в файл ~/.bashrc для автоматического запуска при открытии нового терминала. Добавьте следующие строки в файл:

[[ $TERM != "screen" ]] && exec tmux


Работа с пакетом teleop_twist_keyboard


Теперь познакомимся с пакетом teleop_twist_keyboard.
Запустим скрипт teleop_twist_keyboard.py из пакета teleop_twist_keyboard как обычный узел ROS:

$ roscore
$ rosrun teleop_twist_keyboard teleop_twist_keyboard.py

Мы получим вывод такого вида:

Reading from the keyboard  and Publishing to Twist!
---------------------------
Moving around:
   u    i    o
   j    k    l
   m    ,    .

q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%
anything else : stop

CTRL-C to quit

Выведем список всех активных на данный момент топиков:

$ rostopic list

Топик /cmd_vel должен появиться в списке. На этот топик узел teleop_twist_keyboard публикует сообщения каждый раз, когда была нажата клавиша на клавиатуре.
Покажем вывод сообщений, публикуемых в топик /cmd_vel:

$ rostopic echo cmd_vel

Запустим rqt_graph для представления вычислительного графа ROS в графическом виде. Схема вычислительного графа показывается все активные узлы и топики, которые их связывают.

$ rosrun rqt_graph rqt_graph

image

Здесь мы видим, что узел teleop_twist_keyboard публикует собщения в топик /cmd_vel, а узел rostopic подписывается на этот топик (команда rostopic echo).
Давайте узнаем какого типа сообщения публикуются на топик /cmd_vel:

$ rostopic type /cmd_vel

Команда выведет строку:

geometry_msgs/Twist

Это означает, что сообщения имеют тип Twist из стандартного ROS пакета geometry_msgs.
Мы также можем получить информацию о структуре сообщения командой rosmsg:

$ rosmsg info geometry_msgs/Twist


geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z

Поле ‘linear’ отвечает за линейную скорость, ‘angular’ — за угловую скорость.
Нажмите клавишу ‘i’, вывод будет таким (ассоциируется с движением вперед):

linear: 
  x: 0.5
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 0.0
---

Нажмите клавишу ‘k’, вывод будет таким (остановка):

linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 0.0
---

Нажмите клавишу ‘u’, вывод будет таким (поворот влево):

linear: 
  x: 0.5
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 1.0
---

И наконец при нажатии на клавишу ‘o’ получим такой вывод (поворачиваем вправо):

linear: 
  x: 0.5
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: -1.0
---

Клавиши ‘j’ и ‘l’ отвечают за повороты влево и вправо на месте (без движения вперед).

Управление роботом с клавиатуры с teleop_twist_keyboard


У меня уже написан скетч rosserial для управления роботом посредством нажатия клавиш. Мы просто подписываемся на топик /cmd_vel и в зависимости от получаемого значения каждой из скоростей отдаем необходимую команду контроллеру движения (двигаться вперед, остановиться, повернуть влево или вправо). Скетч можно скачать отсюда.
Разберем подробнее код скетча.
В начале файла подключаем помимо стандартного заголовочного файла ros.h два дополнительных файла с типами сообщений geometry_msgs/Twist.h и geometry_msgs/Vector3.h:

#include <geometry_msgs/Twist.h>
#include <geometry_msgs/Vector3.h>

Мы объявляем обработчик узла ros::NodeHandle:

ros::NodeHandle nh;

Основное значение имеет метод messageCb:

void messageCb(const geometry_msgs::Twist& message) 
{
  geometry_msgs::Vector3 linear = message.linear;
  float forward_vel = float(linear.x);
  
  if(forward_vel == 0) { stop(); return; }
  
  geometry_msgs::Vector3 angular = message.angular;
  float ang_vel = float(angular.z);
  
  if(ang_vel > 0) { turnLeft(); }
  else if(ang_vel < 0) { turnRight(); }
  else { goForward(); }
}

В этом методе мы производим обработку получаемого сообщения из топика cmd_vel. В переменной forward_vel мы сохраняем линейную скорость, в переменной angular — угловую. Линейная скорость нам позволяет отследить команду остановки (значение 0). Угловая скорость определяет направление поворота (если больше 0, то поворачиваем влево, меньше 0 — вправо, если 0 — двигаемся вперед).
Создаем подписчика на топик /cmd_vel:

ros::Subscriber<geometry_msgs::Twist> sub("/cmd_vel", &messageCb);

со указанием ссылки на метод обработки сообщений (messageCb) и типа принимаемого сообщения — geometry_msgs::Twist.
В конце скрипта следуют стандартные методы скетча для rosserial_arduino:

nh.initNode();
nh.subscribe(sub);
Serial.begin(57600);

Запустите rosserial_arduino сервер:

$ rosrun rosserial_python serial_node _port:=/dev/ttyACM0

и загрузите скетч на плату Arduino.

Переключитесь на терминал, где запущен узел teleop_twist_keyboard, попробуйте нажатие клавиш ‘u’, ‘i’, ‘o’ и ‘k’ и проверьте в терминале вывод сервера rosserial.

Таким образом, используя пакет teleop_twist_keyboard мы теперь можем выполнять простое удаленное управление роботом передавая команды движения: двигаться вперед, остановиться, повернуть влево или вправо. В дальнейшем мы научимся управлять роботом с помощью джойстика, что значительно удобнее, с применением уже другого пакета в ROS. Но об этом в следующей статье.

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


  1. Hellsy22
    02.03.2016 00:35
    +1

    С интересом ознакомился со всем циклом статей.

    К сожалению, я не совсем понимаю область применения комбинации ROS и RPI. В конкретном примере с роботом куда проще (и дешевле) было бы взять смартфон с USB OTG и воткнуть в него Arduino Uno/Mini, получив камеру и возможность управлять роботом дистанционно, хоть через wifi, хоть с помощью GSM. Да и ROS под андроид тоже есть.

    Было бы замечательно услышать какие-нибудь примеры, в которых использование ROS+RPI оправдано задачей.


    1. iliasam
      02.03.2016 01:26

      Да и ROS под андроид тоже есть.

      Там вроде бы пока многое еще не доделано.
      RPI хорошо подходит для прямого управления периферийными устройствами (там есть UART, SPI, I2С, GPIO), камера на шлейфе, которую можно закрепить как угодно.
      В случае с телефоном придется писать отдельную прошивку под Arduino, изобретать протокол связи межу ними и.т.д.


    1. vovaekb90
      05.03.2016 19:32

      Меня заинтересовала связка ROS и RPi в связи с давней идеей добавить возможности компьютерного зрения своему роботу на Arduino. Также хотелось "отвязать" робота от лаптопа и сделать его полностью автономным. rosserial оказался подходящим выбором для связки Arduino и RPi. Управление со смартфона я не рассматривал. У меня уже были на руках Arduino и RPi, поэтому решил попробовать с ними.


  1. vubdub
    03.03.2016 01:14
    +1

    Кстати, вышла новая версия платы:
    www.raspberrypi.org/blog/raspberry-pi-3-on-sale


    1. vovaekb90
      05.03.2016 19:34

      Спасибо за сообщение. Описание впечатляет, в частности модуль LAN и Bluetooth