4.1
Вспомним, что мы узнали на предыдущих уроках и узнаем, чем мы займёмся в
этом уроке
В первом уроке мы узнали, что такое ROS, для чего ROS может быть
полезен инженеру-робототехнику, немного узнали об ОС Ubuntu и её
отличиях от ОС Windows, получили начальные сведения об эмуляторе
терминала Linux, установили ROS 1 и пакеты для создания собственного
программного обеспечения. Во втором уроке мы узнали, что такое рабочее
пространство ROS, создали первый пакет, состоящий из двух узлов, один из
которых является издателем, а второй подписчиком, связали их посредством
механизма сообщений ROS. Также научились создавать файлы запуска,
которые помогают организовать сложные схемы запуска пакетов ROS. В
третьем уроке мы узнали про простейший симулятор мобильного робота
turtlesim, поняли его кинематику и подключились к нему с помощью
собственного узла, реализующего взаимодействие с симулятором и как
издатель и как подписчик посредством сообщений ROS. Также в наших уроках
мы постоянно имеем дело с различными командами Linux, ROS и языком
Python3.
Сегодня мы узнаем о сервере параметров ROS – базе общих для всех
узлов параметров ROS, а также познакомимся с Rosbag – средством
протоколирования данных сообщений ROS. Также мы научимся работать с
путям к файлам и обрабатывать переменные времени внутри скриптов.
4.2 Сервер параметров ROS
Сервер параметров
ROS – это централизованная база данных, содержащая параметры,
доступные всем узлам ROS, подключённым к одному ROS-мастеру. Мы можем
использовать сервер параметров для хранения данных, доступ к которым
может быть получен глобально из наших пакетов ROS. Сервер параметров
даёт возможность не только читать, но и самостоятельно создавать,
изменять и удалять параметры. Параметры – ни в коем случае не замена
сообщениям или другим средствам взаимодействия узлов ROS, если вы будете
использовать параметры для потоковой передачи данных, скорее всего
столкнётесь с серьёзным замедлением работы ROS. Параметры – это средство
конфигурации нашей операционной системы роботов, такое как переменные
среды Windows или Linux.
4.2.1 Команда rosparam
Теперь мы научимся управлять сервером параметров ROS. Как всегда, нам
потребуется открыть новый терминал. Теперь давайте посмотрим список
существующих параметров. За работу с сервером параметров ROS отвечает
команда rosparam. Как вы могли заметить раньше, если мы
хотим посмотреть список чего-либо в ROS, мы запускаем команду с ключом
list: rostopic list – список существующих
тем ROS, rosnode list – список существующих узлов ROS,
rosmsg list – список существующих сообщений ROS и т.д. Этот
принцип работает практически для всех команд ROS. Хорошо, давайте
подадим команду rosparam list. Что мы получили в
результате? В результате мы получили сообщение об ошибке ERROR:
Unable to communicate with master!, что вполне логично, так как
чуть ранее я сам же и говорил про доступность параметров для узлов,
подключенных к одному ROS-мастеру. Запустим ROS-мастер
в другом терминале и снова подадим команду
rosparam list:
rosparam_list.png
Мы получили список параметров для нашего ROS-мастера, которые теперь
могут быть нами исследованы. Давайте, например, посмотрим на значение
параметра /rosdistro x помощью ключа get. Тут
возникает закономерный вопрос, как узнать, какие ключи и для чего есть у
команды ROS? Можно ходить в ROS Wiki каждый раз, конечно, но могут быть
такие ситуации, когда это не просто неудобно, но и невозможно, например,
у нас нет доступа к интернету. В этом случае нам поможет справка по
команде, которую мы можем вызвать с помощью ключа help
или -h. Этот способ должен сработать для всех команд
ROS. Попробуем rosparam -h:
rosparam_help.png
Итак, давайте теперь наконец посмотрим на значение параметра
rosdistrorosparam get /rosdistro. Кстати, не
забудьте поставить слэш перед именем параметра, также не забывайте
ставить его перед именами топиков как при выполнении команд из командной
строки, так и при написании собственных программ. Могут быть ситуации,
когда вы забудете его поставить и всё пройдёт замечательно, а может быть
такое, что вы потратите кучу времени на отладку того, что написали, или
хуже того, ваши заказчики получат код, который то работает, то нет. На
самом деле, это означает, что мы не понимаем разрешение (resolving) имен в ROS.
К концу этого занятия попробуем этот пробел заполнить. Хорошо, давайте
всё-таки уже подадим команду и посмотрим, что получилось:
get_rosdistro.png
Логично, мы пользуемся дистрибутивом Noetic, о чём мы теперь узнали и
из параметра rosdistro.
Теперь было бы здорово самим задать какой-нибудь параметр. Давайте
посмотрим в справке, какой ключ нам для этого подходит, и это ключ
set. Давайте создадим параметр my_param со
значением, равным 1. Для этого мы должны подать команду:
rosparam set my_param 1, где my_param –
имя параметра, а 1 – его значение. Если мы подадим
такую команду для параметра, который ещё не существует, параметр будет
создан и ему будет присвоено значение, а если мы подадим такую команду
для существующего параметра, то значение этого параметра будет изменено
на вновь заданное в команде. Сейчас мы создали параметр, в чём мы можем
убедиться, вновь подав команду rosparam с ключом
list, и можем посмотреть значение этого параметра с
помощью ключа get. Попробуйте. Отлично, мы видим наш
параметр в списке параметров и мы можем получить его значение. Давайте,
кстати, убедимся, что этот параметр был доступен только в рамках той
сессии ROS-мастера, в которой мы его задали. Попробуйте сделать это
сами. Что нам надо для этого сделать? В принципе, ничего сложного.
Остановить ROS-мастер, потом запустить ROS-мастер ещё раз и посмотреть
список его параметров. Параметра my_param там нет.
4.2.2 Сервер параметров и
файлы запуска ROS
Теперь опять представим, что мы передаём наш проект некоторому
пользователю, который хочет его запустить на своей машине. Передать ему
список параметров и их значений, которые он каждый раз должен будет
задавать при запуске ROS-мастера программой? > Я давно хотел вам
сказать, что я ваш большой фанат
Но если мы хотим не получить признание сил зла, а создавать хорошие
программные продукты, давайте облегчать жизнь потребителю. Давайте
создадим файл с параметрами, необходимыми для нашего проекта ROS.
Давайте снова вернёмся к нашему пакету publ_n_subs, который мы
создали на уроке 2, и дополним его каталоги каталогом с именем
config. В каталоге пакета у нас есть подкаталог
scripts, в котором мы храним файлы с текстами наших узлов,
подкаталог launch, где мы храним пусковые файлы пакета, а
теперь будет ещё подкаталог config, где у нас будут храниться
файлы конфигураций. Сделайте это. ___ Я ввёл следующие команды:
и убедившись, что подкаталог у меня появился, перешёл в него и создал
файл с именем myparams.yaml. Сделайте это и вы, а потом
откройте файл в редакторе gedit. ___ Вот как-то
так:
cd ~/catkin_ws/src/publ_n_subs/configtouch myparams.yamlgedit myparams.yaml
У нас открылся yaml-файл. Что такое YAML? YAML “это не язык разметки” (YAML
Ain’t Markup Language), как говорит нам расшифровка так называемого
рекурсивного акронима YAML. Итак, что же это за “не язык разметки”?
Будем говорить, что YAML – это удобный формат записи структур данных,
легко поддающийся расшифровке. Например, сейчас мы будем использовать
так называемые словари, то есть будем сопоставлять имени
значение. В качестве имени будет имя нашего параметра, а в качестве
значения – его значение, как бы банально это не звучало. Давайте зададим
два параметра:
my_param:2my_param_2:3
Для того, чтобы выполнить сопоставление значения параметру, мы
используем формат <Имя параметра>:<Значение параметра>, то есть
разделяем, а вернее, объединяем имя и значение с помощью символа
двоеточия. Давайте сохраним и закроем файл. Теперь было бы неплохо
автоматизировать задание параметров для проекта. Как мы автоматизируем
запуск нашего проекта? Совершенно верно, с помощью
roslaunch. То есть, нам необходимо теперь создать в
подкаталоге launch файл запуска, который сможет подхватить наш
файл с параметрами. Давайте перейдём в подкаталог с файлами запуска и
создадим новый файл с именем myparams.launch. Мы его назовём
так, потому что он не будет делать ничего, кроме загрузки наших
параметров. У него будет то же имя, но расширение файла запуска, так что
мы не сможем их перепутать. Хорошо, создадим такой файл и откроем его
для редактирования. ___ Вот так:
cd ../launch/touch myparams.launchgedit myparams.launch
В прошлый раз мы хотели запустить наши ноды, для чего пользовались
тегом node. Сейчас мы написали в файле запуска команду работы с
сервером параметров, использовав тег rosparam, для которого мы
хотим загрузить command=“load” файл
file=“~/catkin_ws/src/publ_n_subs/config/myparams.yaml” Если вы
забыли, как правильно использовать теги в launch-файле, ничего страшного
— например, для <rosparam> можно заглянуть на страницу
документации в ROS Wiki.
Хорошо, файл запуска мы создали, давайте попробуем его запустить
командой roslaunch publ_n_subs myparams.launch:
launchparams_error.png
Неудача. Нам говорят, что файл, который мы хотели загрузить, не
существует, а мы уверены, что существует. Если есть желание, то можете
проверить:
cat ~/catkin_ws/src/publ_n_subs/config/myparams.yaml.
cat_myparams.png
Убедились, существует и даже содержит то, что мы в него записали. В
чем же дело? А дело в том, что использовать символическую запись с
~ в файле запуска нельзя. Только полный путь к
файлу. Не спешите писать полный путь, уверяю вас, это сработает, но
также уверяю вас, что вы не будете этому рады, как только возникнет
необходимость передать ваш пакет кому-то ещё. Что же нам помешает? А
помешает нам то, что на каждом компьютере может быть своё имя
пользователя, соответственно, и домашний каталог этого пользователя нам
не будет известен заранее. Ну что же, это была хорошая попытка выучить
ROS, но она разбилась о непреодолимую преграду. Собираем ручки,
тетрадки, компьютеры и самоуважение и идём домой. Стоп, а действительно
ли преграда настолько непреодолима? Конечно, нет, конечно, о нас опять
позаботились, дав инструмент, который мы можем использовать в файлах
запуска. Вернёмся на страничку в ROS Wiki и посмотрим на то, как они
предлагают записывать путь к файлу через вот такой трюк:
file=“$(find pkg-name)/path/foo.yaml”. Здесь используется
возможность найти каталог пакета по имени пакета, а уж имя пакета мы
всегда знаем. Перепишем наш launch-файл следующим образом:
сохраним и запустим командой
roslaunch publ_n_subs myparams.launch:
launchparams_succs.png
Вот теперь всё отлично. Осталось только посмотреть, есть ли параметры
из файла в списке параметров, и узнать значения этих параметров, что вы
уже умеете. ___ У меня получилось вот так:
rosparam listrosparam get /my_paramrosparam get /my_param_2
rosparam_list_get.png
Теперь давайте подновим наш конфигурационный файл, launch-файл и
скрипт паблишера, чтобы загружать параметр из файла и передавать его
через топик.
Добавим
my_param:2my_param_2:3# Добавляем частный параметр для publisher_nodepublisher_node:value_to_publish:42 # Значение, которое будем отправлять в топик
Добавим строчку загрузки файла myparams.yaml в
launch-файл:
#!/usr/bin/env python3import rospyfrom std_msgs.msg import Int64# Инициализация узла и публикатораrospy.init_node('publisher_node')pub = rospy.Publisher('topic', Int64, queue_size =1)# Загружаем глобальный параметр из YAML (со значением по умолчанию 1, если параметр не найден)value = rospy.get_param('/my_param_2', 1)print ('Глобальный параметр /my_param_2 =', value)# Загружаем частный параметр из YAML (со значением по умолчанию 1, если параметр не найден)value = rospy.get_param('~value_to_publish', 1)print ('Частный параметр ~value_to_publish =', value)# Циклическая публикация сообщенияwhilenot rospy.is_shutdown(): pub.publish(value) rospy.sleep(1)
Здесь мы добавили чтение параметра из базы rosparamvalue = rospy.get_param('~value_to_publish', 1) и печать
этого значения value.
Мы загрузили параметры из YAML-файла, чтобы централизованно управлять
настройками системы. Также мы разделили параметры на глобальные и
частные, предотвращая конфликты имён и обеспечивая гибкость настройки
узлов.
4.3 Средство Rosbag
Средство Rosbag позволяет данные, публикуемые в топики ROS, сохранять
в специальных bag-файлах, которые потом можно “воспроизвести” тем же
самым Rosbag. Rosbag очень полезен при отладке пакетов ROS, написании
программ, использующих данные аппаратного обеспечения, которое не
установлено на компьютере, где ведётся разработка, журналировании и
последующем просмотре данных. Применений у Rosbag достаточно много.
Давайте опробуем это средство на практике. Для начала запустим
ROS-мастер, а в другом окне терминала с помощью команды
rostopic опубликуем pub в топик
/mytopic стандартное сообщение из набора
std_msgs формата Int16 со значением
1. Да, rostopic позволяет нам делать такие
вещи, которые очень полезны при отладке наших пакетов. Итак, подадим
команду rostopic pub /mytopic std_msgs/Int16 1. Теперь
проверим, откроем ещё один терминал и посмотрим список тем, а затем
посмотрим содержимое сообщения. ___
rostopic listrostopic echo /mytopic
Получилось вот что:
mytopic.png
Топик есть, данные в нём тоже есть. Нет понимания, зачем мы это
делали и причем здесь rosbag. Будем разбираться и для
начала создадим в каталоге нашего любимого уже пакета
publ_n_subs подкаталог bag, куда будем складывать наши
bag-файлы, командой mkdir ~/catkin_ws/src/publ_n_subs/bag.
Скажу, что давать подкаталогу именно это имя не обязательно, да и вообще
не обязательно заводить для bag-файлов отдельную папку, просто
организовывая свой пакет таким образом мы понимаем, где что находится и
не захламляем каталог пакета. Теперь нам надо начать всё с начала.
Давайте остановим ROS-мастер, также остановим ожидающие нашей реакции
rostopic pub и rostopic echo и закроем все
окна терминала, а затем откроем окно терминала и запустим ROS-мастер и
вот здесь начнём работать с командой rosbag.
4.3.1 Команда rosbag
Как вы помните, мы создали специальный подкаталог, в котором
планировали хранить bag-файлы – перейдем в него и начнём запись топика
mytopic
cd ~/catkin_ws/src/publ_n_subs/bagrosbag record /mytopic
Нам сообщили, что rosbag подписался на тему
/mytopic и ведёт запись в файл с именем, состоящим из даты
времени начала записи, и расширением bag.
rosbag_rec_1.png
Такой темы ещё не существует, но это не мешает нам подписаться на
неё. Мы просто ожидаем, что она появится позже.
Теперь давайте откроем ещё окно терминала и снова опубликуемся в
топике /mytopicrostopic pub /mytopic std_msgs/Int16 1, а затем остановим и
rostopic и rosbag и посмотрим,
появился ли у нас bag-файл.
ls_bag.png
Файл есть, время в его имени нам сейчас не нужно, писать такое в
командной строке тяжело, поэтому давайте переименуем файл, а заодно
познакомимся с командой mv, с помощью которой это
делается. Формат команды mv <старый файл> <новый файл>.
На самом деле, эта команда не просто переименовывает файл, а переносит
его из каталога в каталог, если имена файлов прописать с путями, но
сейчас у нас нет такой необходимости, поэтому вот так:
mv.png
Почему я не привёл команду, как обычно это делаю, надеюсь, понятно –
ваш файл, скорее всего, имеет другое имя. Хорошо, давайте вернёмся к
применению rosbag и сделаем странное, подадим команду
rostopic echo /mytopic. > Пушка! Они заряжают пушку.
Зачем?
Сейчас мы подадим команду
rosbag play test.bag
и всё станет ясно. Подайте команду, только убедитесь, что вы сейчас в
каталоге с bag-файлами, и посмотрите на вывод в терминале с
rostopic echo.
echo_rosbag_play.png
Мы видим, что кто-то опубликовал в топик /mytopic данные, а
был этот кто-то rosbag, которые прочитал bag-файл,
нашёл там темы и опубликовал их. Тема была только одна, но если бы их
было несколько, несколько и опубликовал бы. Далее мы в этом
убедимся.
4.3.2 Использование rosbag в
пакетах ROS
Давайте немного исправим скрипт паблишера для нашего пакета
publ_n_subs, а именно, дополним его ещё одним публикуемым
топиком. Топиков мы можем публиковать столько, сколько нужно, поэтому
давайте будем публиковаться ещё в топик /mytopic. ___ У меня
получилось так:
Если мы запустим эту ноду и посмотрим на список тем, то увидим там
две новых темы, а печать данных из этих тем покажет нам те числа,
которые мы туда отправили. Теперь давайте создадим новый скрипт
read_bag.py, в котором и будем работать с Rosbag и откроем его
в gedit. ___
Так как я находился в подкаталоге bag каталога нашего пакета, я
воспользовался таким способом записи, в котором две точки подряд
означают родительский каталог. Возможно, мы уже делали так
cd .., чтобы перейти на каталог выше, а если не делали, то
это вполне рабочий способ. Но на всякий случай давайте я перепишу эти
команды в более привычном нам виде:
Начало нашего скрипта пишется нами уже “на автомате”, шебанг и импорт
ROS для Python, а вот следующая строчка встречается первый раз. Несмотря
на то, что мы её раньше не видели, мы прекрасно понимаем, что она
делает. Что же? __ В этой строке мы импортируем библиотеку
rosbag, которая позволяет работать с bag-файлами. В
следующей строке мы видим знакомую инициализацию ноды, которой мы даём
имя read_bag, а вот дальше опять что-то новое, но достаточно
понятное: мы инициализируем объект Rosbag для нашего пакета, а в
качестве аргумента передаем туда имя bag-файла, который создали ранее.
Далее, мы опять используем цикл for, но теперь
используем его по-другому – мы не будем задавать какой-то диапазон, за
нас его уже задали, перечислив в bag-файле сообщения для разных тем.
Прелесть цикла for в питоне в том, что она может сама
задавать и диапазон для какого-то перечислимого множества и количество
шагов, которое понадобится, чтобы пройти по этому множеству и ещё сможет
нам выдать данные из каждого элемента этого множества. Какое же
множество мы будем обрабатывать? Будем обрабатывать множество записей
bag-файла, каждая запись которого состоит из так называемого кортежа
– это неизменяемая структура данных, состоящая в данном случае из трёх
элементов: имени темы, сообщения в формате ROS и временной метки. Для
чтения множества сообщений у объекта Rosbag есть функция
read_messages, которой мы можем указать, какие темы мы
хотим читать. Давайте для начала запишем цикл для множества сообщений из
топика /topic_, а для каждого сообщения мы напечатаем само
сообщение и его временную метку. Пока трудно понять, давайте я напишу, и
мы разберёмся.
for topic, msg, tim in bag.read_messages (topics ="/topic"):print (msg)print (tim)
После ключевого слова for указаны три переменные:
topic, msg, tim. На каждой
итерации цикла в них будут записываться данные из кортежа: имя топика,
сообщение и временная метка, которые содержаться в (ключевое слово в
программе in) множестве прочитанных из bag-файла
сообщений для топика /topic. Так стало понятнее. Объединим
части текста скрипта в один и сохраним.
#!/usr/bin/env python3import rospyimport rosbagrospy.init_node("read_bag")bag = rosbag.Bag("~/catkin_ws/src/publ_n_subs/bag/test.bag")for topic, msg, tim in bag.read_messages (topics ="/topic"):print (msg)print (tim)
А теперь попробуем запустить
rosrun publ_n_subs read_bag.py и получить результат:
py_bag_file_error.png
Мы получили явно не то, что хотели. А вот почему мы получили такой
результат, ошибку времени выполнения, интерпретатор питона нам подробно
сообщает. В данном случае важна последняя строчка, которую я подсветил
жёлтым, говорящая нам о том, что требуемого нами файла нет. Файл есть, я
проверил, если хотите, можете проверить сами, скопировав его имя и
распечатав файл с помощью cat, но питон его не видит. В
чем дело? Всё дело опять в символическом обозначении домашнего каталога,
которое нельзя употреблять при задании имени файла. Способ получить имя
домашнего каталога в Python есть, конечно, но возиться мне с ним сейчас
совсем не хочется, сделаем это в конце занятия. А сейчас давайте просто
перейдем в каталог, который мы создали для bag-файлов и запустим ноду
оттуда.
empty_bag.png
Результат, скажем прямо, неожиданный. > Где деньги, Лебовски?
Давайте посмотрим, всё ли в порядке с bag-файлом. Что может нам
предложить rosbag, если мы попросим его о помощи
-h? Ага, есть ключ info, посмотрим на
файл rosbag info test.bag:
where_is_topic.png
Вот и стало всё понятно, мы же записывали
rosbag record /mytopic, вот темы /topic в нем и нет.
Придётся переписать файл, а чтобы записать все темы, надо использовать
ключ –all. И нет, я не ходил за этим знанием в
интернет, я посмотрел помощь по подкоманде, назовём её так,
record нашей команды rosbag. То есть,
так rosbag record -h тоже можно. Попробуем записать файл
заново, для этого можно использовать ключ
–output-name=<имя файла>. В терминале перейдём в
каталог с bag-файлами, на всякий случай сотрём наш test.bag и
подадим команду на запись всех тем в него же:
cd ~/catkin_ws/src/publ_n_subs/bagrm test.bagrosbag record --all--output-name=test.bag
Хорошо, теперь в другом окне терминала запустим паблишер из нашего
пакета rosrun publ_n_subs publisher.py, вернёмся в окно с
rosbag, убедимся, что он подписан на темы нашего
паблишера и остановим паблишер и rosbag. Попробуем
почитать bag-файл rosrun publ_n_subs read_bag.py:
pybag_read.png
Здесь мы видим данные со значением 1 и огромное число – время
от “рождества Unix” (если быть более корректным – время от начала
эпохи Unix 01.01.1970) в наносекундах.
Давайте слегка облагородим наш вывод, выводя только значение данных, а
не всё сообщение, а также, показывая время в секундах. Заменим тело
цикла for на строку:
print ("Our topic data = ", msg.data, " it was at ", tim.to_sec(), "sec of unix time")
format_pybag_out.png
Здесь мы уже выводим не всё сообщение целиком, а выводим только его
поле данных, а для времени мы используем функцию перевода его в секунды.
Так лучше, как мне кажется. Формат вывода времени можно выбрать и в
более привычном для нас виде, но для этого надо подключить
дополнительный модуль. Давайте сделаем это в конце урока, если не
забудем. А теперь мне кажется, что мы совсем забыли про вторую тему
/mytopic. Давайте для начала научимся читать несколько тем, для
чего воспользуемся такой структурой данных питона, как список. Список в
Python – структура данных, в которой можно хранить объекты разных типов
и имеет не ограниченный заранее размер. Первое свойство для нас сейчас
не очень важно, так как все элементы у нас строковые, а вот второе нам
будет полезно – мы перечислим в списке темы, которые хотим читать из
bag-файла. Запишем мы этот список, перечислив темы в кавычках и через
запятую, а список покажем, ограничив его слева и справа открывающей и
закрывающей квадратной скобкой. Будет что-то типа такой записи
["/topic1", "/mytopic1", "/mydata"], только надо вписать
наши имена тем. Перепишем строку:
for topic, msg, tim in bag.read_messages (topics ="/topic"):
таким образом:
for topic, msg, tim in bag.read_messages (topics = ["/topic", "/mytopic"]):
two_topics_out.png
Здесь мы видим, что некоторые данные у нас имеют значение 1, а
некоторые 3, следовательно, мы печатаем данные из разных топиков.
Давайте сделаем интерфейс более дружелюбным, очевидно, что разбирать, в
какой теме опубликованы данные, по самим данным не удобно, да и в
большинстве случаев невозможно. Давайте будем печатать для каждой темы
её наименование и вывод на экран для каждой темы оформим по-своему. Для
того, чтобы определить, из какой темы данные, воспользуемся переменной
topic и условным оператором if. Перепишем
тело нашего цикла так:
if topic =="/topic":print ("Someone's topic says ", msg.data, " at ", tim.to_sec(), "sec of unix time")if topic =="/mytopic":print ("Our topic says ", msg.data, " at ", tim.to_sec(), "sec of unix time")
Запустим и посмотрим на результат:
topics_say.png
Текст узла, который мы создали на данный момент:
#!/usr/bin/env python3import rospyimport rosbagrospy.init_node("read_bag")bag = rosbag.Bag("../bag/test.bag")for topic, msg, tim in bag.read_messages (topics = ["/topic", "/mytopic"]):if topic =="/topic":print ("Someone's topic says ", msg.data, " at ", tim.to_sec(), "sec of unix time")if topic =="/mytopic":print ("Our topic says ", msg.data, " at ", tim.to_sec(), "sec of unix time")
4.3.3 Обещанные доработки
Теперь давайте сделаем то, что собирались, но не сделали ранее: 1.
Автоматически определим каталог, в котором находится скрипт с текстом
нашего узла и получим полный и правильный путь для файла
test.bag. 2. Выведем время в привычном нам формате.
Работа с путям к каталогам
Для того, чтобы узнать, где находится наш bag-файл и не писать для
каждого компьютера свой путь, воспользуемся переменной
__file__, которая неявно существует для каждого скрипта и
модулем Python, который называется os. Дополним наш
скрипт перед строкой инициализации Rosbag таким текстом:
Что мы здесь делаем? Со строчкой import os всё ясно,
интересное начинается дальше. Для начала с помощью функции
абсолютного пути мы получаем абсолютный путь к файлу скрипта,
который мы берём из переменной __file__. В этой переменной
хранится путь к скрипту, но путь этот может быть долог и тернист, то
есть, начаться не с корня файловой системы, а от места запуска,
например. Например, мы можем получить вот такой путь (далее будет
использоваться некоторый фиктивный путь файлу, его не надо применять на
реальном компьютере): “/../../scripts/read_bag.py”. При этом
абсолютный путь, который мы сможем безбоязненно использовать в
программе, будет
“/home/user/catkin_ws/src/publ_n_subs/scripts/read_bag.py” (не
забываем, что это некий фиктивный путь, который используется только для
объяснения). Полученный путь к файлу мы помещаем в переменную
our_script_full_name. В следующей строке из
our_script_full_name мы получаем значение переменной
our_script_full_path, забирая из абсолютного пути к файлу
только путь к его каталогу. Делается это с помощью функции
выделения каталога из пути. На выходе для нашего фиктивного пути
этот каталог будет
“/home/user/catkin_ws/src/publ_n_subs/scripts”. В третьей
строке конкатенацией строк с помощью оператора + мы
получаем из пути к каталогу скриптов
“/home/user/catkin_ws/src/publ_n_subs/scripts” и пути из
каталога скриптов к каталогу bag-файлов, который мы задали в
относительном виде “/../bag/test.bag”, абсолютный (почти) путь
к каталогу bag-файлов. Путь получится таким
“/home/user/catkin_ws/src/publ_n_subs/scripts/../bag/test.bag”.
Мы здесь использовали возможность задать путь на каталог выше с помощью
двух точек подряд. Если мы попробуем пройти по нашему пути
последовательно, то получится так: root → home → user → catkin_ws →
src → publ_n_subs → scripts → publ_n_subs → bag → test.bag Хотя
обычно параметры узлов задаются иначе, этот пример демонстрирует, как с
помощью модулей Python можно решать различные задачи. Надо только
поискать нужный модуль.
Форматирование времени
Теперь давайте отформатируем время в понятный пользователю вид. Для
начала там, где мы записываем импортируемые модули, напишем
import time, а затем с помощью функции
получения локального времени из времени Unix в секундах получим из
наших отметок времени структуру
с информацией о времени, которую, в свою очередь, с помощью функции
форматирования времени преобразуем в строку. Перепишем тело
цикла:
Мне всё нравится. Сегодняшний урок можно на этом закончить.
4.4 Заключение (старое)
В этом уроке мы познакомились с сервером параметров ROS, поработали с
ним с помощью команды rosparam, научились создавать
файлы конфигурации для сервера параметров и загружать их с помощью
пусковых файлов ROS. Попутно, при создании файла конфигурации, мы
познакомились с форматом файлов YAML. Также мы узнали, что такое
средство протоколирования Rosbag, поработали с ним, используя команду
rosbag, а потом получили доступ к bag-файлу и из нашего
узла ROS. Также мы познакомились с парой модулей Python, один из которых
даёт возможность из скрипта работать с путями файловой системы, а второй
– с переменными времени.
4.4 Заключение (новое)
Мы успешно завершили ещё один важный этап в изучении ROS,
познакомившись с двумя ключевыми инструментами: сервером
параметров и инструментом протоколирования
rosbag.
С помощью команды rosparam вы научились управлять
централизованной базой данных параметров, доступной всем узлам,
подключённым к одному ROS-мастеру. Это позволяет хранить
конфигурационные данные проекта в одном месте и использовать их при
запуске пакетов. Мы рассмотрели, как сохранять параметры в формате YAML
и загружать их через launch-файлы — это делает проект более
структурированным, удобным для настройки и переносимым между
системами.
Также мы подробно разобрались с rosbag — мощным
средством для записи и воспроизведения сообщений ROS. Вы научились
записывать данные с топиков, сохранять их в файлы и читать обратно в
Python-скриптах. Это особенно важно при отладке сложных систем,
тестировании кода без подключения реальных датчиков или при анализе
поведения робота в прошлом.
В процессе работы с rosbag вы познакомились с обработкой
кортежей, использованием модулей os и time, а
также научились правильно определять пути к файлам относительно
расположения скрипта. Эти навыки помогут вам создавать более надёжные и
переносимые программы вне зависимости от среды исполнения.
Кроме технических деталей, вы получили опыт работы с несколькими
ключевыми концепциями: - Работа с сервером параметров
ROS - Формат YAML и его использование в ROS -
Запись и воспроизведение топиков с помощью
rosbag - Чтение bag-файлов в
Python - Использование временных меток и их
форматирование - Определение абсолютных путей в
Python
Эти навыки являются фундаментальными при разработке робототехнических
систем на основе ROS. Они позволяют не только автоматизировать процессы,
но и обеспечивать повторяемость экспериментов, точную настройку систем и
эффективную отладку программного обеспечения.
Продолжайте практиковаться, экспериментировать с параметрами,
пробовать новые комбинации топиков и совершенствовать работу с данными.
В следующих уроках эти знания станут основой для более сложных задач,
таких как логирование, управление состоянием системы и взаимодействие с
внешними устройствами.