ROS节点订阅与发布
1. 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace #初始化工作空间
cd ~/catkin_ws
catkin_make #编译工作空间
执行catkin_make命令后,catkin文件夹下有三个文件夹:build、devel和src。build文件为编译空间(The Build Space),其中的CMake*和catkin文件为工作包和项目保存缓存信息、配置和其他中间文件;devel文件为开发空间(The Development Space),该文件夹保存编译后的程序,这些程序不需要安装就可以进行测试运行。
source devel/setup.bash #将该工作空间加入环境变量
2. 创建功能包
创建工作空间后,工作空间可以顺利通过编译吧,淡此时工作空间不存在功能包,即还没有功能实现。因此需要使用catkin_create_pkg创建功能包。
cd ~/catkin_ws/src
catkin_create_pkg ROS_Test1 std_msgs roscpp
catkin_create_pkg命令行格式:catkin_create_pkg [package_name] [depend1] [depend2]。因此上述命令行中的std_msgs和roscpp分别为功能包ROS_Test1的依赖项。
- std_mags:包含了常见的消息类型,表示基本数据类型和其他基本的消息构造。
- roscpp:表示该功能包通过c++实现ROS的各种功能。提供了一个客户端库,c++开发者可以调用接口迅速完成主题、服务等相关工作。
- rospack depends ROS_Test1(查看功能包依赖项)
- rospack depends1 ROS_Test1(查看功能包1级依赖项)
3. 编译功能包
cd ~/catkin_ws
catkin_make
4. 创建数据发布节点
roscd ROS_Test1/src
创建文件node_a.cpp,内容如下所示:
#include "ros/ros.h" //包含了使用ROS节点的必要文件
#include "std_msgs/String.h" //包含了使用的数据类型
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "node_a"); //初始化ROS,节点名命名为node_a,节点名必须保持唯一
ros::NodeHandle n; //实例化节点, 节点进程句柄
ros::Publisher pub = n.advertise<std_msgs::String>("str_message", 1000); //告诉系统要发布话题了,话题名为“str_message”,类型为std_msgs::String,缓冲队列为1000。
ros::Rate loop_rate(10); //设置发送数据的频率为10Hz
//ros::ok()返回false会停止运行,进程终止。
while(ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss<<"Hello World";
msg.data = ss.str();
ROS_INFO("node_a is publishing %s", msg.data.c_str());
pub.publish(msg); //向话题“str_message”发布消息
ros::spinOnce(); //不是必须,若程序中订阅话题则必须,否则回掉函数不起作用。
loop_rate.sleep(); //按前面设置的10Hz频率将程序挂起
}
return 0;
}
5. 创建数据订阅节点
roscd ROS_Test1/src
创建文件node_b.cpp,内容如下所示:
#include "ros/ros.h"
#include "std_msgs/String.h"
//话题回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("node_b is receiving [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "node_b"); //初始化ROS,节点命名为node_b,节点名必须唯一。
ros::NodeHandle n; //节点句柄实例化
ros::Subscriber sub = n.subscribe("str_message", 1000, chatterCallback); //向话题“str_message”订阅,一旦发布节点(node_a)在该话题上发布消息,本节点就会调用chatterCallbck函数。
ros::spin(); //程序进入循环,直到ros::ok()返回false,进程结束。
return 0;
}
6. 编译节点
rosed ROS_Test1 CMakeLists.txt(或vim ~/catkin_ws/src/ROS_Test1/CMakeLists.txt)
在文件尾部添加内容如下:
add_executable(Test1_node_a src/node_a.cpp) #将node_a.cpp文件编译成可执行文件Test1_node_a
add_executable(Test1_node_b src/node_b.cpp)
add_dependencies(Test1_node_a ROS_Test1_generate_message_cpp) #添加可执行文件的消息依赖项
add_dependencies(Test1_node_b ROS_Test1_generate_message_cpp)
target_link_libraries(Test1_node_a ${catkin_LIBRARIES}) #为可执行文件Test_node_a添加链接库
target_link_libraries(Test1_node_b ${catkin_LIBRARIES})
以上内容编辑完成以后,进行节点编译:
cd ~/catkin_ws
catkin_make
在终端上运行命令:
roscore
另外开两个终端,分别运行命令行(若无法运行,先执行命令行source devel/setup.bash):
rosrun ROS_Test1 Test1_node_a
rosrun ROS_Test1 Test1_node_b
此时,则可以在终端上输出订阅的信息。
7. 补充
ros::ok()返回false的条件
- SIGINT收到(Ctrl-C)信号
- 另一个同名节点启动,会先中止之前的同名节点
- ros::shutdown()被调用
- 所有的ros::NodeHandles被销毁
spinOnce()与spin()区别
- spinOnce()函数调用后,会继续执行后续的代码段。例如node_a中,执行该函数后会继续执行loop_rate.sleep()。
- spin()调用后,程序不会返回,不会执行后续的代码段。例如node_b中,执行该函数后,程序不会返回。
上一篇: DOM 中文档节点,文本节点之间的关系