最后启动KobukiRos对象的update()函数,用于检测底盘的各种状态,比如底盘的连接状态,电池状态,轮子是否接触地面等等。
Turtlebot的运行机制总结如下:
启动minimal.launch之后,启动Kobuki_nodelet节点,新建两个类对象,一是用于底盘和ROS通信类KobukiRos(桥梁),一个是底盘的驱动类Kobuki(心脏)。
通信类中定义了用于接收ROS命令话题的接收者,接收如/cmd_vel等话题。接收者接收到话题消息后,通过回调函数,将ROS通用格式的数据转换为底盘可用格式的数据,并调用驱动类的函数接口,通过串口给底盘单片机发送数据,实现对底盘的操控。
通信类中还定义了用于发布底盘状态话题的发布者,如发送/sensors/imu_data的话题。通信类通过驱动类的函数接口获得原始传感器数据,将其转换为ROS通用数据格式,由发布者发送给ROS上层,来实现反馈功能。
最后,Kobuki_nodelet启动了一套完整的监测机制,用于监测底盘的各种异常状态。
参考源码:
kobuki_nodelet.cpp:(实现Kobuki_nodelet类)
https://github.com/yujinrobot/kobuki/blob/indigo/kobuki_node/src/nodelet/kobuki_nodelet.cpp
kobuki_ros.hpp:
https://github.com/yujinrobot/kobuki/blob/indigo/kobuki_node/include/kobuki_node/kobuki_ros.hpp
kobuki_ros.cpp:(实现KobukiRos类,相当于桥梁)
https://github.com/yujinrobot/kobuki/blob/indigo/kobuki_node/src/library/kobuki_ros.cpp
subscriber_callbacks.cpp:(实现回调函数,ROS数据格式和底盘数据格式的转换)
https://github.com/yujinrobot/kobuki/blob/indigo/kobuki_node/src/library/subscriber_callbacks.cpp
kobuki.hpp:
https://github.com/yujinrobot/kobuki_core/blob/indigo/kobuki_driver/include/kobuki_driver/kobuki.hpp
kobuki.cpp:(实现Kobuki类,驱动类,相当于心脏)
https://github.com/yujinrobot/kobuki_core/blob/indigo/kobuki_driver/src/driver/kobuki.cpp
详细跳转机制:(以/cmd_vel为例)
在Kobuki_nodelet.cpp中,首先新类KobukiRos对象。然后调用KobukiRos的init()初始化函数,在这个函数中,声明了许多发布者和订阅者,下面以/cmd_vel话题为例,继续深入。
velocity_command_subscriber= nh.subscribe(std::string("commands/velocity"), 10,&KobukiRos::subscribeVelocityCommand, this);
当有/cmd_vel话题发布时,这个发布者的回调函数自动被调用,即进入KobukiRos::subscribeVelocityCommand()中。
在回调函数中,其将数据发送给驱动类Kobuki的函数中:
kobuki.setBaseControl(msg->linear.x, msg->angular.z);
kobuki.setBaseControl这个函数通过调用diff_driver类(差速轮驱动)的setVelocityCommands()函数,将/cmd_vel话题的Twist类型数据转换为左右轮速,并保存在vector容器中。
注意,diff_driver::setVelocityCommands()并没有直接将左右轮速通过串口发送给底盘,而是保存在vector
为什么没有直接通过串口传输呢,那传输步骤在哪呢?我们接着来看,以上的步骤其实仅仅是一个线程,我们再来看看另外的线程。
继续回到KobukiRos的init()初始化函数,除了声明了发布者和订阅者用于接收发布数据,同时也调用了驱动类Kobuki的init()初始化函数。
在驱动类Kobuki的init()初始化函数中,初始了串口,并设置了一些必要信息,最后进入了Kobuki::spin()循环函数。
在spin()函数中,首先判断了是否打开了串口,如果没打开就打开并设置串口。确认完毕之后,先做读操作,然后做写操作。在写操作中,调用了sendBaseControlCommand(),在这个函数中,先调用了上面讲过的diff_driver::velocityCommands()函数,将转换好的数据设置在Kobuki类的vector