Roslaunch, управление запуском
Раньше мы использовали для запуска утилиту rosrun
, которая запускает конкретный исполняемый файл.
В реальных системах, одновременно должны работать множество программ. Для их запуска и конфигурации служит утилита roslaunch
Используя roslaunch
возможно дополнительно настраивать исполняемые файлы в момент их запуска (передавать параметры, изменять имена и тп)
roslaunch
использует файлы с расширением .launch, которые представляет собой обычный XML файл.
Давайте создадим файл ./launch/demo.launch
<launch>
<node pkg="test_package" type="topic_publisher.py" name="topic_publisher1"/>
<node pkg="test_package" type="topic_subscriber" name="topic_subscriber1"/>
<node pkg="test_package" type="topic_publisher.py" name="topic_publisher2"/>
<node pkg="test_package" type="topic_subscriber" name="topic_subscriber2"/>
</launch>
Теги, необходимые для запуска узла с помощью команды roslaunch
, описаны в теге launch
. Тег node
описывает ноды, который которые необходимо запускать с помощью roslaunch
. Параметры включают «pkg», «type» и «name».
Параметр | Описание |
---|---|
pkg | Имя пакета |
type | Название ноды, которая будет выполняться |
name | Имя (исполняемое имя), используемое при выполнении ноды, соответствующего выше параметру type. Обычно это имя совпадает, но при его запуске можно использовать другое имя. |
После сохранения файла, мы можем его запустить, выполнив
$ roslaunch test_package demo.launch --screen
SUMMARY
========
PARAMETERS
* /rosdistro: kinetic
* /rosversion: 1.12.13
NODES
/
topic_publisher1 (test_package/topic_publisher.py)
topic_publisher2 (test_package/topic_publisher.py)
topic_subscriber1 (test_package/topic_subscriber)
topic_subscriber2 (test_package/topic_subscriber)
ROS_MASTER_URI=http://localhost:11311
process[topic_publisher1-1]: started with pid [24963]
process[topic_subscriber1-2]: started with pid [24964]
process[topic_publisher2-3]: started with pid [24965]
process[topic_subscriber2-4]: started with pid [24978]
Если добавить параметр --screen
, информация о работе запущенных программ будет отображенна на экране текущего терминала.
Или указав полный путь к .launch файлу
$ roslaunch /home/user/catkin_ws/src/test_package/launch/demo.launch --screen
Как мы видим, запущенно 4 процесса с разными pid.
Также мы можем увидеть список запущенных нод
$ rosnode list
/rosout
/topic_publisher1
/topic_publisher2
/topic_subscriber1
/topic_subscriber2
Обьединение процессов в группы
Если мы посмотрим на список топиков
$ rostopic list
/hello
/rosout
/rosout_agg
То поймем, что оба Издателя (Publisher) публикуют данные в один топик /hello
(других топиков не создано) и каждый из подписчиков получает сообщения сразу от двух Издателей. Скорее всего, такой режим работы нам не интересен.
Если мы хотим чтобы один конкретный Издатель и Подписчик были изолированы от аналогичных процессов, мы можем обьеденить их в группы.
Создадим новый файл запуска ./launch/demo1.launch
<launch>
<group ns="ns1">
<node pkg="test_package" type="topic_publisher.py" name="topic_publisher"/>
<node pkg="test_package" type="topic_subscriber" name="topic_subscriber"/>
</group>
<group ns="ns2">
<node pkg="test_package" type="topic_publisher.py" name="topic_publisher"/>
<node pkg="test_package" type="topic_subscriber" name="topic_subscriber"/>
</group>
</launch>
Запустим новый файл
$ roslaunch test_package demo1.launch
$ rostopic list
/ns1/hello
/ns2/hello
/rosout
/rosout_agg
Мы видим, что создано два отдельных топика, с которым работает один Издатель и один Подписчик.
$ rostopic info /ns1/hello
Type: std_msgs/String
Publishers:
* /ns1/topic_publisher (http://cola:44225/)
Subscribers:
* /ns1/topic_subscriber (http://cola:44043/)
$ rostopic info /ns2/hello
Type: std_msgs/String
Publishers:
* /ns2/topic_publisher (http://cola:45259/)
Subscribers:
* /ns2/topic_subscriber (http://cola:35017/)
Установка переменных окружения при запуске
Очень часто, возникает необходимость конфигурировать исполняемые файлы в момент их запуска. Например нужно иметь возможность изменять скорость Serial
порта и его адрес. Каждый раз менять эти переменные в коде не удобно и очень долго.
Чтобы решить данную проблему, мы можем использовать возможность передавать конфигурационные переменные в запускаемые программы через .launch
файлы
Рассмотрим пример
<launch>
<node pkg="test_package" type="test_params.py" name="test_params" output="log" respawn="true">
<param name="port" value="/dev/ttyS0"/>
<param name="boud" value="57600"/>
</node>
</launch>
Мы видим что в ветке node
добавились элементы param
с настройками. Открывая такой файл, сразу видно, какие параметры возможно конфигурировать и их значения по умолчанию.
Для того, чтобы исполняемый файлы смогли обрабатывать эти параметры, необходимо добавить для них специальный код. Это не сложно, и для python
может выглядеть так
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
rospy.init_node('test_params')
r = rospy.Rate(10) # 10hz
port = rospy.get_param('~port','/dev/ttyS0')
while not rospy.is_shutdown():
print(port)
r.sleep()
Вторым параметром в функции get_param
указывается значение по умолчанию, если параметр не определен в .launch
файле.
Параметры возможно передавать при запуске через rosrun
rosrun test_package test_params.py _port:=/dev/ttyS1
Еще одна удобная практика для работы с параметрами, это перенос настроек в начала файла, перечислив их как аргументы. Это можно сделать используя элемент arg
а далее обращение к этим аргумента в формате $(arg env_name)
<launch>
<arg name="device" default="/dev/ttyS0"/>
<arg name="boud" default="57600"/>
<node pkg="test_package" type="test_params.py" name="test_package" output="log" respawn="true">
<param name="port" value="$(arg device)"/>
<param name="boud" value="$(arg boud)"/>
</node>
</launch>
Эта практика позволяет не листать большие файлы и всегда иметь перед глазами самые важные настройки.
Подключение других .launch файлов <include>
В реальных проектах, запускаются десятки нод. Конфигурировать каждую из них в одном файле, не всегда удобно. К тому-же обычно сторонние пакеты уже содержат подходящие .launch
файлы. Поэтому существуют механизм include
, который позволяет подключать другие файлы запуска.
Приведем пример
<launch>
<include file="$(find test_package)/launch/test_params.launch">
<arg name="device" value="/dev/ttyS1"/>
</include>
<include file="$(find navibro)/camera/camerav1_640x480.launch"/>
<include file="$(find navibro)/launch/aruco_detect.launch"/>
<include file="$(find navibro)/launch/fiducial_slam.launch"/>
</launch>
В этом примере, мы подключаем файл test_params.launch
, который находиться в нашем пакете и настраиваем его на работу через устройство /dev/ttyS1.
. А также подключаем три других .launch
файла из другого пакета.
Использование условий if и unless
При написании сложный .launch файлов, очень помагают атрибуты if
и unless
, которые позволяют формировать простые алгоритмы ветвления при работе с roslaunch
Приведем несколько примеров
<launch>
<arg name="have_serial" value="true"/>
<group if="$(arg have_serial)">
<!-- Блок выполниться только если have_serial установлено в true -->
<node pkg="test_package" type="test_params.py" name="test_package" output="log" respawn="true">
</group>
<!-- Также if можно использовать для одного тега-->
<include if="$(arg have_serial)" file="$(find test_package)/launch/test_params.launch">
<arg name="device" value="/dev/ttyS1"/>
</include>
</launch>
Атрибут unless
работает противоположно атрибуту if
. Если значение 0
то блок выполняется.
Значение атрибутов для if
и unless
должно быть булевым (0,1,true,false
)