选择调试器
写了ROS程序,如果调试呢?很长一段时间,我都是通过cout变量看哪儿除了问题进行调试的,大佬勿笑话.其实程序不大的话还是蛮方便,什么多余的设置都不用.然而面对大型的程序这就有些吃力了.一般大家通过IDE调试程序,ROS官方列出了许多可用的IDE
http://wiki.ros.org/IDEs
调试程序最常用的一般来说无非就是: 加断点,显示变量值,执行下一步等几个命令.
大家使用的IDE可能不一样.我使用的VScode,有的使用Qtcreator, Ecllipse, Clion,有的不使用IDE.要调试ROS的话,每个IDE都有不同的设置方法.VScode我目前为止都不知道怎么设置= =...在上面的网址下给出了VScode关于ROS的plugin,我安装了.这个plugin能给出的功能其实自身并不包含调试,而是使一些ros命令方便在VScode里实现.
我注意到qtCreator等几个IDE貌似添加了很好的ROS辅助功能,不过我查找了一番(并没有花太多时间,也许遗漏了),里面讲了怎么在IDE里设置ROS啊什么的,但是没有讲之后该怎么做.也就就是普通的在某一行程序旁添加断点??我也许以后才取试了,我想找一个比较universal的方法.跟什么IDE没有关系,那么这就非使用gdb莫属了.
gdb就相当于ubuntu自带的debugger吧.具体能调试哪些语言什么见官网.
https://www.gnu.org/software/gdb/
使用gdb调试,和程序猿理想中的调试方法有些出入.使用IDE习惯了的用户,设置断点会在程序旁某个位置点一下,然后运行程序调试.程序就会在断点处暂停.
gdb调试,一切都在terminal中使用命令行实现.
https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf
上面pdf中的内容包含了gdb调试最常用的命令,我们根据它来调试程序.
普通的非ROS程序用gdb怎么调试呢?在编译好后(编译的方法和正常的程序一样,不过要在cmakelists前面添加)
set(CMAKE_BUILD_TYPE Debug)
之后直接使用
gdb 可执行程序
就可以进入gdb调试模式了.如果你的程序需要添加一些输入,比如说,你正常跑程序是下面这样
./可执行程序 arg1 arg2 arg3
那么你使用gdb的话就是
gdb 可执行程序
r arg1 arg2 arg3
r
代表的是run
.
ROS里调试稍微有些不一样,不过大体相同.
我们先写一段ROS代码.再使用gdb尝试添加断点,查看变量等内容.使用的代码是我们在ROS从入门到放弃第二讲的内容中用到的pub数据类型Int8的代码.没看过那个的也没关系,和ROS官方tutorial发布一个string的代码几乎一样.下面列出代码
#include "ros/ros.h"
#include "std_msgs/Int8.h" //#include "std_msgs/String.h"
#include <sstream>
//publish int8 number
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::Int8>("chatter", 1000); //ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
std_msgs::Int8 msg; //std_msgs::String msg;
// std::stringstream ss;
// ss << "hello world " << count;
msg.data = count;// msg.data = ss.str();
ROS_INFO("%d", msg.data); //ROS_INFO("%f", msg.data.c_str())
chatter_pub.publish(msg); //line 28
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
把这段程序随意写入一个ros package当中,写入CMakeLists.txt进行编译.我们还需要在pakcage的CMakeLists.txt接近顶部添加
set(CMAKE_BUILD_TYPE Debug)
其实这个我没试过哈哈哈,但是应该能针对某一个package作用,自己我直接把所有ros package都设置成debug模式了.方法是使用catkin_make编译的时候,往后添加一点东西
catkin_make -DCMAKE_BUILD_TYPE=Debug
这样编译之后,你的所有程序都可以使用gdb进行debug了.
开始调试
跑ros程序的两个方法一个是rosrun,一个是roslaunch.使用下面语句执行文件
rosrun --prefix 'gdb -ex run --args' [package_name] [node_name]
执行完上面的语句,你会发现程序直接开始跑了,我还来不及设置断点什么的呢.问题应该出在run那个参数上,不过我暂时没找到替代的,得想想其他办法.
rosrun其实就相当于直接执行二进制文件./文件
.那么我们直接找到catkin_make产生的二进制文件进行运行不就完了?每一个package产生的二进制文件存放的位置在
you_catkin_workspace/devel/lib/your_package_name
里.cd进去上面的地址,你应该能看到你在CMakeLists.txt里通过add_executable产生的那个二进制文件的名字(就是rosNode了).在terminal中输入
gdb node_name
就会发现进入gdb模式了,terminal的输出像下面这样
这时候我们可以输入第三个链接pdf中的命令进行调试了.
设置断点
pdf中写的设置断点的方法为
break <where>
,这个where指的是程序的哪一行,由于简书的代码段貌似不能显示行数,我在上面的程序的28行注释了一下line 28.我们在terminal中输入
break 28
会看到一则消息关于断点设置的.然后我们就可以跑程序了.在terminal中输入
run
我们根据terminal的输出应该能很轻易的知道28行程序暂停了.断点已经设置成功了.
另外也可以
break 函数名
设置断点,这样进入函数的时候程序会暂停
查看变量值
现在我们要查看某个变量的值呢?pdf中显示是print/format <what>
或者display/format <what>
.what表示是什么变量.我们在terminal中输入
display count
便能看到程序中变量count的值了.
执行下一步
同样查看pdf得知,在terminal中输入next
就可以了.
设置被包含的程序的断点
我们写大型程序的时候,一个rosnode肯定include了好多文件,上面的break 28
默认在主函数中设置断点了,那么我们想在其他不在同一个cpp文件中的函数设置断点之类的呢?比如我们在CMakeLists.txt中编译的时候使用的下面语句
add_executable(main main.cpp b.cpp c.cpp)
之后使用
gdb main
进行调试,如果我们想在b.cpp中加一个断点,我们只需要在terminal中特殊指明是哪个cpp加上行数即可,比如
break b.cpp:line_number
其中line_number表示你想在b.cpp中的哪行代码添加breakpoint.
显示出错的位置
gdb调试出错了,比如出现segmentation fault,通常会自动显示出错位置,输入where可以显示出更详细的内容
退出gdb
程序正在运行,按下crtl+c,在输入q退出.
程序没有运行,直接输入q退出.
总之根据pdf我们慢慢尝试,把gdb调试运用熟练.上面的调试其实和ROS没有什么关系,普通的c++程序编译好后也是这么调试.使用roslaunch时则稍有不同.但是非常简单.比在需要调试的那个node之后添加一句话就可以了.比如上面的发布int8的程序,假设名字叫debugTest1.cpp,在test包里,由他编译出来的node的名字是debugTest1.跑这个nodelaunch文件名叫debugTest.launch,内容如下
<launch>
<node pkg="test"
type="debugTest1" name="debugTest1" output="screen"/>
</launch>
那么为了debug,只需要再该node改成下面即可
<launch>
<node pkg="test"
type="debugTest1" name="debugTest1" output="screen"
launch-prefix="xterm -e gdb --args"/>
</launch>
之后仍旧如常地跑文件roslaunch test debugTest.launch
.
会出现下面的窗口
你会在上方小的黑色窗口里输入gdb的相关命令进行调试.
roslaunch里添加的那一行内容也可以更换,见
http://wiki.ros.org/roslaunch/Tutorials/Roslaunch%20Nodes%20in%20Valgrind%20or%20GDB
我们选择的是第一个.你可以自己添加其他内容试试.
一些小的trick或者命令
print Eigen矩阵
如果x代表矩阵,那么命令这么书写
print *x.data()@length_x
其中length_x是矩阵的row*column.如果length_x小于矩阵的size的话,那么会由第一行第一个从左往右输出到第二行到第三行...由此到length_x长度.
如果直接print x
则除了矩阵的内容之外还有一大堆多余的东西.
在load shared library的时候暂停
set stop-on-solib-events 1
使用了这行命令在每次有shared library载入的时候程序会暂停.这时候你可以针对该lib设置断点什么的.其实可以直接设置尚未载入的lib的source文件的断点,语法和设置被包含的程序的断点
那部分一样.不过由于lib尚为被载入所以找不到程序,gdb会问你
No source file named System.cpp.
Make breakpoint pending on future shared library load? (y or [n])
选y就可以了.