上文提到,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty.... 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息... std_msgs 由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型。
此处以Person类型为例
1.定义msg文件
功能包下新建 msg 目录,添加文件 Person.msg
string name
uint16 age
float64 height
msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:
- int8, int16, int32, int64 (或者无符号类型: uint*)
- float32, float64
- string
- time, duration
- other msg files
- variable-length array[] and fixed-length array[C]
- 特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头。
2.编辑配置文件
2.1package.xml
添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<!-- exce_depend 以前对应的是 run_depend 现在非法-->
2.2CMakeLists.txt
编辑 msg 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
## 配置 msg 源文件
add_message_files(
FILES
Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
message_generation相当于编译依赖;message_runtime相当于执行依赖。
3.编译
ctrl+shift+b 编译后,生成一个头文件:
C++ 需要调用的中间文件(.../工作空间/devel/include/包名/xxx.h)
后续调用相关 msg 时,是从这个中间文件调用的
4.vscode 配置
为了方便代码提示以及避免误抛异常,需要先配置 vscode,将前面生成的头文件路径配置进 c_cpp_properties.json 的 includepath属性:
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/opt/ros/noetic/include/**",
"/usr/include/**",
"/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 4
}
5.发布方实现
/*
需求: 循环发布人的信息
*/
#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ROS_INFO("这是发布方!");
ros::init(argc,argv,"banzhuren");
ros::NodeHandle nh;
ros::Publisher pub=nh.advertise<plumbing_pub_sub::Person>("xueshengmsg",10);
plumbing_pub_sub::Person person_msg;
person_msg.age=18;
person_msg.height=1.75;
person_msg.name="张三";
ros::Rate r(1);
while(ros::ok())
{
pub.publish(person_msg);
ROS_INFO("发布的消息:%s,%d,%2f",person_msg.name.c_str(),person_msg.age,person_msg.height);
person_msg.age++;
r.sleep();
ros::spinOnce();
}
return 0;
}
6.订阅方实现
/*
需求: 订阅人的信息
*/
#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"
void domsg(const plumbing_pub_sub::Person::ConstPtr &person)
{
ROS_INFO("订阅的信息:%s,%d,%2f",person->name.c_str(),person->age,person->height);
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ROS_INFO("订阅方实现");
ros::init(argc,argv,"jiazhang");
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("xueshengmsg",10,domsg);
ros::spin();
return 0;
}
7.配置 CMakeLists.txt
区别于std_msgs,需要添加add_dependencies
用以设置所依赖的消息相关的中间文件。
add_executable(demo03_pub_person src/demo03_pub_person.cpp)
add_executable(demo04_sub_person src/demo04_sub_person.cpp)
add_dependencies(demo03_pub_person ${PROJECT_NAME}_generate_messages_cpp) //重点重点重点
add_dependencies(demo04_sub_person ${PROJECT_NAME}_generate_messages_cpp) //重点重点重点
target_link_libraries(demo03_pub_person
${catkin_LIBRARIES}
)
target_link_libraries(demo04_sub_person
${catkin_LIBRARIES}
)
8.执行
同一般话题通信类似
参考:AutoLabor