本文主要内容为关于机器人操作系统(Robot Operation System)编程基础内容,以简单概念讲述加上实例操作结合,适合初学者入门学习。
本文的学习目标如下:
通常来说,一个移动机器人是多进程协同工作的,除了少部分进程可以独立完成工作,其他进程都需要进程间的数据交互,因此进程间的通信机制是构建复杂机器人项目的基础。
图1 ROS进程通信机制示意图
在ROS的世界里,最小的进程单元就是节点(Node)。一般情况下,一个节点负责机器人的某一个单独模块。一个软件包里有多个执行文件,通常为C++编译生成的可执行文件或者Python脚本,可执行文件在运行之后就变成了一个进程(Process),这个进程即为ROS中的节点。
节点管理器在整个构架里相当于节点管理中心,管理者各个节点,管理着各个节点(Node)。节点在启动时,首先启动需要在Master处进行注册,之后Master会将该Node纳入到整个ROS程序当中。Node之间的通信也是通过Master进行连线。当ROS程序启动时,首先启动Master,再由Master依次启动Node。在Node建立起连接以后,Master的任务就完成了,此时如果关闭Master,已运行的Node之间的通信还可以继续进行。下面是Node和Master相关命令:
roscore //启动ros Master
rosrun[pkg_name] [node_name] //启动一个aiqiyi
rosnode list //查看当前运行的node信息
rosnode info[node_name] //显示node的详细信息
rosnode kill [node_name] //结束node
roslaunch[pkg_name] [file_name.launch] //启动master和多个node
Topic是常用的ROS通信方式。对于实时性、周期性的消息,使用Topic模式来传输是最佳的选择。Topic是一种点对点的单向通信方式,这里的“点”指的是节点Node。Topic要经历下面的初始化过程,首先Publisher发布节点和Subscriber在节点管理器Master进行注册;然后Publisher会发布Topic话题,Subscriber在节点管理器Master的指挥下会订阅该话题,从而建立起节点与节点之间的联系。
图3 Topic通信示意图
主要通过通知小海龟运动的实例介绍Topic话题通信中的Publisher。采用C++实现程序编写、编译及控制,具体操作如下:
1.打开终端输入以下创建名为learning_topic的功能包:
cd ~/mrobot_ws/
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
其中catkin_create_pkg命令的作用是创建一个新的catkin功能包,上面代码的作用是创建一个名为learning_topic的功能包,并添加该功能包所需要的依赖roscpp、rospy、std_msgs、geometry、turtle司马。
2.在learning_topic文件下的src文件夹中创建velocity_publisher.cpp文件,构建代码框架:
#include
#include
int main(int argc,char **argv){
return 0;
}
3.在main函数中初始化ROS节点,在ROS系统中注册节点名称:
ros::init(argc,argv,“velocity_publisher” );
ros::init(NodeHandle nh;
4.接着创建句柄,实例化Node,并创建发布者对象:
ros::init(Publisher turtle_vel_pub=nh.advertise(“turtle1/cmd_vel”,10);
NodeHandle是与ROS系统通信的主要接入点,调用NodeHandle的advertise函数将发布一个话题Topic,函数的第一个参数为Topic名称,第二个参数为消息队列大小。在实际应用中,消息的发送和接收之间不是同步进行的,存在消息发布和接受的时间差。因此ROS会把发布的消息写入缓冲区,供接收程序读取,一旦超过缓冲区信息数量,最早的数据就会被丢弃。
以上代码在执行时告诉主机,将在一个名字为“/turtle1/cmd_vel”的Topic上发布一个geometry_msgs:: Twist类型的消息,这就使得主机告诉所有订阅了“/turtle1/cmd_vel” Topic的节点,我们将在这个Topic上发布的数据;第二个参数告诉主机,能缓冲10条信息,超过了十条就会覆盖之前的信息。
5.设定发布频率,单位为赫兹(Hz),本例设置为10Hz,即每秒发布10次数据:
ros::Rate loop_rate(10);
6.循环发布线速度为0.5m/s、角速度为0.2rad/s的消息并打印,至此velocity_publisher.cpp代码部分完成:
while(ros::ok()){
geometry_msgs::Twist vel_msg;//ROS预定义消息类型
vel_msg.linear.x=0.5;
vel_msgs.angular.z=0.2;
turtle_vel_pub.publish(vel_msg);
ROS_INFO(“Publish turtle velocity command[%0.2f m/s,%0.2f rad/s]”,vel_msg.linera.x,velmsg.angular.z);
loop_rate.sleep();
}
7.配置CMakeLists.txt中的编译规则,将下面的代码插入到CMakeLists.txt文件中:
add_executable(velocity_publisher src/velcity_publisher.cpp)
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
8.编译并运行:
cd ~/catkin_ws
catkin_make
source devel/setup.bash
#新建终端,先开启master
roscore
#新建终端,启动小海龟
rosrun turtlesim turtlesim_node
#回到刚编译成功的第一个终端,运行我们自己编写的发布者程序
rosrun learning_topic velocity_publisher
#另外启动一个终端,输入下面的命令,可以看到当前的Topic列表
rostopic list
启动程序后,小海龟会按照设定好的线速度和角速度移动。由于线速度和角速度都是固定的,所以小海龟的移动轨迹是一个圆,效果如下图所示:
图4 小海龟运动图
通过订阅小海龟的位姿信息来介绍Topic话题中的Subscriber。
1.在learning_topic文件夹中创建pose_subscriber.cpp文件,并构建代码框架:
#include
#include”tyrtlesim/Pose.h”
int main(char argc,char **argv){
return 0;
}
2.在main函数上方编写poseCallback回调函数,打印订阅内容:
void poseCallback(const turtlesim::Pose::ConstPtr& msg){
ROS_INFO(“Turtle pose:x:%0.6f,y:%0.6f”,msg->,msg->y);
}
3.在main函数中初始化ROS节点,在ROS系统中注册节点名称:
ros init(argc,argv,”pose_subscriber”);//初始化ROS节点
4.创建句柄,实例化Node,并创建订阅者对象:
ros::NodeHandle n;
ros::Subscriber pose_sub=n.subscribe(“/turtle1/pose”,10,poseCallback);
上面的代码告诉ROS节点管理器,我们将会从”/turtle1/pose“这个话题中订阅消息,当有消息到达时会自动调用这里指定的poseCallback回调函数。这里的参数10表示订阅队列的大小,如果消息处理的不够快,之前的消息则会被舍弃。
5.循环等待回调函数:
ros::spin();
6.配置CMakeLists.txt中的编译规则,将下面代码插入CMakeLists.txt文件当中:
add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
7.编译并运行:
cd ~/catkin_ws #自己创建的工作区间
catkin_make
source devel/setup.bash
roscore #打开master
rosrun turtlesim turtlesim_node #新开终端
source devel/setup.bash
rosrun learning_topic pose_subscriber #新开终端
【注意】由于ROS应用了C++11标准,需要将CMakeLists.txt文件中的add_complie_options(-std=c++11)注释解开或者添加以上代码。