Добрый день, уважаемые читатели Хабра! Это вторая статья из серии статей о практическом использовании ROS на Raspberry Pi. В первая статья В цикле я описал установку необходимых компонентов ROS и настройку рабочей среды для работы.
Во второй части серии мы начнем практически использовать возможности ROS на платформе Raspberry Pi. В частности, в этой статье я собираюсь рассказать об использовании платы камеры Raspberry Pi на Raspberry Pi в сочетании с ROS для решения проблем компьютерного зрения.
Кому интересно, смотрите кат.
Плата камеры RPi
Для работы нам понадобится вот такая камера Raspberry Pi Camera Board:Эта камера подключается напрямую к графическому процессору через разъем CSi на плате, что позволяет записывать и кодировать изображения с камеры, не используя время процессора.
Для подключения камеры используется кабель ZIF. Разъем для подключения кабеля на плате расположен между портами Ethernet и HDMI:
Установка библиотек
Итак приступим к установке необходимых библиотек.Сначала давайте установим OpenCV:
Чтобы использовать камеру Raspberry Pi, нам понадобится библиотека raspicam. Скачать архив отсюда .$ sudo apt-get install libopencv-dev
Далее устанавливаем библиотеку: $ tar xvzf raspicamxx.tgz
$ cd raspicamxx
$ mkdir build
$ cd build
$ cmake .
$ make
$ sudo make install
$ sudo ldconfig
Вам необходимо включить поддержку камеры в Raspbian через программу raspi-config: $ sudo raspi-config
Выберите вариант 5 — Включить камеру, сохраните выбор и перезагрузите систему.
Начало работы с РОС
Для простоты использования давайте создадим новое рабочее пространство catkin для наших пакетов: $ mkdir -p ~/driverobot_ws/src
$ cd ~/driverobot_ws/src
$ catkin_init_workspace
$ cd ~/driverobot_ws
$ catkin_make
Создайте новый пакет ROS:
$ cd src/
$ catkin_create_pkg raspi_cam_ros image_transport cv_bridge roscpp std_msgs sensor_msgs compressed_image_transport opencv2
Спецификация команды catkin_create_pkg следующая: catkin_create_pkg [зависит1] [зависит2], где вы можете указать в разделе «Зависимость» столько зависимостей, сколько захотите — библиотеки, которые будет использовать пакет. Эта команда создает скелет проекта ROS: пустой каталог src для сценариев узлов и файлов конфигурации CMakeLists.txt и package.xml. Я нашел простой скрипт, получающий кадры с камеры и публикующий их с помощью «издателя», и адаптировал его для ROS Indigo. Вы можете скачать его отсюда .
Для его использования вам необходимо установить драйвер камеры Raspberry Pi UV4L: $ curl http://www.linux-projects.org/listing/uv4l_repo/lrkey.asc | sudo apt-key add -
Добавьте следующую строку в файл /etc/apt/sources.list deb http://www.linux-projects.org/listing/uv4l_repo/raspbian/ wheezy main
, обновите пакеты и установите: $ sudo apt-get update
$ sudo apt-get install uv4l uv4l-raspicam
Чтобы загружать драйвер при каждой загрузке системы, мы также установим дополнительный пакет: $ sudo apt-get install uv4l-raspicam-extras
Перейдем к редактированию пакета.
Вставьте строки в файл CMakeLists.txt. отсюда .
Самые важные строки в файле CMakeLists.txt: link_directories(/usr/lib/uv4l/uv4lext/armv6l/)
…
target_link_libraries(capture ${catkin_LIBRARIES} uv4lext)
Таким образом мы добавляем ссылку на драйвер uv4l, необходимый для компиляции пакета.
В конце файла прописываем специальные строки для нашего узла: add_executable(capture src/capturer.cpp)
target_link_libraries(capture ${catkin_LIBRARIES} uv4lext)
Строка add_executable создает двоичный файл для запуска, а target_link_lbraries связывает дополнительные библиотеки для двоичного файла захвата.
Вставить недостающие строки отсюда в файл package.xml, чтобы он выглядел так: <Эxml version="1.0"?>
<package>
<name>raspi_cam_ros</name>
<version>0.0.0</version>
<description>The raspi_cam_ros package</description>
<maintainer email="[email protected]">pi</maintainer>
<license>TODO</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>cv_bridge</build_depend>
<build_depend>image_transport</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>opencv2</build_depend>
<build_depend>compressed_image_transport</build_depend>
<run_depend>cv_bridge</run_depend>
<run_depend>image_transport</run_depend>
<run_depend>roscpp</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>sensor_msgs</run_depend>
<run_depend>opencv2</run_depend>
<run_depend>compressed_image_transport</run_depend>
</package>
В первых строках задаются основные параметры узла — имя, версия, описание, информация об авторе.
Строки build_dependent определяют зависимости библиотеки, необходимые для компиляции пакета, а строки run_dependent определяют зависимости, необходимые для запуска кода в пакете.
Создайте файл capturer.cpp внутри папки src и вставьте строки отсюда .
Здесь в методе main() узел инициализируется и запускается в цикле: ros::init(argc, argv,"raspi_cam_ros");
ros::NodeHandle n;
UsbCamNode a(n);
a.spin();
Вся логика скрипта заключается в том, что мы получаем изображение с камеры с помощью OpenCV, оборачиваем его в сообщение для ROS в методе fillImage и публикуем в теме.
При этом используется пакет image_transport для создания «издателя» изображения.
Запустим драйвер uv4l, выполнив команду: $ uv4l --driver raspicam --auto-video_nr --width 640 --height 480 --nopreview
который создаст потоковую передачу MJPEG.
Давайте скомпилируем наш узел ROS: $ roscore
$ cd ~/driverobot_ws
$ catkin_make
Проверьте значение переменной ROS_PACKAGE_PATH: echo $ROS_PACKAGE_PATH
/opt/ros/indigo/share:/opt/ros/indigo/stacks
Значение переменной ROS_PACKAGE_PATH должно включать путь к нашей рабочей области.
Давайте добавим нашу рабочую область в путь: $ source devel/setup.bash
Теперь снова запустив echo $ROS_PACKAGE_PATH, мы должны увидеть такой вывод: /home/youruser/catkin_ws/src:/opt/ros/indigo/share:/opt/ros/indigo/stacks
, где /дом/ /catkin_ws/src — путь к нашему рабочему пространству.
Это означает, что ROS может «видеть» наши узлы, созданные в catkin_ws, и мы можем запускать их через rosrun.
Запустим наш узел ROS: $ rosrun raspi_cam_ros capture
Запустим графическую программу rqt_image_view для отображения видеопотока из темы: $ rosrun rqt_image_view rqt_image_view
Выберите тему image_raw в окне rqt_image_view.
При запуске узла может возникать ошибка «Gtk-WARNING**: невозможно открыть дисплей: -1» при работе через ssh или «GdkGLExt-WARNING**: Windows system не поддерживает OpenGL».
при запуске в режиме удаленного рабочего стола VNC. Решение — подключиться к Raspberry Pi через SSH с переадресацией X11: $ ssh -X pi@<host_pi>
Узнать, как часто публикуются сообщения в теме, можно с помощью команды rostopic: rostopic hz image_raw
Эта команда каждую секунду вычисляет частоту получения сообщений по теме и отображает ее в консоли.
Для модели B+ у меня был следующий результат: average rate: 7.905
min: 0.075s max: 0.249s std dev: 0.02756s
Как видите, частота публикации сообщений составляет 8 Гц.
Также я проверил частоту публикации изображений с камеры на модели RPi 2. Здесь результаты были намного лучше: average rate: 30.005
min: 0.024s max: 0.043s std dev: 0.00272s
Сообщения уже публикуются с частотой 30 Гц, что является довольно хорошим приростом скорости по сравнению с моделью B+.
Визуальное управление роботом с помощью OpenCV и ROS
Теперь мы напишем небольшой пакет ROS для использования компьютерного зрения на роботе с Raspberry Pi, который будет выполнять алгоритм распознавания (в нашем случае визуальное ориентирование с использованием метода следования по строке) и публиковать величину необходимого перемещения робота в виде тема.С другой стороны, узел управления движением робота подпишется на эту тему и отправит команды управления движением на Arduino. Теперь давайте добавим в скрипт capturer.cpp «издатель», который будет публиковать значение сдвига.
Для начала включим определение типа сообщения для значения сдвига — std_msgs/Int16. #include <std_msgs/Int16.h>
rosserial принимает специальные файлы сообщений msg и генерирует для них исходный код. Используется следующий шаблон: package_name/msg/Foo.msg → package_name::Foo
Исходный код стандартных сообщений Rosserial хранится в папке package_name внутри каталога ros_lib.
Далее инициализируем сообщение для этого типа: std_msgs::Int16 shift_msg;
Создаем «издателя»: ros::Publisher shift_pub;
и внутри конструктора UsbCamNode даем ему определение: shift_pub = nh.advertise<std_msgs/Int16>(“line_shift”, 1);
Здесь мы задаем тип сообщений и название темы для публикации.
Далее мы добавим логику расчета значения сдвига строки с помощью OpenCV и опубликуем ее в теме line_shift в методе take_and_send_image() перед строкой #ifdef OUTPUT_ENABLED: // Some logic for the calculation of offest
shift_msg.data = offset;
shift_pub.publish(shift_msg);
Готового алгоритма следования по строкам у меня нет, поэтому читатель волен написать здесь свою логику.
Фактические данные в сообщении хранятся в поле данных.
Структуру сообщения можно просмотреть с помощью команды: $ rosmsg show std_msgs/Int16
Теперь запустим узел: $ rosrun raspi_cam_ros capturer
Используем команду rostopic echo для отображения данных, опубликованных в теме line_shift: $ rostopic echo line_shift
Теперь добавим «абонента» в узел управления роботом.
Давайте включим определение типа сообщения: #include <std_msgs/UInt16.h>
Затем мы добавляем функцию обратного вызова, которая выполняется при получении сообщения из темы.
void messageCb(const std_msgs::UInt16& message)
{
int shift_val = int(message.data);
char* log_msg;
if(shift_val < 0) log_msg = "Left";
else if(shift_val > 0 ) log_msg = "Right";
else log_msg = "Forward";
nh.loginfo(log_msg);
}
Функция обратного вызова должна иметь тип void и принимать ссылку на константный тип сообщения в качестве аргумента.
Для простоты я записываю сообщение о направлении смещения линии.
Здесь вы можете добавить собственную логику движения робота для вашего сценария.
Создаем подписчика на сообщения из темы line_shift. ros::Subscriber<std_msgs::UInt16> sub("line_shift", &messageCb);
Здесь мы задаем название темы и ссылку на функцию обратного вызова.
Далее идут стандартные методы эскиза для rosserial_arduino: void setup()
{
nh.initNode();
nh.subscribe(sub);
Serial.begin(57600);
}
void loop()
{
nh.spinOnce();
delay(100);
}
Единственное отличие состоит в том, что мы добавляем nh.subscribe(sub) для создания фактической «подписки» узла на тему.
Скетч для управления роботом можно скачать отсюда .
Маленькая хитрость! В ROS есть специальные файлы запуска, которые позволяют автоматически запускать узлы как отдельные процессы с определенными параметрами.
файлы запуска создаются в формате xml, и их синтаксис позволяет запускать множество узлов одновременно.
Однако файл запуска не гарантирует, что узлы будут запущены именно в указанном порядке.
Вы можете создать файл запуска, чтобы упростить запуск сервера rosserial_python. $ cd <catkin_ws>/src
$ catkin_create_pkg rosserial_controller
$ cd src/rosserial_controller
$ vim rosserial_controller.launch
Напишем файл запуска следующего содержания: <launch>
<node pkg="rosserial_python" type="serial_node.py" name="arduino_serial">
<param name="port" value="/dev/ttyACM0"/>
</node>
</launch>
Давайте скомпилируем и запустим его: $ cd ~/<catkin_ws>
$ catkin_make
$ source devel/setup.bash
$ roslaunch rosserial_controller rosserial_controller.launch
Визуализировать значения, опубликованные в теме line_shift, мы можем с помощью утилиты rqt_plot, как это сделано в статья : $ rqt_plot line_shift
Теперь вы можете в полной мере воспользоваться преимуществами камеры Raspberry Pi и библиотеки OpenCV для визуальной ориентации вашего робота, распознавания объектов, отслеживания и многих других сценариев.
Дайте волю своему воображению! В следующий раз мы поговорим об управлении роботом в режиме телеуправления с помощью нажатия клавиш на клавиатуре.
ПС.
В сети есть полезные шпаргалки — ROS Cheatsheet. Для версии ROS Indigo вы можете скачать это отсюда .
Теги: #Робототехника #arduino #ros
-
Программисты Добрались До Новорожденных
19 Oct, 24 -
Загадочное Число 6174.
19 Oct, 24