1. Написание простого publisher и subscriber (Python)
Описание: На этом уроке рассказывается, как написать узлы издателя(publisher) и подписчика(subscriber) на Python.
1.1 Написание Узла Издателя (Publisher Node)
В ROS, узел (node) — это термин для обозначения исполняемого файла, который подключён к сети ROS. Попробуем создать узел издателя («болтун» («talker»)), который будет постоянно транслировать сообщения.
Перейдите в каталог в пакета beginner_tutorials, созданный в начале этого курса:
roscd beginner_tutorials
1.1.1 Код
Создайте внутри пакета beginner_tutorials файл nodes/talker.py и вставьте в него следующий код:
Для создания и запуска редактирования файла воспользуйтесь командами:
$ mkdir nodes $ touch nodes/talker.py $ rosed beginner_tutorials talker.py
#!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials') import rospy from std_msgs.msg import String def talker(): pub = rospy.Publisher('chatter', String) rospy.init_node('talker') while not rospy.is_shutdown(): str = "hello world %s"%rospy.get_time() rospy.loginfo(str) pub.publish(String(str)) rospy.sleep(1.0) if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass
Не забудьте сделать файл узла исполняемым файлом:
$ chmod +x nodes/talker.py
1.1.2 Разъяснение кода
Теперь, давайте разберём, что же делает код, который мы используем.
#!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials')
Каждый узел ROS, в самом верху, будет иметь это объявление. Первая строчка делает ваш сценарий исполняемым, а вторая (как описывается в руководстве PythonPath) говорит roslib считать файл manifest.xml и добавить все зависимости, перечисленные в нём, в ваш PYTHONPATH. Это будет важно для следующей секции кода.
import rospy from std_msgs.msg import String
Ваш код уже может импортировать стандартные библиотеки Python, такие как sys и time, но roslib.load_manifest() позволит найти rospy и std_msgs, которые были объявлены в качестве зависимостей в manifest.xml. Вы должны импортировать rospy, если вы пишете ROS узел. Импорт std_msgs.msg объясняется тем, что теперь мы можем использовать тип сообщения std_msgs/String (простой контейнер для строк) для публикации.
def talker(): pub = rospy.Publisher('chatter', String) rospy.init_node('talker')
Эта часть кода, определяет интерфейс узла talker к остальный ROS.
pub = rospy.Publisher(‘chatter’, String) — заявляет, что ваш узел публикует сообщения на тему «chatter», используя для этого сообщения типа String. String здесь, на самом деле, класс std_msgs.msg.String.
Следующая строчка, rospy.init_node(NAME), очень важна, так как она говорит rospy имя вашего узла — пока rospy не имеет этой информации, он не может начать общаться с Мастером ROS (ROS Master). В этом случае, ваш узел возьмет на имя ‘talker’.
Примечание: имя должно быть базовым, т.е. оно не должно содержать косую черту «/».
while not rospy.is_shutdown(): str = "hello world %s"%rospy.get_time() rospy.loginfo(str) pub.publish(String(str)) rospy.sleep(1.0)
Этот цикл является довольно стандартной rospy конструкцией: проверка флага rospy.is_shutdown(), а затем выполнение основной работы. Вы должны проверить is_shutdown(), чтобы узнать, должна ли ваша программа завершить работу (например, по нажатию Ctrl-C или по другой причине). В данном случае, «основная работа» — это вызов метода pub.publish(String(str)), который публикует в нашей теме chatter вновь созданную строку сообщения. Затем, в цикле вызывается rospy.sleep(), которая похожа на time.sleep(), за исключением того, что она работает с имитацией времени (см. Clock).
Этот цикл, также вызывает rospy.loginfo(str), который выполняет тройные обязанности: сообщение выводится на экран, так же оно будет записано в файл журнала (log-файл) узла, и ещё, оно будет записано в rosout. rosout очень удобно для отладки: можно дотянуться до сообщения, используя rxconsole вместо того, чтобы искать окно консоли с выдачей узла.
std_msgs.msg.String является очень простым типом сообщения, так что вы можете быть удивлены, как это выглядит для публикации более сложных типов. Общее правило в том, чтобы конструировать аргументы в том же порядке, как в .msg-файлах. Вы также можете не передавать аргументы и инициализировать поля непосредственно, например:
msg = String() msg.data = str
или вы можете инициализировать некоторые из полей, а остальные оставить со значениями по-умолчанию:
String('data'=str)
Вы можете задаваться вопросом о последних строчках:
if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass
В дополнение к стандартной проверке Python __main__, этот код перехватывает исключение rospy.ROSInterruptException, которое может быть вызвано методами rospy.sleep() и rospy.Rate.sleep(), когда нажата Ctrl-C или ваш узел неожиданно завершил работу. Это исключение присутствует для того, чтобы вы случайно не продолжили выполнение кода после sleep().
Теперь, нам нужно написать узел для получения сообщений.
1.2 Написание подписчика узела (Subscriber Node)
1.2.1 Код
Создайте в пакете beginner_tutorials файл nodes/listener.py и вставьте в него следующий код:
#!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials') import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_name()+"I heard %s",data.data) def listener(): rospy.init_node('listener', anonymous=True) rospy.Subscriber("chatter", String, callback) rospy.spin() if __name__ == '__main__': listener()
Не забудьте сделать файл узла исполняемым файлом:
$ chmod +x nodes/listener.py
1.2.2 Разъяснение кода
Код для listener.py похож на talker.py, за исключением того, что мы добавили метод callback для подписки на сообщения.
rospy.Subscriber("chatter", String, callback)
Эта строчка заявляет, что ваш узел поддерживает подписывается на тему chatter, которая относится к типу std_msgs.msgs.String. Когда новое сообщение будет получено, будет вызван метод callback с сообщением в качестве первого аргумента.
Мы также несколько изменили код вызова rospy.init_node(). Мы добавили аргумент anonymous=True. ROS требует, чтобы каждый узел имел уникальное имя. Если появляется узел с таким же именем, то он выбивает предыдущий. Это связано с тем, что неисправность узлов может легко завалить всю сеть. Флаг anonymous=True говорит, чтобы rospy сгенерировал уникальное имя для этого узла, так что вы сможете легко запустить в работу сразу несколько узлов listener.py.
И последнее добавление, rospy.spin() просто удерживает ваш узел от выхода, пока узел не будет отключен. В отличие от roscpp, rospy.spin() не влияет на функцию callback подписчика, так как те имеют свои собственные потоки.
1.3 Сборка ваших узлов
Мы используем CMake в качестве нашей системы сборки, да, вы должны использовать его даже для узлов Python. Это делается для того, чтобы убедиться, что создается автоматически сгенерированный Python-код для сообщений и сервисов. Мы также используем Makefile для удобства.
roscreate-pkg автоматически создаёт Makefile, так что вам не придется его редактировать.
Теперь запустите:
$ make
Теперь, когда вы написали простых издателя и подписчика, давайте проверим их работу.
Ссылки
http://www.ros.org/wiki/ROS/Tutorials/WritingPublisherSubscriber(python)