我们都知道ROS有默认的日志工具,用rosclean check
检查日志所在路径和占用的大小,一般放在~/.ros/log
,可以定义环境变量ROS_LOG_DIR
改变日志存放的位置。rosclean purge
可以清除日志,日志记录了所有的输出信息,有时我们只需要看部分信息,看自带的日志太费劲了,所以本文讲一下怎么利用ROS实现自定义的日志。
log4..
是基于log4j的一系列的c++移植版本,使用了log4j的模式结构,目前主要有:log4cxx,log4cplus,log4cpp.
一开始看到网上说ROS提供了log4cpp库的安装,而且还有对应的官方帮助页面,于是执行sudo apt-get install ros-kinetic-log4cpp
顺利安装了,在CMakeList里的find package
添加log4cpp
,结果发现编译时报错:
执行catkin_make时,编译器会寻找find package
中的package对应的-config.cmake
文件,比如roscpp
这个package,我们用roscd roscpp
进入对应安装目录中,看到一个cmake目录,进入后发现几个文件,其中一个是roscppConfig.cmake
。但是log4cxx
对应的目录连cmake文件夹都没有,所以编译失败。后来看到网上一些人的讨论,说log4cpp涉及到Orocos环境,这个比较陌生,实在懒得研究了。
后来又看到log4cxx已经集成到了ROS里面,不过不是以package的形式,而是头文件的形式,在安装ROS时就安装到了/usr/local/log4cxx
,这样就好办多了,不用担心编译问题了。
为什么使用log4cxx,因为它提供了强大的配置功能,好像ROS也能实现,但是我没有去研究,应该就是rosconsole
这个东西,它在启动时会加载$ROS_ROOT/config/rosconsole.config
作为配置文件。另外可以定义配置文件供log4cxx使用,定义环境变量ROSCONSOLE_CONFIG_FILE
为配置文件的路径。
一般做成launch文件,这样使用方便,编辑灵活:1
2
3
4
5<launch>
<env name="ROSCONSOLE_CONFIG_FILE"
value="$(find log_test)/setting.conf"/>
<node pkg="log_test" type="log_test" name="log_test" output="screen"/>
</launch>
package和可执行文件的名称都是log_test
,配置文件由环境变量指定路径。
一个典型的配置文件是这样的,看上去不复杂,但是和普通log4cxx配置不同,需要结合ROS的特性,查了好久才成功:1
2
3
4
5
6
7
8
9log4j.logger.ros.log_test=INFO,rosout_a # log_test是package名
log4j.appender.rosout_a=org.apache.log4j.RollingFileAppender # 输出到文件,超过大小后自动创建下一个
log4j.appender.rosout_a.Threshold=INFO # 最低级别是INFO
log4j.appender.rosout_a.Append=true # 追加模式
log4j.appender.rosout_a.MaxFileSize=100KB # 每个日志文件的大小
log4j.appender.rosout_a.MaxBackupIndex=10 # 最多产生多少日志文件,默认2个
log4j.appender.rosout_a.File=/home/hlhp/output.txt # 输出文件,可结合环境变量${ROSOUT_LOG_PATH}
log4j.appender.rosout_a.layout=org.apache.log4j.PatternLayout # 常用PatternLayout
log4j.appender.rosout_a.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss SSS} %m%n # 输出格式
注意:
log_test
是package名称。注意文件中就是log4j
不是log4cxx
,PatternLayout指可以灵活地指定布局模式,最后输出格式中的SSS
指的是毫秒。log4j采用类似C语言中的printf函数的打印格式格式化日志信息:1
2
3
4
5
6
7%m 输出代码中指定的消息
%n 输出一个回车换行符
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java: 10 )
日志打印宏以_ONCE
结尾,表示只调用一次:ROS_DEBUG_STREAM_ONCE("This appears only once.");
日志打印宏以_THROTTLE
结尾,表示按指定频率打印日志:ROS_DEBUG_STREAM_THROTTLE(0.1, "This appears every 0.1 seconds.");
日志消息有三个不同的目的地:控制台,话题/rosout,日志文件。相比于控制台输出,rosout
话题输出的主要作用是它在一个流中包含了系统中所有节点的日志消息。查看话题/rosout中的消息:1
rostopic echo /rosout
也可以使用图形界面查看:rqt_console
,rqt_console
订阅的是话题/rosout_agg
,话题由节点rosout收集话题/rosout
聚合之后发布。
我写的一个节点订阅了5个话题,这几个话题是单片机发布的,在回调函数里输出对应消息类型的变量信息,日志信息最终在output.txt
中,注意输出信息是用逗号隔开,这样用EXCEL打开可以将各部分隔开,实现表格效果:
说白了,代码里没用到log4cxx的东西,当然也可以用,但是我这个例子没必要,关键用的是它的配置文件。
显然这是编码问题,在ROS_INFO之前加入下面代码中的一行:1
2
3// 设置区域为中文,或者直接取所有枚举的并集,不必仔细研究了
// setlocale(LC_CTYPE, "zh_CN.utf8");
setlocale(LC_ALL, "");
默认的日志输出是这样的:1
2[ INFO] [1556115082.590417546]: msg:91
[ INFO] [1556115083.591389486]: msg:12
感觉时间没用必要,希望能像log4cxx一样自定义,其实看源码rosconsole.cpp
可知ROS日志就是基于log4cxx。我们可以修改环境变量ROSCONSOLE_FORMAT
定制格式,格式包括以下选项:1
2
3
4
5
6
7
8
9severity
message
time
thread
logger
file
line
function
node
看一下rosconsole.cpp
源码部分:1
2
3
4
5
6
7
8
9const char* g_format_string = "[${severity}] [${time}]: ${message}";
Formatter g_formatter;
format_string = getenv("ROSCONSOLE_FORMAT");
if (format_string)
{
g_format_string = format_string;
}
g_formatter.init(g_format_string);
从中可以发现源码里先给了个默认格式,我们也可以针对环境变量自己设置格式.
网上有人问能不能自定义日志信息的颜色,结果发现在函数Formatter::print
里,各等级的日志信息的颜色已经写死了,比如levels::Fatal
对应COLOR_RED
,所以不能自定义了.
根据环境变量,我们可以这样修改:1
export ROSCONSOLE_FORMAT='[${severity}][${node}] [${function}] line_${line} ${message}'
只保留等级、节点名、函数、行号、信息,结果是这样的:1
2
3[ INFO][/Pub] [main] line_19 msg:13
[ INFO][/Pub] [main] line_19 msg:37
[ INFO][/Pub] [main] line_19 msg:73
ROS提供两个带界面的日志工具:rqt_console和rqt_logger_level
命令如下:1
2rosrun rqt_console rqt_console
rosrun rqt_logger_level rqt_logger_level
使用很简单,就不详细解释了
在ROS中坐标系总是3维的,而且遵循右手法则,Z轴用拇指表示,X轴用食指表示,Y轴用中指。X轴指向前方,Y轴指向左方,Z轴指向上方。研究坐标系绕某轴旋转时,也是用右手法则,右手握住坐标轴,大拇指 的方向朝着坐标轴朝向的正方向,四指环绕的方向定义沿着这个坐标轴旋转的正方向。
在Rviz中,默认X轴是红色、Y轴时绿色、Z轴是蓝色,也就是XYZ对应RGB。
机器人的朝哪走是向前,这是由陀螺仪和码盘决定的。
机器人自身坐标系一般用base_link
表示,所在位置为原点,朝向的方向为X轴正方向,左方为Y轴正方向,头顶是Z轴正方向。
base_footprint
坐标系,原点为base_link
坐标系原点在地面的投影,只有Z值有变化
earth
坐标系适用于多个机器人的环境,先不讨论了
如果想使用其他坐标系,必须使用tf
包对其与map
坐标系进行转换,比如下面三个坐标系都有直接或间接的转换:
如果没有经过转换,就会报错,例如:
For frame [tag_9]: No transform to fixed frame [/map]. TF error: [Could not find a connection between ‘map’ and ‘tag_9’ because they are not part of the same tree.Tf has two or more unconnected trees.] tag_9
没有向固定坐标系map
的转化,不在tf树上。
ROS最主要的构成是节点(node),每一个节点可以看做为一个单独的处理单元.它可以接受外部的消息(这个步骤叫订阅,subscribe),也可以向外部发出自己的消息(这个步骤叫发布,publish);
节点之间通讯的管理需要一个管理员(ros中被称为roscore节点).当节点分布在不同的计算机上时,需要指明master.ROS底层的通信是通过HTTP完成的,因此ROS内核本质上是一个HTTP服务器,它的地址一般是http://localhost:11311/
,即本机的11311端口。当需要连接到另一台计算机上运行的ROS时,只要连上该机的11311端口即可。
Binary install 是指通过sudo apt-get install ros-kinetic-pkg
的方式安装的。这样的包会直接被安装进opt/ros/kinetic/share
文件夹里面
rosbash
是ROS提供的一系列便利的命令,在source /opt/ros/kinetic/setup.bash
后可使用。一个setup.*sh
会尽可能撤销先前其他所有setup.*sh
的影响,然后它再自己施加影响。
source
可在最后加上选项--extend
,作用是 skips the undoing of changes from a previously sourced setup file,
devel/setup.bash
的作用是让我们能使用roscd
等ROS独有的shell命令,还有识别当前工作空间的package名称
roscd: 直接跳到某个package的地址,这个是用的最多的。不过它的本质是针对环境变量ROS_LOCATIONS
中规定的地址,它的格式是这样:1
export ROS_LOCATIONS="pkgs=~/ros/pkg:openmv=/home/user/qt-ros-ws/src/openmv_cam"
pkgs和openmv是其中规定的两个地址,二者用冒号隔开,现在就可以使用roscd openmv
了
rosls: 列出某个package中的所有文件
rospd: 这个命令特殊,可以记录和跳到所有package的地址,可以看做roscd的升级版。rospd package
会跳到某个package,rospd
会列出去过的所有package地址并且编号,以后可以直接rospd num
到指定地址
查看当前运行的节点用rosnode list
命令
优雅关闭每个节点,使用rosnode kill
命令。使用ctrl+c或者直接关闭终端也可以关闭节点,但这种方法不免过于简单粗暴,而且并未完全的关闭节点,因为节点管理器那里仍然有记录。
rosnode kill -a # 关闭除rosout
外的所有节点
ping节点: rosnode ping node
,检查一个节点的连通性
显示ROS节点使用的IP: rosnode machine
rospack list
rospack find turtlesim
,输出/opt/ros/kinetic/share/turtlesim
rospack depends turtlesim
使用catkin_make
系列命令时,会把工作空间所有的包的CMakeLists先检查一遍,有时会很麻烦。使用catkin_make_isolated
命令可以针对单独的包进行编译,但速度会慢很多。
rqt
命令会运行一个对话框:
rqt_graph
包用于显示各节点和主题之间的关系,命令:rosrun rqt_graph rqt_graph
rosdep是一个安装系统依赖项的命令,用于package的依赖项安装,比如对PACKAGE:1
rosdep install PACKAGE
也可以安装工作空间中所有package的依赖项,先cd到工作空间的根目录,执行下面命令:1
rosdep install --from-paths src --ignore-src -r -y
用曲线查看主题的各个消息情况rosrun rqt_plot rqt_plot
,这个工具是用Qt开发的
只用catkin_make
遍历所有包时,运行的是cmake
进程。实际编译时,才运行cc1plus
进程,一个核一个进程。
catkin_make
编译时,对工作空间所有packages的编译顺序是按拓扑遍历的,不是按字母也不是按创建时间。修改任意一个CMakeLists.txt
或者package.xml
后,执行catkin_make
会将工作空间的所有package的CMakeLists重新处理一遍。catkin_make
遍历所有包的过程,占CPU并不大,占CPU还是在编译阶段
catkin_make
可能报错内存不足,尤其是move_base
,因为需要占用很多资源
catkin_make
时占CPU太大,导致几乎死机。可以从另一台电脑SSH,执行 pkill cc1plus
停止编译
编译整个工作空间是这样的:
先检查了slam_gmapping_nodelet
的依赖项,然后是编译fake_localization和depth_image_proc,然后又开始检查map_to_image_node,并不是按包的顺序编译slam_gmapping_nodelet
,也就是并不是一个一个包按顺序编译,而是万箭齐发式的。因此,要看某一个包的问题时,不要编译整个工作空间,否则很难找。
catkin_make -D
是指定编译的配置, 在D后面增加命令 ,比如指定编译类型Release和核数编译: catkin_make -DCMAKE_BUILD_TYPE=Release -j8
编译时,catkin_make会显示各种包的情况plain cmake
是纯cmake的包,不能用catkin_make
metapackage
都是ROS官方的,自己不用写这种类型,它不安装任何文件(除了package.xml说明的), 也不包含任何代码文件、test、launch文件,你会发现navigation
这个包只有四个文件:CHANGELOG.rst
, CMakeLists.txt
, package.xml
, README.rst
它的package.xml
会含有下面内容:1
2
3<export>
<metapackage />
</export>CMakeLists.txt
中还有一句catkin_metapackage()
这里涉及到 16和18版本的不同 ,对于16 kinetic,会有如下如下报错
18 melodic不会有这个报错,此外18版对C++11类型的支持更好,有时无需手动修改cmake
目前常用的package.xml开头是这样的:1
2
3
4
<package format="2">
<name>name</name>
<version>0.0.0</version>
旧版本的是这样的:1
2
3<package>
<name>base_local_planner</name>
<version>1.14.4</version>
旧版本的不能使用exec_depend
,而是用run_depend
,否则会报错
以前一直认为单独编译某个package的命令是catkin_make --pkg package1
,结果这样仍然会将工作空间中所有package的CMakeLists全检查一遍,花费时间相当长,实际的命令是这个:1
catkin_make -DCATKIN_WHITELIST_PACKAGES="package1;package2"
可以编译一个或多个package,但是执行这个命令之后再catkin_make
回发现它仍然只编译上次的package。恢复成编译所有package去掉引号里面的内容就行:1
2
3catkin_make -DCATKIN_WHITELIST_PACKAGES=""
# 对ninja编译的包
catkin_make -DCATKIN_WHITELIST_PACKAGES="package" --use-ninjabuild
文件夹存放cmake
和make
相关的文件,devel
文件夹存放编译生成的文件和目标,包括setup.sh
。
跟make clean
类似,也有个catkin_make clean
命令,它会删除所有编译的可执行文件或库文件,但是不会删除删除头文件,例如msg和srv生成的头文件
现在执行INSTALL
命令不再是sudo make install
了,而是catkin_make install
,它相当于:1
2
3
4
5cd ~/catkin_ws/build
# If cmake hasn't already been called
cmake ../src -DCMAKE_INSTALL_PREFIX=../install -DCATKIN_DEVEL_PREFIX=../devel
make
make install
这样执行完以后,工作空间会出现一个install
文件夹,里面存放着编译生成的库文件,问题是它会把所有package的install都进行处理,如果想改变这个目录的位置,执行catkin_make -DCMAKE_INSTALL_PREFIX=path install
。另外在CMakeLists中指定安装目录用SET(CMAKE_INSTALL_PREFIX < install_path >)
编译出的.so和可执行文件可以直接放到另一台电脑使用,但是如果是跨平台,catkin_make install
恐怕不可用,需要交叉编译。
结果会在share/status_panel/cmake
中生成两个cmake文件
这两个文件是做依赖包时必需的,比如roscpp就有相应的文件
如果想使用ROS的头文件,必须在CMakeLists里加入下面内容,也就是链接ROS的头文件和库:1
2
3
4
5
6
7
8include_directories(
# include
${catkin_INCLUDE_DIRS}
)
find_package(roscpp)
target_link_libraries(foo
${catkin_LIBRARIES}
)
别忘了target_link_libraries在add_excutable之后。用message函数可以看到include
文件夹和libraries
文件夹如下:
在CMakeLists中加入catkin_package()
, 才能在执行catkin_make
后,在devel/lib
中生成可执行文件