ROS之话题的发布与订阅
消息:
msg文件就是一个描述ROS中所使用消息类型的简单文本。它们会被用来生成不同语言的源代码。
注意,在构建的时候,我们只需要"message_generation"。然而,在运行的时候,我们只需要"message_runtime"。
查看package.xml, 确保它包含以下两条语句:
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
在 CMakeLists.txt文件中,利用find_packag函数,增加对message_generation的依赖:
catkin_package( … CATKIN_DEPENDS message_runtime … …)
add_message_files( FILES Num.msg)
generate_messages()
1、创建工作空间:
mkdir -p ~/ROS_Learning_ws/src cd ~/ROS_Learning_ws/src catkin_init_workspace #初始化工作空间 cd ~/ROS_Learning_ws catkin_make #编译工作空间
source devel/setup.bash #将该工作空间加入环境变量
2创建功能包:
创建工作空间后,工作空间可以顺利通过编译,此时工作空间不存在功能包,即还没有功能实现。需要使用catkin_create_pkg创建功能包。
cd ~/ROS_Leraning/src catkin_create_pkg learning_communication std_msgs roscpp rospy
catkin_create_pkg命令行格式:catkin_create_pkg [package_name] [depend1] [depend2] [depend3]。因此上述命令行中的std_msgs和roscpp分别为功能包ROS_Test1的依赖项。
std_msgs:包含了常见的消息类型,表示基本数据类型和其他基本的消息构造。
roscpp:表示该功能包通过c++实现ROS的各种功能。提供了一个客户端库,c++开发者可以调用接口迅速完成主题、服务等相关工作。
rospy:表示该功能包通过python实现ROS的各种功能。提供了一个客户端库,python开发者可以调用接口迅速完成主题、服务等相关工作。
此时执行以下命令可以查看功能包的依赖项。
rospack depends learning_communication #查看功能包依赖项
3、编译功能包:
返回到工作空间根目录:
cd ~/ROS_Learning catkin_make
4、创建发布和订阅节点
在功能包下的src文件下,即learning_communication/src创建发布话题节点文件:talker.cpp 内容如下:
//该例程将发布chatter话题,消息类型String #include <sstream> #include "ros/ros.h" #include "std_msgs/String.h" int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "talker"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); // 设置循环的频率 ros::Rate loop_rate(10); int count = 0; while (ros::ok()) { // 初始化std_msgs::String类型的消息 std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); // 发布消息 ROS_INFO("%s", msg.data.c_str()); chatter_pub.publish(msg); // 循环等待回调函数 ros::spinOnce(); // 按照循环频率延时 loop_rate.sleep(); ++count; } return 0; }
在功能包下的src文件下,即learning_communication/src创建订阅话题节点文件:listener.cpp 内容如下:
//该例程将订阅chatter话题,消息类型String #include "ros/ros.h" #include "std_msgs/String.h" // 接收到订阅的消息后,会进入消息回调函数 void chatterCallback(const std_msgs::String::ConstPtr& msg) { // 将接收到的消息打印出来 ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { // 初始化ROS节点 ros::init(argc, argv, "listener"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); // 循环等待回调函数 ros::spin(); return 0; }
在一个功能包内可以同时发布可创建多个实例化节点句柄,创建多个发布函数,创建多个订阅回调函数。
5、创建CMake文件:
在learning_communication包根目录下找到CMakeLists.txt文件在文件结尾端添加如下:
add_executable(talker src/talker.cpp) #将talker.cpp文件编译成可执行文件talker target_link_libraries(talker ${catkin_LIBRARIES}) #为可执行文件talker添加链接库
add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp) #添加可执行文件的消息依赖
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)
以上内容编辑完成以后,进行节点编译:
回到工作空间根目录进行编译:
cd ~/ROS_Learning catkin_make
编译完成后运行
roscore rosrun learning_communication talker #需开启新终端 rosrun learning_communication listener #需开启新终端
运行结果如下图:
补充:
ros::ok()返回false的条件:
1. SIGINT收到(Ctrl-C)信号
2. 另一个同名节点启动,会先中止之前的同名节点
3. ros::shutdown()被调用
4. 所有的ros::NodeHandles被销毁
spinOnce()与spin()区别:
spinOnce()与spin()是兄弟函数,学名叫做消息回调处理函数。区别如下:
spinOnce()函数调用后,会继续执行后续的代码段。例如talker中,执行该函数后会继续执行loop_rate.sleep()。
spin()调用后,程序不会返回,不会执行后续的代码段。例如listener中,执行该函数后,程序不会返回。