0. 前言
- 本文主要内容:
- ROS 中自带了日志系统,本文简单学习一下基本使用。
- ROS 中启动节点的主要方法就是 roslaunch,需要仔细学习一下用法。
- 参考资料:
1. 日志系统
-
引入:
- C++和Python都支持日志系统
- 构建项目时不需要单独引入其他包
- 对应的头文件是
#include <ros/console.h>
,但直接使用#include <ros/ros.h>
也可以。
-
配置文件(config格式)
- 默认配置文件路径
$ROS_ROOT/config/rosconsole.config
- 自定义配置文件路径可通过环境变量
ROSCONSOLE_CONFIG_FILE
设置 - 这两个配置文件有相同的格式
- 默认配置文件路径
# Set the default ros output to warning and higher
log4j.logger.ros=WARN
# Override my package to output everything
log4j.logger.ros.my_package_name=DEBUG
- ROS中为package指定环境变量可通过修改 launch 文件,如
<launch>
<env name="ROSCONSOLE_CONFIG_FILE"
value="$(find mypackage)/custom_rosconsole.conf"/>
<node pkg="mypackage" type="mynode" name="mynode" output="screen"/>
</launch>
- Logger 的命名方式
- 有root logger,名字就是
ros
,可通过环境变量ROSCONSOLE_ROOT_LOGGER_NAME
修改 - 默认情况下,某个package中logger的名字就是
ros.<package_name>
,可通过环境变量ROSCONSOLE_DEFAULT_NAME
修改。 - 如果指定了名字(named variations),那么logger的名字就是
ros.<package_name>.<name>
- 有root logger,名字就是
- 编译时删除日志,可通过设置宏
ROSCONSOLE_MIN_SEVERITY
实现,该宏的取值包括,低于这个等级的都删了
ROSCONSOLE_SEVERITY_DEBUG
ROSCONSOLE_SEVERITY_INFO
ROSCONSOLE_SEVERITY_WARN
ROSCONSOLE_SEVERITY_ERROR
ROSCONSOLE_SEVERITY_FATAL
ROSCONSOLE_SEVERITY_NONE
- 改变最小输出日志等级
#include <ros/console.h>
if( ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug) ) {
ros::console::notifyLoggerLevelsChanged();
}
- 输出日志格式
- 通过环境变量
ROSCONSOLE_FORMAT
设置日志等级,如export ROSCONSOLE_FORMAT='[${severity}] [${time}]: ${message}'
- 可选参数包括
- 时间的格式是一串数字,没有任何意义……
- 到Noetic版本(ubuntu20.04)才支持有意义的时间输出格式……
- 。。。。。。。。。。。。。。。。。。。。。。。。。。。。真的劝退
- 通过环境变量
- 输出位置选择
- 可以是 stdout, stderr, log file, /rosout 节点
- 输出日志的方式
ROS_DEBUG("Hello %s", "World");
ROS_DEBUG_STREAM("Hello " << "World");
# 指定logger名字,lgger名字就是 ros.package_name.test_only
ROS_DEBUG_NAMED("test_only", "Hello %s", "World");
ROS_DEBUG_STREAM_NAMED("test_only", "Hello " << "World");
# 根据条件输出
ROS_DEBUG_COND(x < 0, "Uh oh, x = %d, this is bad", x);
ROS_DEBUG_STREAM_COND(x < 0, "Uh oh, x = " << x << ", this is bad");
# 融合前两个条件
ROS_DEBUG_COND_NAMED(x < 0, "test_only", "Uh oh, x = %d, this is bad", x);
ROS_DEBUG_STREAM_COND_NAMED(x < 0, "test_only", "Uh oh, x = " << x << ", this is bad");
# 只输出一次
ROS_DEBUG_ONCE("This message will only print once");
# 每多少次输出一次
ROS_DEBUG_THROTTLE(60, "This message will print every 60 seconds");
ROS_DEBUG_DELAYED_THROTTLE(10, "Waiting for service 'add_two_ints'");
2. roslaunch
-
roslaunch 的作用就是:
- 启动多个节点
- 在参数服务器设置参数
- 可自动重启节点
- 自动启动
roscore
-
启动命令是:
roslaunch package_name file.launch
-
在xml文件中支持很多 substitution args
- 不知道该怎么翻译,在ROSLaunch节点启动前解析,用于指定一些参数
$(env ENVIRONMENT_VARIABLE)
:指定环境变量,替换对应参数$(optenv ENVIRONMENT_VARIABLE)
:如果设置了环境变量,就用环境变量取值,替换对应参数,如果环境变量没有数值,就默认为空$(optenv ENVIRONMENT_VARIABLE default_value)
:设置了环境变量默认值,其他的效果与上一个命令相同$(find pkg)
:寻找一个package的相对路径,常用语替代硬编码的路径。$(anon name)
:对一个字符串生成一个anonymous id,同样字符串反复生成结果相同。$(arg foo)
:寻找同一个launch文件中的<arg>
对应内容$(eval <expression>)
:kinetic中新增,解析python表达式,如"$(eval arg('foo') + env('PATH') + 'bar' + find('pkg')"
$(dirname)
:lunar中新增,获取当前launch文件所在路径
-
if/unless 语法
<group if="$(arg foo)">
<!-- stuff that will only be evaluated if foo is true -->
</group>
<param name="foo" value="bar" unless="$(arg foo)" /> <!-- This param won't be set when "unless" condition is met -->
-
常用标签很多,简单浏览一遍,主要靠后面细看
-
<launch>
:launch文件的根元素,其他元素都包含在这个元素中 -
<node>
:指定要启动的ROS节点,注意,包括重启节点选项 -
<machine>
:指定节点运行的机器 -
<include>
:导入其他launch xml文件 -
<remap>
:单独针对对某个节点,重命名topic名字(应该不止针对topic,不过没细看,具体可以参考文档)<remap from="/different_topic" to="/needed_topic"/>
-
<env>
:为节点设置环境变量,只能在 launch/include/node/machine 中使用- 环境变量相关文档,没细看
-
<param>
:设置 Parameter Server 中的参数- lunar 中新增了解析 yaml 文件的功能,没细看。
-
<rosparam>
:使用 rosparam 功能 -
<group>
:将一系列node组合起来,一起配置 -
<test>
:与node类似,只不过该node用于测试,是 rostest 的一部分 -
<arg>
:
-
-
官网中的实例一
- 最简单的实例,启动一个node。
- name应该是随便取名,pkg就是包名,type就是二进制程序名。
- 为什么有name?猜测就是某个二进制程序可以同时启动多次,每次启动的name必须不同
<launch>
<node name="talker" pkg="rospy_tutorials" type="talker" />
</launch>
- 官网中的实例二
- 设置了需要启动的机器信息
- 同时启动多个节点
<launch>
<!-- local machine already has a definition by default.
This tag overrides the default definition with
specific ROS_ROOT and ROS_PACKAGE_PATH values -->
<machine name="local_alt" address="localhost" default="true" ros-root="/u/user/ros/ros/" ros-package-path="/u/user/ros/ros-pkg" />
<!-- a basic listener node -->
<node name="listener-1" pkg="rospy_tutorials" type="listener" />
<!-- pass args to the listener node -->
<node name="listener-2" pkg="rospy_tutorials" type="listener" args="-foo arg2" />
<!-- a respawn-able listener node -->
<node name="listener-3" pkg="rospy_tutorials" type="listener" respawn="true" />
<!-- start listener node in the 'wg1' namespace -->
<node ns="wg1" name="listener-wg1" pkg="rospy_tutorials" type="listener" respawn="true" />
<!-- start a group of nodes in the 'wg2' namespace -->
<group ns="wg2">
<!-- remap applies to all future statements in this scope. -->
<remap from="chatter" to="hello"/>
<node pkg="rospy_tutorials" type="listener" name="listener" args="--test" respawn="true" />
<node pkg="rospy_tutorials" type="talker" name="talker">
<!-- set a private parameter for the node -->
<param name="talker_1_param" value="a value" />
<!-- nodes can have their own remap args -->
<remap from="chatter" to="hello-1"/>
<!-- you can set environment variables for a node -->
<env name="ENV_EXAMPLE" value="some value" />
</node>
</group>
</launch>
- 官网中的实例三
- 设置参数实例
<launch>
<param name="somestring1" value="bar" />
<!-- force to string instead of integer -->
<param name="somestring2" value="10" type="str" />
<param name="someinteger1" value="1" type="int" />
<param name="someinteger2" value="2" />
<param name="somefloat1" value="3.14159" type="double" />
<param name="somefloat2" value="3.0" />
<!-- you can set parameters in child namespaces -->
<param name="wg/childparam" value="a child namespace parameter" />
<!-- upload the contents of a file to the server -->
<param name="configfile" textfile="$(find roslaunch)/example.xml" />
<!-- upload the contents of a file as base64 binary to the server -->
<param name="binaryfile" binfile="$(find roslaunch)/example.xml" />
</launch>
- C++ 代码中如何读取参数?
ros::init(argc, argv, "param");
ros::NodeHandle nh;
std::string config_path;
n.param<std::string>("configfile", config_path, "");