17 April 2017

Логирование в программе ROS

Robotics
Добрый день, уважаемые читатели! Продолжаю рассказывать о различных интересных и полезных возможностях робототехнической платформы Robot Operation System (ROS). В этом небольшом руководстве я расскажу о возможностях логирования средствами ROS при программировании на языке C++. Думаю, многие согласятся с тем, что вывод сообщений при выполнении программы имеет большое значение при разработке программ, особенно при отладке. ROS предоставляет удобное API для вывода различных типов сообщений в терминал с возможностью визуализации и фильтрации сообщений в специальных инструментах ROS. Давайте начнем!

Использование методов библиотеки console.h для вывода сообщений в узле


Итак, напишем простой узел, который назовем logging_tutorial:

catkin_create_pkg logging_tutorial roscpp std_msgs

Добавим в CMakeLists.txt:

add_executable(logging_node src/main.cpp)

target_link_libraries(logging_node                                                                            
   ${catkin_LIBRARIES}                                                                                        
)

Теперь начнем писать код main.cpp. Импортируем в заголовке файла специальный заголовочный файл console.h с определением функций логирования:

#include <ros/console.h>

Добавим простое сообщение в метод main:

int main(int argc, char** argv)
{
	ros::init(argc, argv, "logging_node");

	ros::NodeHandle n;
        ROS_INFO("logging_node start");

        return 0;
}

Скомпилируем пакет и запустим его:

cd ~/catkin_ws
catkin_make
source devel/setup.bash
rosrun logging_tutorial logging_node

Вывод будет таким:

[ INFO] [1492194213.009783103]: logging_node start

В начале сообщения добавляется “уровень” или тип и временная метка в секундах и наносекундах с 1 января 1970. В данном случае у нас сообщение с уровнем INFO.

Мы можем передавать в сообщение параметры аналогично методу printf:

int val = 5;
ROS_INFO("message with argument: %d", val);

Можно создать сообщение в виде стандартного потока подобно std::cout:

ROS_INFO_STREAM("message with argument: " << val);

ROS поддерживает следующие «уровни подробности» (в порядке возрастания релевантности):

  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL

Для вывода сообщения определенного уровня используется соответствующая функция в формате: ROS_\<LEVEL\>. Сообщения различных типов выводятся с подсветкой определенным цветом: DEBUG — зеленым, INFO — белым, WARN — желтым, ERROR — красным, FATAL — фиолетовым. О предназначении каждого типа сообщений нетрудно догадаться по его названию. Так например, сообщения DEBUG полезны при отладке.

Добавим несколько различных сообщений и запустим все в цикле. У меня получился такой файл:

#include <ros/ros.h>
#include <ros/console.h>

int main(int argc, char** argv)
{
	ros::init(argc, argv, "logging_node");

	ros::NodeHandle n;

	ros::Rate rate (1);
    
    while(ros::ok())
    {

        ROS_DEBUG("debug message");

        ROS_INFO("logging_node start");

        int val = 5;
        ROS_INFO("message with argument: %d", val);

        ROS_INFO_STREAM("stream message with argument: " << val);

        ROS_WARN("My warning");

        ROS_ERROR("Some error");

        ROS_FATAL("Fatal error");

        ros::spinOnce();
        rate.sleep();

    }
	return 0;
}


По умолчанию при выполнении программы отображаются сообщения всех типов кроме DEBUG. Это определяется минимальным уровнем, так называемым “уровнем важности”, с помощью параметра ROSCONSOLE_MIN_SEVERITY. Параметр ROSCONSOLE_MIN_SEVERITY ограничивает вывод сообщений таким образом, что выводятся все сообщения с уровнем равным или выше минимального. По умолчанию минимальный уровень установлен равным DEBUG. Можно изменить минимальный уровень присвоив ROSCONSOLE_MIN_SEVERITY значение одного из уровней сообщений (ROSCONSOLE_SEVERITY_DEBUG, ROSCONSOLE_SEVERITY_INFO и т. д.) либо ни одному из них (ROSCONSOLE_SEVERITY_NONE), с помощью макроса в начале файла узла:

#define ROSCONSOLE_MIN_SEVERITY ROSCONSOLE_SEVERITY_DEBUG

Также можно задать уровень важности для всех узлов в пакете в файле CMakeLists.txt:

add_definitions(-DROSCONSOLE_MIN_SEVERITY=ROSCONSOLE_SEVERITY_ERROR)

У данного способа есть одно ограничение: выводиться будут только сообщения уровня выше DEBUG, даже если мы прямо укажем ROSCONSOLE_SEVERITY_DEBUG. Решить эту проблему можно с помощью специального файла конфигурации. Создадим папку config и внутри нее файл custom_rosconsole.conf с таким содержимым:

log4j.logger.ros.logging_tutorial=DEBUG

Затем создадим launch файл logging.launch и укажем путь до файла custom_rosconsole.conf в переменной ROSCONSOLE_CONFIG_FILE:

<launch>                                                                                                         
<env name="ROSCONSOLE_CONFIG_FILE"                                                                               
     value="$(find logging_tutorial)/config/custom_rosconsole.conf"/>                                                                           
<node pkg="logging_tutorial" type="logging_node" name="logging_node" output="screen"/>                           
</launch>

Здесь мы добавили параметр output=«screen», что позволяет выводить сообщения уровней начиная с INFO.

Скомпилируем и запустим наш лаунч:

cd ~/catkin_ws
catkin_make
roslaunch logging_tutorial logging.launch

Мы получим подобный вывод:

image

В ROS есть возможность назначать сообщениям имена. По умолчанию сообщения получают имя узла. Пользователи могут давать имена различным сообщениям посредством функции ROS_\<LEVEL\>[_STREAM]_NAMED:

ROS_INFO_STREAM_NAMED("named_msg", "named info stream message with argument: " << val);


Имена позволяют назначать каждому именованному сообщению разные минимальные уровни в файле конфигурации и изменять их индивидуально. Имя сообщений указывается после имени пакета:

log4j.logger.ros.logging_tutorial.named_msg = ERROR


Полезными также могут быть условные сообщения (conditional messages), выполняемые при определенном условии. Для этого используется функция ROS_\<LEVEL\>[_STREAM]_COND[_NAMED]:

ROS_INFO_STREAM_COND(val < 2, "conditional stream message shown when val (" << val << ") < 2");


Сообщения можно выводить с определенной частотой с помощью функции ROS_\<LEVEL\>[_STREAM]_THROTTLE[_NAMED]. Первым параметром задается частота в секундах:

ROS_INFO_THROTTLE(3, "throttled message with period 3 seconds");


Использование rqt_console для вывода сообщений


Запустим утилиту rqt_console:

rosrun rqt_console rqt_console

И запустим наш узел с помощью launch:

roslaunch logging_tutorial logging.launch

Окно rqt_console будет выглядеть так:

image

Здесь в таблице для каждого сообщения отображается его уровень, название узла, название файла и строка кода, где оно генерируется.

Двойным кликом на сообщении можно получить детальную информацию о нем:

image

Можно отфильтровать сообщения, исключив все сообщения о ошибках. Для этого добавим фильтр нажав на плюсик возле таблицы Exclude messages и выбрав опцию "...containing":

image

Или исключить сообщения с уровнем Info:

image

Также сообщения заданного уровня можно выделить среди всех использовав фильтры в секции Highlight Messages:

image

Таким образом, rqt_console позволяет гибко настраивать отображение сообщений из различных узлов и упростить отладку программ в ROS.

Надеюсь, данное руководство окажется полезным в работе и упростит решение возникающих проблем при программировании. Желаю всем успехов в работе с ROS и до новых встреч!
Tags:rosloggingотладка программпрограммирование
Hubs: Robotics
+6
3.3k 28
Comments 6
Top of the last 24 hours