在前面两篇文章中《发布者 publisher 的编程实现》、《订阅者 subscriber 的编程实现》学习到了 publisher 与 subscriber 是如何发布和订阅消息的。但不管是 publisher 发布的 Twist 消息还是 subscriber 订阅的海龟位置 post 信息,都是在 ROS 里面已经定义好了的,我们可以直接使用。那我们在自己的开发过程当中这些 ROS 已经定义好的消息没有办法满足我们的需求时,我们就可以自己来定义消息类型。以下就是要完成消息的自定义。
在learning_topic
目录下新建msg
文件夹,用来存放后续的消息类文件。
进入该文件夹下并在该文件夹下打开命令行,使用touch Person.msg
指令新建Person.msg
文件。
打开该文件,输入以下内容并保存:
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
这里写的并不是cpp,也不是python。但他在编译过程中会由ros自动编译成cpp或python。
打开learning_topic
文件夹下的package.xml
文件。
如果你的电脑安装了浏览器,xml文件可能会由浏览器默认打开,不能编辑。可以在这个目录下打开命令行,输入
gedit package.xml
指令打开文件。
在最下方如图所示的地方添加以下语句,并保存:
message_generation</build_depend>
message_runtime</exec_depend>
用于添加编译依赖,这里添加 message_generation
功能包
message_runtime功能包
打开learning_topic
文件夹下的CMakeLists.txt
。
find_package
中添加message_generation
:这一步是为了添加功能包依赖。Declare ROS dynamic reconfigure parameters
的位置,在上方添加如下语句:add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)
第一句话,告诉编译器Person.msg
是我们定义的消息接口
第二句话,告诉编译器在编译Person.msg
文件的时候,需要依赖哪些库/包。这里我们需要依赖std_msgs
,刚刚我们写的如string
、uint8
,就是在std_msgs
中定义的。
catkin specific configuration
(即## Build ##
上方),在图示位置添加以下语句(我这里比较特殊,正上方的注释掉的语句与要添加的语句一模一样,这种情况可以不添加新语句而是直接把那句话的注释解除掉)CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim
回到工作空间的根目录catkin_ws
下,打开命令行,输入指令catkin_make
进行编译。
编译成功后,可以在/learning_topic/devel/include/learning_topic/
路径下,找到刚刚编写的Person.msg
文件已经被编译成为Person.h
文件。
在图示路径下建立两个cpp文件。代码如下。
person_publisher.cpp:
/**
* 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
*/
#include
//!!!!!关键!!!!!
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "person_publisher");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
//!!!!!关键!!!!!
ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
// 设置循环的频率
ros::Rate loop_rate(1);
int count = 0;
while (ros::ok())
{
// 初始化learning_topic::Person类型的消息
learning_topic::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_topic::Person::male;
// 发布消息
person_info_pub.publish(person_msg);
ROS_INFO("Publish Person Info: name:%s age:%d sex:%d",
person_msg.name.c_str(), person_msg.age, person_msg.sex);
// 按照循环频率延时
loop_rate.sleep();
}
return 0;
}
person_subscriber.cpp:
/**
* 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
*/
#include
//!!!!!关键!!!!!
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "person_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
//!!!!!关键!!!!!
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
cpp文件编写好后,再次编写刚刚的CMakeLists.txt
文件,对刚刚编写cpp内容进行配置。
找到## Install ##
位置的上方,添加以下语句,保存。
说明:这里相比之前配置
CMakeLists.txt
,多了add_dependencies
的语句目的是让可执行文件(前两句做的工作)和动态生成的文件产生依赖关系
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
步骤和之前的一模一样。(如果之前修改过.bashrc文件的话就可以把source devel/setup.bash
语句省略掉)
cd ~/catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun learning_topic person_subscriber
rosrun learning_topic person_publisher
可以看到,消息发送者 publisher 和消息接收者 subscriber 已经构建联系。
如果已经修改过环境变量,并且可以找到功能包对应的可执行文件,然而执行
rosrun
命令时也无法自动补全功能包名称,这个时候执行一遍rospack list
命令即可。
ROS Master 是帮助节点的创建和链接的。节点一旦创建和链接成功后,就不再受 ROS Master 影响。
此时,如果在执行命令roscore
的终端按ctrl+c
退出roscore
(即退出ROS Master),会发现 subscriber 和 publisher 还在照常收发数据。
除非是需要再访问 ROS Master 介入的参数时,才需要ROS Master 的参与,比如现在有第三个节点想要与这两个中的任意一个节点沟通的话,没有 ROS Master 的帮助,这个沟通是无法进行的。