5、开发环境-调试C/C++控制器

发布时间 2023-07-27 11:54:20作者: gary_123

控制器处理

在Webots环境中,Webots应用程序和每个机器人C/C++控制器在不同的操作系统进程中执行。例如,当执行“soccer.wbt”世界时,内存中总共有八个进程;一个用于Webots,六个用于六个玩家机器人,一个用于监督机器人。若要使用Microsoft Visual Studio调试C/C++控制器,请参阅此处。
当控制器进程执行非法指令时,操作系统会终止该指令,而Webots进程和其他控制器进程仍处于活动状态。尽管Webots仍然处于活动状态,但由于它在等待来自终止的控制器的数据,因此模拟会被阻止。因此,如果您遇到模拟意外停止,但Webots GUI仍有响应的情况,这通常表明控制器已崩溃。这可以很容易地通过列出此时的活动进程来确认:例如,在Linux上,键入:

$ ps -e
...
12751 pts/1    00:00:16 webots
13294 pts/1    00:00:00 soccer_player
13296 pts/1    00:00:00 soccer_player
13297 pts/1    00:00:00 soccer_player
13298 pts/1    00:00:00 soccer_player
13299 pts/1    00:00:00 soccer_player
13300 pts/1    00:00:00 soccer_player
13301 pts/1    00:00:00 soccer_supervisor <defunct>
...

在macOS上,使用ps-x,在Windows上使用任务管理器。如果您的一个机器人控制器在列表中丢失(或显示为无效),则确认它已崩溃,因此阻止了模拟。在本例中,“soccer_supersvisor”已崩溃。请注意,控制器的崩溃几乎肯定是由控制器代码中的错误引起的,因为Webots中的错误会导致Webots崩溃。幸运的是,GNU调试器(gdb)通常可以帮助查找崩溃的原因。以下示例假设“soccer_supersvisor”控制器存在问题,并指示如何进行调试。

将GNU调试器与控制器一起使用

第一步是使用调试目标重新编译控制器,以便将调试信息添加到可执行文件中。您必须直接在终端中重新编译控制器,因为Webots文本编辑器“构建”按钮将从构建中省略调试信息:

$ make clean
$ make debug
...

重新编译控制器后,需要确保Robot节点的控制器设置为外部控制器。如果不是,则可以从场景树中进行设置:单击“暂停”和“重置”按钮,将Robot节点的控制器字段设置为<extern>,然后保存世界文件。从终端,转到包含控制器程序的文件夹,并用gdb启动它:

$ gdb my_controller

在gdb中,例如输入如下:

(gdb) break my_controller.c:50
(gdb) run

然后,使用run按钮(也可以使用Step、Real Time或Fast按钮)运行Webots模拟。您的控制器程序将开始控制Webots中的外部机器人。一旦到达断点,您将能够查询变量、设置新的断点等。
然后,cont命令将指示调试器恢复进程的执行。您也可以使用步骤功能逐步进行。
控制器的执行可以随时中断(ctrl-C),以便查询变量、设置断点等。当发生崩溃时,gdb会打印类似于以下的诊断消息:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208314144 (LWP 16448)]
0x00cd6dd5 in _IO_str_overflow_internal () from /lib/tls/libc.so.6

这表示问题的位置。您可以使用gdb的where命令来更精确地检查调用堆栈。例如类型:

gdb) where
#0 0x00cd6dd5 in _IO_str_overflow_internal() from /lib/tls/libc.so.6
#1 0x00cd596f in _IO_default_xsputn_internal() from /lib/tls/libc.so.6
#2 0x00cca9c1 in _IO_padn_internal() from /lib/tls/libc.so.6
#3 0x00cb17ea in vfprintf() from /lib/tls/libc.so.6
#4 0x00ccb9cb in vsprintf() from /lib/tls/libc.so.6
#5 0x00cb8d4b in sprintf() from /lib/tls/libc.so.6
#6 0x08048972 in run(duration=0) at soccer_supervisor.c:106
#7 0x08048b0a in main() at soccer_supervisor.c:140

通过仔细检查调用堆栈,您可以找到错误的来源。在这个例子中,我们将假设sprintf函数是可以的,因为它在系统库中。因此,问题似乎是由run函数中非法使用sprintf函数引起的。必须仔细检查源文件“soccer_supersvisor.c”的第106行。当控制器仍在内存中时,您可以查询一些变量的值,以了解发生了什么。例如,可以使用框架和打印命令:

(gdb) frame 6
#6  0x08048953 in run (duration=0) at soccer_supervisor.c:106
106         sprintf(time_string, "%02d:%02d", (int) (time / 60),
 (int) time % 60);
(gdb) print time_string
$1 = 0x0

frame命令指示调试器选择指定的堆栈帧,print命令打印表达式的当前值。在这个简单的例子中,我们清楚地看到问题是由传递给sprintf函数的NULL(0x0)time_string参数引起的。接下来的步骤是:

  1. Fix the problem
  2. Recompile the controller
  3. Reload the world to give it another try.
 一旦它工作并给出正确的输出,您就可以从Makefile中删除-g标志。