玩kobuki设备的人都知道可以在上面运行不同的原生程序,像turtlebot_teleop keyboard_teleop.launch,turtlebot_navigation gmapping_demo.launch等等,这些原生程序就像是跑在机器上的app,由于kobuki driver对上对下都提供了详细的通信方式。因此想充分了解kobuki,重点要分析kobuki driver。
对上ros apps 的通信方式为ros topics,详细信息见前面的文章:turtlebot的mobile_base节点
对下kobuki firmware 的通信方式为 serial 读写,详细信息见前面的文章:kobuki驱动通信规范
本文主要介绍kobuki driver的主要逻辑和从firmware读出的主要信息如何转成apps需要的topics
在ros中程序都以节点方式存在,运行 roslaunch turtlebot_bringup minimal.launch,显示
看上去是好多个节点进程都有各自的pid,但其中很多节点都采用了nodelete方式,运行在同一个进程中,详细信息见前面的文章:ros中的nodelet。
其中主要的代码文件为kobuki.cpp,kobuki_ros.cpp,kobukiNodelet.cpp,编译生成的文件为libkobuki_nodelet.so,以动态库的形式存在。拉起kobuki节点相当动态加载ibkobuki_nodelet.so。在xxx/turtlebot_bringup/launch/includes/kobuki/mobile_base.launch.xml 中可以看到如下:
<node pkg="nodelet" type="nodelet" name="mobile_base" args="load kobuki_node/KobukiNodelet $(arg manager)">
从而得出结论,是mobile_base_nodelet_manager这个进程加载了libkobuki_nodelet.so,而kobuki node的名字就是mobile_base。
备注:节点和进程不是一一对应的,一个进程可以只包含一个节点,一个进程还可以包含多个节点。
入口:当加载动态库时,”load kobuki_node/KobukiNodelet $(arg manager)“
1.首先执行代码为kobukiNodelet.cpp的onInit(),在该函数中先调用kobuki_ros.cpp中的init(),代码如下:
if (kobuki_->init(this->getPrivateNodeHandle(), this->getNodeHandle()))
然后启动一个线程时刻更新状态
update_thread_.start(&KobukiNodelet::update, *this);
2.查看kobuki_ros.cpp中的init()
2.1先定义了发布和订阅的topics
advertiseTopics(nh);
subscribeTopics(nh);
2.2定义了一系列slot句柄,方便后续直接使用slot句柄发布接收topics。
2.3设置一系列Driver Parameters。
2.4设置轮胎Joint States的参数
2.5初始化了odometry参数和信息,因为tf只涉及到位置坐标的变换,而kobuki的线速度和角速度则有odometry来计算并发布。
2.6初始化驱动,即调用kobuki.cpp的init()函数。
3.查看kobuki.cpp的init()函数
3.1初始化一些Signals句柄,用来发布订阅各自的主题
3.2打开通信串口
serial.open(parameters.device_port, ecl::BaudRate_115200, ecl::DataBits_8, ecl::StopBits_1, ecl::NoParity);
3.3通过串口获取一些kobuki基础信息
3.4启动线程Kobuki::spin,代码如下
thread.start(&Kobuki::spin, *this);
4.查看Kobuki::spin代码,主要逻辑都在这个线程里实现
4.1 建立一个循环,当发送关闭请求后才会从循环中跳出
while (!shutdown_requested)
4.2 在这个循环中,不停地读串口中的数据,鉴于kobuki firmware 以50Hz的频率不停地通过串口上传基础信息,包括()。在循环中每次读完数据就不进行sleep操作。
4.3 读完串口信息后处理这些基础信息,转成topics格式发布出去
4.4 下发基础运动命令,也已50Hz的频率下发。这里有个注意点:如果apps没有操作kobuki运动,则下发的命令(线速度,角速度)均为0。
sendBaseControlCommand();
5.额外的命令下发
diver 通过订阅topics知道ros apps下发的命令
除了订阅的基础运动topics(commands/controller_info),剩余dirver订阅的topics,均不再spin线程中处理,由相关注册函数直接调用serial.write命令下发的firmware上。见subscriber_callbacks.cpp,由订阅的topics注册函数
这里没看到有锁,故应该会有命令下发的冲突吧。这里还需要详细查看代码。
6.odom
见~~~