本章将学习如何生成和查看日志消息。
接上回,这两天断更学习笔记,本人电脑以及很多资料放在实验室了,因为疫情突然封校,全体隔离,后借了电脑还原资料续更,读书是 不能停止的。突破寒冬的唯一捷径就是不断学习与其 抱怨身处黑暗,不如提灯前行。
介绍
在之前的章节中,我们经常看到“ROS_INFO_STREAM”的字样,ROS日志的核心思想是使程序生成一些简短的文本字符流,分为五个不同的严重级别/严重性/级别等。这些级别有:
- DEBUG
频繁出现,只要程序正常工作便不必在意。 - INFO
较轻 - WARN
中等 - ERROR
较严重 - FATAL
很少出现,很重要,表示程序中存在的一些问题导致无法继续运行。
划分等级的意图是提供一种区分和管理日志消息的全局
方法。生成一个fatal程序并不会终止你的程序,生成一个debug并不会调试你的程序。
示例程序
一个简单的例子如下:
#include <ros/ros.h>
int main (int argc,char **argv)
{
ros::init(argc, argv, "count_and_log");
ros::NodeHandle nh;
ros::Rate rate (10);
for(inti=1; ros::ok(); i++)
{
ROS_DEBUG_STREAM("Counted?to?"<<i);
if((i%3)==0)
{
ROS_INFO_STREAM(i<<"?is?divisible?by?3.");
}
if((i%5)==0)
{
ROS_WARN_STREAM(i<<"?is?divisible?by?5.");
}
if((i%10)==0)
{
ROS_ERROR_STREAM(i<<"?is?divisible?by?10.");
}
if((i%20)==0)
{
ROS_FATAL_STREAM(i<<"?is?divisible?by?20.");
}
rate.sleep();
}
}
将会生成如下所示的代码:
[INFO] [1375889196.165921375]: 3 is divisible by 3.
[WARN] [1375889196.365852904]: 5 is divisible by 5.
[INFO] [1375889196.465844839]: 6 is divisible by 3.
[INFO] [1375889196.765849224]: 9 is divisible by 3.
[WARN] [1375889196.865985094]: 10 is divisible by 5.
[ERROR] [1375889196.866608041]: 10 is divisible by 10.
[INFO] [1375889197.065870949]: 12 is divisible by 3.
[INFO] [1375889197.365847834]: 15 is divisible by 3
- 可以明显地看到:这个输出不包含任何debug
级别的消息,因为最小的级别是info。
生成日志消息
一般生成日志消息:
ROS_DEBUG_STREAM(message);
ROS_INFO_STREAM(message);
ROS_WARN_STREAM(message);
ROS_ERROR_STREAM(message);
ROS_FATAL_STREAM(message);
- message是C++中的输出流,比如std::cout等,可以使用插入运算符(<<)。
生成print风格的代码的日志消息:
ROS_INFO(“Position=(%0.2f, %0.2f) direction=%0.2f”, msg.x, msg.y, msg.theta);
- 由于是以行为单位的,因此没必要使用endl这种终止符。
生成一次性的日志消息:
ROS_DEBUG_STREAM_ONCE(message);
ROS_INFO_STREAM_ONCE (message);
ROS_WARN_STREAM_ONCE (message);
ROS_ERROR_STREAM_ONCE (message);
ROS_FATAL_STREAM_ONCE (message);
- 使用静态变量来使消息只生成一次(无视循环)。
生成频率受控的日志消息:
ROS_DEBUG_STREAM_THROTTLE(interval, message);
ROS_INFO_STREAM_THROTTLE(interval, message);
ROS_WARN_STREAM_THROTTLE(interval, message);
ROS_ERROR_STREAM_THROTTLE(interval, messge);
ROS_FATAL_STREAM_THROTTLE(interval, message);
- 参数interval是double类型的,以秒为单位,这是相邻日志消息出现的最小时间间隔。
- 如果用这种方式实现之前代码(第一个代码)的功能的话,它们的差距如下:
- 生成频率可控的日志消息,采用的是轮询机制而不是休眠机制。而这种轮询机制在实际程序中通常是一个糟糕的思路。
查看日志消息
实际上日志消息可以有三个不同的目的地:
- 控制台输出
- rosout话题的消息
- 也可以写入日志消息中
控制台
默认,DEBUG和INFO消息被打印至标准输出(standard output),而WARN、ERROR和FATAL消息将被送至标准错误(standard output)。
而实际上它们的区别是无关紧要的。可以通过文件重定向技术进行重定向。以下是三种重定向技术:
command > file
command &> file
stdbuf -oL command &> file
- 格式化控制台消息
[${severity}] [${time}]: ${message}
roslaunch工具默认不会将日志消息导入输出流,为了查看,必须显式地使用output="screen"属性。
rosout
每一个日志消息都被发布到/rosout上,消息类型是rosgraph_msgs/Log。下图是消息的各个域:
1 byte DEBUG=1
2 byte INFO=2
3 byte WARN=4
4 byte ERROR=8
5 byte FATAL=16
6 std_msgs/Header header
7 uint32 seq
8 time stamp
9 string frame_id
10 byte level
11 string name
12 string msg
13 string file
14 string function
15 uint32 line
16 string [] topics
可以通过以下消息查看:
rostopic echo /rosout
也可以通过图形化界面查看(每条消息独占一行):
rqt_console
实际上rqt_console的描述并不完全正确,实际上它订阅的是/rosout_agg,而不是/rosout。后缀_agg表示实际上是被rosout节点输出到/rosout_agg话题上。
日志文件
日志消息还有一个去处就是rosout节点生成的日志文件。文件名类似:
~/.ros/log/run_id/rosout.log
是纯文本文件。
运行标识码(run_id)是一个通用的唯一识别码(UUID),基于计算机的MAC地址和当前时间生成,下边是一个run_id的例子:
57aa1860-d765-11e2-a830-f0def1e189cc
查找运行标识码有如下两种方法:
- 通过检查 roscore 生成的输出,在靠近输出末端的位置,可以看到与下面内容类似的一行
setting /run_id to run_i
- 通过以下命令向节点管理器询问当前的
run_id rosparam get /run_id
由于 run_id 存放在参数服务器上,因此该命令是有效的。
检查日志文件:
rosclean check
- 日志文件将随着时间累积,ROS并不会采取任何的措施来减小日志文件大小。
删除所有的日志文件:
rosclean purge
启用或停用日志消息
日志级别是提供每个节点日志细节程度的能力。设置日志级别类似于rqt_console中的图纸级别过滤,不同的是,改变日志级别将会组织在源头生成。
通过命令行
伪代码:rosservice call /node-name/set_logger_level ros.package-name leve
调用 set_logger_level 服务,该服务由各个节点自动提供。
例:
rosservice call /count_and_log/set_logger_level ros.agitr DEBUG
- node-name是你期望设置日志级别的节点名称
- pkg-name是拥有这个节点的功能包的名称
- level参数是DEBUG、INFO、WARN、ERROR、FATAL中的一个字符串。
通过图形界面
rqt_logger_level
通过C++代码设置日志级别
节点改变自身的日志级别也是可能的。最直接的方式是调用 ROS 来实现日志功能的 log4cxx 提供的接口,代码如下
#include <log4cxx/logger.h>
. . .
log4cxx::Logger::getLogger(ROSCONSOLE_DEFAULT_NAME)->setLevel(
ros::console::g_level_lookup[ros::console::levels::Debug]
);
ros::console::notifyLoggerLevelsChanged();
- 除了必要的语法理解,这些代码应该很容易识别是将日志级别设置为 DEBUG。Debug 这个标识当然可以替换为 Info、Warn、Error 或者 Fatal
总结
消息对于跟踪和调试复杂ROS 系统的行为是很有用的,特是这些系统拥有大量不同节点时。