Продвигаемся к улыбке
Собрав «бургер» по схеме из прошлого поста, перейдем к программному наполнению.
Так как мы собираем по уже готовому проекту, логично привести инструкции, в нем указанные. Они находятся здесь.
Все очень удобно и там же можно скачать уже готовый образ с Raspbian Stretch + ROS + OpenCV, записать его на sd карту для raspberry. (ROS Kinetic, OpenCV 3.4.1. Да, есть и поновее, но иногда лучше взять и поехать, чем собирать все самому из исходников).
Тем не менее, не смотря на удобство, мне все же пришлось немного поправить image. Так как выяснились некоторые неудобные детали оригинального образа:
- нет GUI (графического интерфейса). Это не критично, тем более для ROS, но для езды по линии надо калибровать камеру на самой raspberry, и видеть как она (камера) передает цвета (об этом ниже);
- сборка OpenCV не выводит изображение на экран, даже если самостоятельно поставить GUI. Очевидно в проекте rosbots opencv собиралась без этой опции.
- нет мелких костылей (VNC, текстового редактора для записей, mc).
- Поэтому, OpenCV был пересобран с поддержкой вывода изображения в GUI (собрана openCV 3.4.3), установлен GUI, мелкие костыли.
Готовый образ здесь, и дальше работа будет строиться на его основе.
Настроим сеть (wi-fi) и ROS-master на raspberry pi
.Настоятельно рекомендую для опытов использовать отдельный роутер со своим wi-fi. Можно для этих целей просто создать точку доступа на телефоне. Это связано с тем, что по wi-fi будет летать достаточно много пакетов и, желательно, чтобы они не тонули в общем трафике.
После заливки образа на sd карту raspberry, настроим сеть. Первоначальные настройки сети такие:
interface wlan0
static ip_address=192.168.43.174/24
static routers=192.168.43.1
static domain_name_servers=192.168.43.1
Содержатся в /etc/dhcpcd.conf
Поэтому можно не подключать шланги к raspberry, чтобы все поменять, а просто создать точку доступа с пафосным названием boss и паролем 1234554321. Адрес raspberry будет 192.168.43.174. Так же на этот адрес можно кроме ssh зайти по VNC: логин — pi, пароль — 123qweasdzxcV.
Настроим ROS-мастер
Небольшая ремарка для тех, кто не сталкивался с ROS (robotic operation system). ROS-мастер — это посредник, через который в ros общаются разные узлы (ноды, сервисы и т.п.) Если ros мастер не запущен или запущен не по тому адресу, ноды друг друга не увидят.
В нашей системе ROS мастер стартует автоматически с загрузкой ОС и все, что от нас требуется, указать ip-адрес для ROS-мастера в соответствующем файле системы.
Если вы настройки сети не меняли, которые указананы выше, то и настраивать ничего не требуется.
В противном случае правим bashrc:
nano ~/.bashrc
В самом конце файла исправьте ip адреса (оба) на ваш случай:
export ROS_MASTER_URI=http://192.168.43.174:11311
export ROS_HOSTNAME=192.168.43.174
Перезагрузитесь.
Теперь при запуске терминала на тележке, вывод будет таким (либо тем, что вы указали в настройках):
For all slaves, "export ROS_MASTER_URI=http://192.168.43.174:11311"
Это означает, что ROS мастер работает по указанному ip адресу.
Управляем тележкой по wi-fi
С начала проверим, что у нас работают ноды.
В терминале:
rosnode list
Вывод будет таким:
/rosout
/uno_serial_node
Если ничего не вывело, то проверьте прописали ли вы ROS-master в настройках как указано выше, подключили ли шланг usb к arduino, перезагрузились.
После проверки запустим 1-ю ноду, отвечающую за движение:
rosrun rosbots_driver part2_cmr.py
*специальная ros команда запускает из пакета rosbots_driver python файл part2_cmr.py
Система сообщит, что нода запущена:
Здесь видно, что определен радиус колес и расстояние между ними. Исправить эти значения, как и иные, связанные с движением можно в файле robot.py по пути
/home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/examples/coursera_control_of_mobile_robots/part2/full/controller
так как в самом part2_cmr.py этих параметров нет. Откроем второй терминал и введем rostopic list:
Здесь видно, что появился топик /part2_cmr/cmd_vel. В этом топике /part2_cmr «слушает», что ему будут говорить другие ноды и, в зависимости от того, что скажут, будет управлять движением. О том, что именно «слушает», а не «говорит» можно понять, используя команду.
rostopic info /part2_cmr/cmd_vel
Здесь видно, что /part2_cmr subscriber (подписалась) на топик и слушает.
*В топик можно самостоятельно, без нод, что-то «сказать».
Например:
rostopic pub -1 /wheel_power_left std_msgs/Float32 '{data: 1.0}'
повращать вперед левым колесом
rostopic pub -1 /wheel_power_left std_msgs/Float32 '{data: 0.0}'
остановить левое колесо
rostopic pub -1 /wheel_power_left std_msgs/Float32 '{data: -1.0}'
Повращать назад назад колесом
rostopic pub -1 /wheel_power_left std_msgs/Float32 '{data: -0.5}'
Повращать назад левым колесом помедленнее.
Синтаксис такой: rostopic pub — желание говорить в топик, -1 — разовое желание, /wheel_power_left — топик, куда говорим, std_msgs/Float32 — язык (формат сообщений),'{data: -0.5}' — что говорим.
Теперь запустим того, кто будет говорить в топик /part2_cmr/cmd_vel. Это будет нода отправки команд с клавиатуры.
Не закрывая предыдущий терминал с работающий нодой, запустим еще один и введем:
rosrun teleop_twist_keyboard teleop_twist_keyboard.py /cmd_vel:=/part2_cmr/cmd_vel
*Так как публикация по умолчанию ведется в топик /cmd_vel, мы его перенаправляем используя
/cmd_vel:=/part2_cmr/cmd_vel, чтобы сообщения сыпались именно в /part2_cmr/cmd_vel.
Нода управления запустилась и можно поездить, нажимая клавиши на клавиатуре:
Если ехать не получается или идет едва уловимый писк из под колес — надо увеличить скорость, нажимая на «w» в терминале с запущенной нодой. То же самое (увеличить или уменьшить) можно сделать со скоростью поворота — кнопка «e». Важно также находиться в терминале с запущенной нодой, если переключиться в другой терминал кнопки управления работать не будут. Кнопка «k» в терминале управления — это стоп.
В отдельном терминале посмотрим на топик /part2_cmr/cmd_vel:
Теперь в топике /part2_cmr/cmd_vel есть и говорящий, и слушающий.
Едем по линии на OpenCV
Перед тем как куда-то поехать, надо убедиться, что робот ездит при управлении с клавиатуры. Здесь важная ремарка необходима. При управлении с клавиатуры в примере выше, поворот налево должен соответствовать нажатию клавиши j, направо l (латинская л), вперед i, назад ,(запятой). Если в вашем случае это не так, то могут быть проблемы с поездкой. Чтобы все привести в норму, надо на arduino в нашем бургере поменять проводные пары, идущие с драйвера двигателя на ноги 4,5,6,7 arduino: 4,5 поменять местами с 6,7 либо 4 и 5,6 и 7 друг с другом в зависимости, куда будут крутиться колеса. Можно это также сделать программно, поправив код для arduino по пути — /home/pi/gitspace/rosbots_driver/platformio/rosbots_firmware/examples/motor_driver/src/main.cpp
#define M_LEFT_PWM 6
#define M_LEFT_FR 7
#define M_RIGHT_PWM 5
#define M_RIGHT_FR 4
и перезалив его на arduino командой:
upload_firmware ~/gitspace/rosbots_driver/platformio/rosbots_firmware/examples/motor_driver
Поработаем с цветами
Наш садоводческий опыт будет заключаться в том, чтобы выделить линию на полу, по которой поедет робот, определить ее цвет. По умолчанию робот ее не видит. В качестве линии можно использовать либо скотч (желтый) либо изоленту либо что-то иное с характерным цветом и достаточно широкое. *Прозрачный скотч вряд ли подойдет, т.к. его будет сложно выделить на фоне.
Зайдем в папку и запустим скрипт:
cd /home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/rosbots_driver
python bgr-to-hsv.py
*Внимание! Если вы используете оригинальный образ от rosbots, а не мой, этой программы там нет.
Откроется два окна:
Перед нами диапазоны цветов в HSV. Что такое hsv и почему не rgb, прошу погуглить самостоятельно.
h1,s1,v1 — нижний и h2,s2,v2 — соответственно, верхний диапазон.
Теперь надо выделить линию с изолентой (возможно не изолентой а скотчем) на полу, двигая ползунки в окне. В окне result должна остаться только линия изоленты:
Линия изоленты непривычно белого цвета, все остальное — черное. Этот результат необходим.
Запишем, запомним цифры HSV диапазонов. Мой случай — 56,155,40 и 136,255,255. Диапазоны HSV будут разными при разной освещенности около камеры робота.
Закроем окна, введя ctrl+c в терминале и внесем HSV диапазоны в файл follow_line_step_hsv.py:
cd /home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/rosbots_driver
nano follow_line_step_hsv.py
В строках:
lower_yellow = np.array([21,80,160])
upper_yellow = np.array([255,255,255])
Поставим цифры своих HSV диапазонов.
Время ехать по линии
Запускаем ноду-мотор в терминале 1:
rosrun rosbots_driver part2_cmr.py
Запускаем камеру-ноду во втором терминале:
sudo modprobe bcm2835-v4l2
roslaunch usb_cam usb_cam-test.launch
Запускаем ноду opencv в третьем терминале:
cd /home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/rosbots_driver
python follow_line_step_hsv.py
Если все пошло удачно, то робот поедет по линии, а также появится дополнительное окно:
В этом окне изолента будет помечаться красным кругом.
Общий смысл кода — выделить цветовой сегмент на определенном расстоянии от камеры, нарисовать красный кружок и ехать к этому кругу, стараясь, чтобы он находился в центре.
Наконец, о важном — о котах и улыбках
Так как наша цель — поехать к коту или к улыбающемуся человеку, то нам придется использовать что-то посложнее в своем коде. Также нам понадобятся коты и улыбающиеся люди. Со вторым сейчас сложнее: мало кто улыбается в это непростое, тревожное время. Поэтому начнем с котов.
Для экспериментов подойдут фото котов в фас.
Запустим в 1-м терминале камеру-ноду:
cd /home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/rosbots_driver
python pi_camera_driver.py
Во 2-м терминале ноду-мотор:
rosrun rosbots_driver part2_cmr.py
В 3-м терминале ноду-поиска кота:
cd /home/pi/rosbots_catkin_ws/src/rosbots_driver/scripts/rosbots_driver
python follow_cat2.py
Тележка понемногу будет двигаться к коту:
Теперь потребуется доброволец, который умеет улыбаться. Возьмем портрет малоизвестной публичной личности небольшой страны.
В 3-м терминале ноду-поиска кота можно закрыть — ctrl+c и вместо ее запустить поиск улыбки на лице малоизвестной публичной личности:
python follow_smile.py
Тележке придется медленно, недоверчиво ехать к улыбке малоизвестной персоны:
Как, возможно, многие уже догадались, в скриптах, которые мы запускали используются каскады Хаара. По тому же принципу, что и с поездкой по линии, выделяется квадрат искомой области и программа пытается удержать его в центре, двигая робота.
К сожалению, производительность на raspberry 3b оставляет желать лучшего, несмотря на настройки камеры 320x240 и 15 Fps. Задержки ощутимы с нарастанием времени. Не каждый кот выдержит.
Как это можно улучшить?
Попробовать пересобрать оптимизированную opencv, как рекомендует Адриан (https://www.pyimagesearch.com/2017/10/09/optimizing-opencv-on-the-raspberry-pi/)? Использовать ресурсы внешнего ПК для обработки изображений? Попробовать не сжимать картинки в jpeg, которые летят в обработчик Хаара? Да и еще один большой минус — коты должны быть большими и в фас. Расстояние 15 см на листе A4. При удалении от камеры, кот уже неузнаваем и неуязвим. Поставить на камеру raspberry монокль с 8x увеличением?
P.S.: Если у вас дойдут руки до экспериментов с образом, который приведен в статье, то можете поездить еще за различными частями тела, соответственно запуская вместо ноды-кота:
python follow_fullbody.py
python follow_upperbody.py
python follow_lowerbody.py
лицом или глазом:
python follow_face.py
python follow_right_eye.py
Если есть интерес к тому как плавно трогаться с места, чтобы робот не разливал чай, а также как управлять им не с самой raspberry, напишите.
NetBUG
Прикольно, я не увидел в прошлом посте, что стек «робот + нода в ROS» готовый.
По-моему, реализация отличная. Кстати, чистое детектирование улыбки/лица haar-каскадом на 3B может работать в пределах 100 мс при 640*480; после первоначального нахождения цели можно вместо каскада использовать object tracking — особенно если объект относительно небольшой. Вот неплохой обзор: www.pyimagesearch.com/2018/07/30/opencv-object-tracking
У меня с KCF был успешный опыт обработки полудюжины объектов на кадре
zoldaten Автор
Спасибо, подзабыл про это решение.