前面化解了Micro_ROS通讯问题,并在 RT-Thread Studio 环境下,使用Micro_ROS软件包中的例程,实现了STM32F411CE核心板和ROS2主机的通讯。之后还尝试修改例程 micro_ros_sub_twist.c ,实现了接收 turtle_teleop_key 所发出的 turtle1/cmd_vel 信息,很是兴奋,认为只需将由 msh 命令启动的例程纳入到我的程序中(msh本身是个独立线程,和我的程序运行无关),即可实现用 turtle_teleop_key操控小车了。
计划第一步先将 micro_ros_sub_twist.c 示例程序略加修改后加入到我的程序中,作为一个线程(示例本身也是创建了一个线程),由我的程序启动,在收到ROS2主机信息的基础上,下一步实现在此线程中解析数据,并通过 RT-Thread的消息机制和其它线程交互,操控小车。
本以为这是块“豆腐”,不用一天即可完成,没想到却“硌了牙”!整整花了一周时间才搞定。欲知详情,容我慢慢道来。
一、构思
之前完成了前文所述的 ROS2 及 Micro_ROS 基本环境;这一步计划在原有的程序框架下,增加一个线程,专门负责和 ROS2 主机通讯,类似于原来程序中的命令接收线程。将收到的 ROS2 信息解析后,转发给相应线程,实现小车的操控。
第一步想要实现的是订阅(subscribe)turtle_teleop_key 所发出的 turtle1/cmd_vel 主题,其数据格式为 twist,而 RT-Thread 软件包 Micro_ROS 中就有相应例程:micro_ros_sub_twist.c,略加修改后作为我的线程即可(从表面上看十分简单)。
为减小不确定性,这一步都没有考虑使用收到的信息控制小车,只是将收到的内容输出即可,重点是能在我的程序框架下运行此例程,实现和 ROS2 主机交互。
二、实施
新线程命名为:microRosThread。
总不能直接拷贝例程吧,所以第一步先将变量名个性化:
再按我原来的方式创建线程:
将原来示例中的初始化部分放在线程的开始部分,和原程序中其它线程一样,先完成各自的初始化,之后进入while(1)循环:
之后就是原来的初始化同步机制,用事件组实现所有线程均初始化完成后,再各自进入死循环执行线程任务,避免出现初始化不同步导致的错误:
至于输出信息,因为只是作为验证,故直接拷贝原来的例程:
将例程的原来的打印输出改为 RT-Thread 的日志输出。
再在主程序中,按原来线程的创建逻辑添加此线程。
至此,程序修改完毕,并不复杂;逻辑也算是清晰、简单。
三、遇阻
本以为会很顺利,没想到编译出错。关键是出错位置均在 Micro_ROS 软件包中,自己代码中的错误很容易就消除了。
首先是RMW目录下的 types.h 文件中的 425行出错:
提示在 __attribute__((deprecated(msg))) 之前少东西,后请教高手后,说可以删除这部分,删除后,此错误消除(后来找到问题并解决后,此处不用删除),但新的错误出现。
这次是编译完成,在链接过程中提示出错:
在microRosThread 程序中,调用 set_microros_transports() 函数时,提示micro_ros_rtt.h中所声明的函数指针未定义:
此函数是将四个串口操作的函数指针传给 Micro_ROS ,实现对硬件的操作。总是提示这四个函数指针未定义。
软件包在示例程序编译时是没有问题的,且可以正确执行。按此推论,应该不是软件包代码问题。
首先怀疑是编译环境所致,缺少相应的路径。可 RT-Thread 编译使用了 Kconfig及Scon生成make 文件,我对此不熟,临时抱佛脚也未吃透,网上搜未果。找身边 C 语言高手请教,可人家也不了解 Kconfig及 Scon,无法轻易解决。
不过在此过程中,发现只是这个函数有问题,将 set_microros_transports() 封掉,编译即可通过。看来问题就出在那几个函数指针上!
至此,已反反复复折腾了近一周时间了。
期间,我尝试直接将示例程序拷贝到我的程序文件夹中,用我的程序调用示例程序中的函数,未果。
总觉得是 msh 执行的环境和我自己的程序不同,所以想尝试改变示例程序的激活方式,按照程序的方式启动,而非msh命令。
既然在我的程序中通过调用方式编译不通,那就重新建一个工程,再将示例程序直接放在主函数中,通过main启动。
按此实施,居然编译通过了;仔细观察发现,原来新建的工程主函数为 C 文件,尝试将其扩展名改为 cpp,前面的问题重现了。
看来问题找到了:是 C++ 和 C 编译的差别所致!
再次请教C语言高手,他说是的:C++程序编译时有name mangling 处理,会将所有程序中的函数重新命名;而C程序编译没有。
看来问题就在此:因为 set_microros_transports() 函数中所用的几个函数定义在文件rtt_serial_transports.c 中,而我的程序当初为了学习类定义,使用了 C++,所有文件均为 cpp,新创建的 microRosThread 也是。应该就是在C++文件中调用C函数所产生的问题。
四、化解
既然明确了问题,就有办法解决。
继续请教,他们似乎也未遇到过这类问题,但居然帮我从chatgpt 那里找到了答案:
按此实施,针对我目前的程序,简化一下,将 microRosThread 线程改为C文件,这样和调用的 Micro_ROS 软件包的 C 文件相同,不会发生编译重命名。因为软件包内容太多,不方便修改,只能用我的程序迎合软件包。
问题转化为我的主函数 C++ 程序如何调用 microRosThread.c 中的 C 函数,只有一个创建线程函数和主程序相关,其余均通过RT-Thread消息机制交互,不存在问题;参考chatgpt 的答案处理:
- 声明函数和函数指针
2)用函数指针调用C文件中的创建函数:
microRosThread程序不变,只是修改扩展名为C:
编译通过:
执行正确:
class="csdn-video-box" data-report-view="{"spm":"3001.10261","extra":{"id":"qHbKT22D-1732699692383"}}">第一次实现ROS2话题订阅
评论记录:
回复评论: