美文网首页ROS
ROS外传:程序调试(c++)

ROS外传:程序调试(c++)

作者: 陈瓜瓜_ARPG | 来源:发表于2018-12-10 14:07 被阅读533次

    选择调试器

    写了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的输出像下面这样

    gdbDebug.png
    这时候我们可以输入第三个链接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
    会出现下面的窗口

    debugRoslaunch.png

    你会在上方小的黑色窗口里输入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就可以了.

    相关文章

      网友评论

        本文标题:ROS外传:程序调试(c++)

        本文链接:https://www.haomeiwen.com/subject/tuerhqtx.html