【ROS实践学习入门系列(五)自定义节点功能包使用与消息传递】
本内容经官网实例以及一些参考书总结而成,欢迎留言评论交流~
联系方式:ziyuanw#foxmail.com(#换成@)
一、回顾小海龟案例:
在小海龟的案例中我们知道有两个节点的终端窗口,在一个终端窗口中输入键盘消息,另外一个终端窗口接收消息,最后才使得小海龟的图像按照键盘方向移动。
rostopic发布器节点(红色)正在与rostopic 输出海龟动作节点(绿色),通过键盘/turtle1/cmd_vel控制方向的输入消息,进行通信:
那么问题来了我想自己定义两个类似的节点然后让他们完成类似的工作也是可行的,或者说,ROS系统的节点之间通信的基础就是:一个节点发布数据,另外一个节点接收数据,同时操作数据并使用这些数据来完成一些任务和工作。
二、自定义功能包:
1.创建catkin工作空间:
我这里就在用户家目录下直接创建名为catkin_ws的catkin工作空间。(创建功能包的目录可自行选择)
$mkdir -p ~/catkin_ws/src
$cd ~/catkin_ws
$catkin_make
回顾之前博客中的catkin工作空间相关文件说明。ROS的任务都是在自己的工作空间完成的。因此为了保证自己创建的两个节点能够顺利完成任务,我需要自定义一个catkin工作空间来将这两个节点放入。而这两个节点所完成的任务即构成了第二篇博客中所说的功能包。如果在自己的工作空间有多个这种类似任务的功能包,即组成了元功能包。
具有元功能包集合的catkin工作空间目录结构如下:
catkin_workspace_folder/ -- WORKSPACE
src/ -- SOURCE SPACE
CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin
package_1/
CMakeLists.txt -- CMakeLists.txt file for package_1
package.xml -- Package manifest for package_1
...
package_n/
CMakeLists.txt -- CMakeLists.txt file for package_n
package.xml -- Package manifest for package_n
2.创建功能包
有了工作空间还只是第一步基础,在之前博客中所说到的,一个功能包要想完成一个功能,一定要有功能包清单即package.xml文件进行管理,以及CMakeLists.txt文件对功能包中的实现节点功能的源程序进行编译,进而建立其与工作空间的联系才行。
一个catkin功能包必须包含:
- CMakeLists.txt
- package.xml
最简单的程序包看起来就像这样:
my_package/
CMakeLists.txt
package.xml
ROS创建功能包的命令:
catkin_create_pkg [packagename] [dependency1] .... [dependencyN]
第一个参数为自定义功能名称,后面为建立功能包所需要的依赖包。
- (注意,功能包都是在catkin_ws的src目录下创建。)
这里我创建一个名为test的功能包:
catkin_create_pkg test std_msgs roscpp
参数说明:
- catkin_create_pkg 命令创建了一个名为test的功能包。
- std_msgs:包含了创建消息类型,表示基本数据类型和其他基本的消息构建,如多维数组。
- roscpp:使用C++实现ROS的功能,它提供了一个客户端库,能够让开发者使用这些接口快速完成与ROS主题,服务,参数相关开发工作。
最后完成创建可发现在~/catkin_ws/src下面多出了一个test文件夹,我的功能包节点源程序就需要放在其下src目录中。其中文件结构如下:由于其为一个功能包,因此可使用rospack命令对其进行查找路径和查看依赖关系:
创建完功能包记得进入工作空间更新一下环境变量配置,不然会出现找不到功能包。
3.创建节点
本例我创建一个名为example1_a的节点用于发送消息,一个名为example1_b用于接收消息并将消息输出在屏幕。两个节点的源程序分别为example1_a.cpp和example1_b.cpp放在~/catkin_ws/src/test/src目录下:
example1_a.cpp源程序如下:
#include<ros/ros.h> //包含ROS节点所有必要的文件
#include<std_msgs/String.h> //包含要使用的消息类型
#include<sstream> //字符串流输入输出
int main(int argc,char **argv)
{ //初始化节点并设置名称(该名称唯一)
ros::init(argc,argv,"example1_a");
//进程的处理程序,允许我们与环境交互
ros::NodeHandle n;
//将节点实例化为发布者,将发布主题和名称告知节点管理器,名为message,第二个参数为缓冲区大小,若主题发布数据速度较快,缓冲区至少设置为存放1000条消息
ros::Publisher chatter_pub=n.advertise<std_msgs::String>("message",1000);
//发送数据的频率为10HZ
ros::Rate loop_rate(10);
//按下Crtl+C组合或ROS停止所有节点,ros::ok()停止
while(ros::ok())
{ //创建一个消息变量,变量类型必须符合发送数据要求
std_msgs::String msg;
//定义字符串流
std::stringstream ss;
ss<<"I am the example1_a node";
msg.data=ss.str();
//ROS_INFO("%s",msg.data.c_str());
//继续发布消息,使用之前发布器的定义发布消息
chatter_pub.publish(msg);
//spinOnce()函数负责处理所有ROS内部时间和操作,例如读取订阅的消息。
ros::spinOnce();
//按照10HZ频率将程序挂起
loop_rate.sleep();
}
return 0;
}
example1_b.cpp源程序如下:
#include<ros/ros.h> //包含ROS节点所有必要的文件
#include<std_msgs/String.h> //包含要使用的消息类型
//设置回调函数messageCallback,每次该节点收到一条消息时都会调用此函数。该函数可以处理数据。
void messageCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]",msg->data.c_str());
}
int main(int argc, char **argv)
{ //初始化节点并设置名称(名称唯一)
ros::init(argc,argv,"example1_b");
//进程的处理程序,允许我们与环境交互
ros::NodeHandle n;
//将节点实例化为订阅者,并将所发布的主题类型的名称告知节点管理器。名为message。处理消息的函数为messageCallback
ros::Subscriber sub = n.subscribe("message",1000,messageCallback);
//spin()函数负责处理节点开始读取主题和在消息到达时,回调函数messageCallback被调用的主循环。Ctrl+C组合建,节点退出消息循环,于是结束循环
ros::spin();
return 0;
}
4.编译节点
①修改CMakeLists.txt文件:
rosed工作空间内跳转并以文本方式(默认编辑器为:vim) 打开CMakeLists.txt文件,并添加可执行文件,依赖,目标链接库:
在最后一行添加如下内容:
## Build the example1_a and example1_b
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_executable(example1_a src/example1_a.cpp)
add_dependencies(example1_a test_generate_messages_cpp)
target_link_libraries(example1_a ${catkin_LIBRARIES})
add_executable(example1_b src/example1_b.cpp)
add_dependencies(example1_b test_generate_messages_cpp)
target_link_libraries(example1_b ${catkin_LIBRARIES})
②catkin_make编译节点:
$cd ~/catkin_ws
$catkin_make
- (PS:后面参数可选为编译指定功能包,若缺省则默认编译全部功能包的全部节点。)
catkin_make --pkg [packagename]
编译完成可看到输出成功两个可执行节点。
5.运行节点
还是之前博客中所提到的步骤:
在~/catkin_ws工作空间中:
1.打开第一个终端窗口输入:roscore
2.打开第二个终端输入:rosrun test example1_a
3.打开第三个终端输入:rosrun test example1_b
- 此时不要先急于执行,一定要先在终端中更新一下环境配置,不然可能找不到该节点:
效果如下:
6.rqt_graph查看节点消息传递
在上面两个节点传递消息同时,CTRL+Shift+T打开第四个终端:
可以看到节点example1_a发布message主题,节点example1_b订阅该主题。
至此,一个简单的两个节点功能包test已经完成,后面将会用到新的功能用于节点通信:消息和服务。
下一篇:【ROS实践入门(六)消息msg和服务srv文件创建与使用】
参考资料:
【1】ROS官网:http://wiki.ros.org/cn
【2】ROS机器人高效编程