ROS话题通信自定义msg

从21年至今写过不下10个ROS话题通信的工程,今天系统地记录下自定义msg的过程,让后来者少走弯路。

1、自定义msg简介

在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息… std_msgs 由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型。

msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:

  • int8, int16, int32, int64 (或者无符号类型: uint*)
  • float32, float64
  • string
  • time, duration
  • other msg files
  • variable-length array[] and fixed-length array[C]

ROS中还有一种特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头。

2、自定义msg示例

需求:创建自定义消息,该消息包含刚体的6自由度坐标(3D坐标+欧拉角):x, y, z, r, p, y。

操作流程:

  1. 按照固定格式创建 msg 文件
  2. 编辑配置文件
  3. 编译生成可以被 Python 或 C++ 调用的中间文件

2.1、定义msg文件

功能包下新建 msg 目录,添加文件 Pose.msg,因为数据属于浮点型,选择32位的float。

float32 axis_x 
float32 axis_y
float32 axis_z
float32 roll
float32 pitch
float32 yaw

2.2、编辑配置文件

package.xml中添加编译依赖时依赖与执行时依赖

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

在这里插入图片描述

CMakeLists.txt编辑 msg 相关配置

# 需要加入 message_generation,必须有 std_msgs
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
## 配置 msg 源文件
add_message_files(
  FILES
  Pose.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES ros_vo
 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

2.3、编译工程

进入工作空间目录并catkin_make编译

Scanning dependencies of target _ros_vo_generate_messages_check_deps_Pose
[  0%] Built target std_msgs_generate_messages_lisp
[  0%] Built target std_msgs_generate_messages_py
[  0%] Built target std_msgs_generate_messages_eus
[  0%] Built target std_msgs_generate_messages_nodejs
[  0%] Built target std_msgs_generate_messages_cpp
Scanning dependencies of target run_vo
[  0%] Built target _ros_vo_generate_messages_check_deps_Pose
Scanning dependencies of target ros_vo_generate_messages_py
Scanning dependencies of target ros_vo_generate_messages_eus
Scanning dependencies of target ros_vo_generate_messages_cpp
Scanning dependencies of target ros_vo_generate_messages_nodejs
Scanning dependencies of target ros_vo_generate_messages_lisp
[ 11%] Generating Lisp code from ros_vo/Pose.msg
[ 33%] Generating C++ code from ros_vo/Pose.msg
[ 33%] Generating Python from MSG ros_vo/Pose
[ 44%] Generating EusLisp manifest code for ros_vo
[ 55%] Generating EusLisp code from ros_vo/Pose.msg
[ 66%] Generating Javascript code from ros_vo/Pose.msg
[ 66%] Built target ros_vo_generate_messages_lisp
[ 66%] Built target ros_vo_generate_messages_nodejs
[ 77%] Building CXX object ros_vo/CMakeFiles/run_vo.dir/src/run_vo.cpp.o
[ 88%] Generating Python msg __init__.py for ros_vo
[ 88%] Built target ros_vo_generate_messages_cpp
[ 88%] Built target ros_vo_generate_messages_py
[ 88%] Built target ros_vo_generate_messages_eus
Scanning dependencies of target ros_vo_generate_messages
[ 88%] Built target ros_vo_generate_messages
[100%] Linking CXX executable /home/dzh/Demo/VO/devel/lib/ros_vo/run_vo
[100%] Built target run_vo

编译后的中间文件查看:

C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h),后续调用相关 msg 时,是从这些中间文件调用的。

在这里插入图片描述

3、自定义msg调用

需求:

编写发布订阅实现,要求发布方以10HZ(每秒10)的频率发布自定义消息,订阅方订阅自定义消息并将消息内容打印输出。

分析:

在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:

发布方
接收方
数据(此处为自定义消息)

流程:

编写发布方实现;
编写订阅方实现;
编辑配置文件;
编译并执行。

3.1、发布方实现:

// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h" 
#include "geometry_msgs/Point.h"
#include "rtsp/Pose.h"

int main(int argc, char  *argv[])
{
    
       
    //设置编码
    setlocale(LC_ALL,"");

    //2.初始化 ROS 节点:命名(唯一)
    // 参数1和参数2 后期为节点传值会使用
    // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
    ros::init(argc,  argv, "poseTalker");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能

    //4.实例化 发布者 对象
    //泛型: 发布的消息类型
    //参数1: 要发布到的话题名称
    //参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
    ros::Publisher pub = nh.advertise<rtsp::Pose>("pose", 10);

    //5.组织被发布的数据,并编写逻辑发布数据
    // x方向像素偏移、y方向像素偏移、z方向真实距离(6m内有效,否则为0)
    rtsp::Pose pose;
    // 类型为float64
    pose.axis_x  = 1;
    pose.axis_y = 2;
    pose.axis_z = 3;
    pose.roll = 4;
    pose.pitch = 5;
    pose.yaw = 6;

    //逻辑(一秒1次)
    ros::Rate r(10);

    //节点不死
    while (ros::ok())
    {
    
    
        //发布消息
        pub.publish(pose);
        //加入调试,打印发送的消息
        // ROS_INFO("发送的消息:%f %f %f",point.x, point.y, point.z);
        //根据前面制定的发送频率自动休眠 休眠时间 = 1/频率;
        r.sleep();
        //暂无应用
        ros::spinOnce();
    }

    return 0;
}

3.2、订阅方实现:

// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "geometry_msgs/Point.h"
#include "rtsp/Pose.h"

// 处理pose消息
void dealPose(const rtsp::Pose::ConstPtr &msg_pose){
    
    
    ROS_INFO("axis_x: %f, axis_y: %f, axis_z: %f, roll: %f, pitch: %f, yaw: %f", msg_pose.axis_x, msg_pose.axis_y, msg_pose.axis_z, msg_pose.roll, msg_pose.pitch, msg_pose.yaw);
}

int main(int argc, char  *argv[])
{
    
    
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;
    //4.实例化 订阅者 对象,订阅话题为Point
    ros::Subscriber sub = nh.subscribe<rtsp::Pose>("pose", 10, dealPose);
    //5.处理订阅的消息(回调函数)
    //ros::Subscriber sub2 = nh.subscribe<未知结构体类型>("未知话题名称", 10, recvMsg);
    // 6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据,并调用回调函数处理
    return 0;
}

3.3、配置 CMakeLists.txt

add_executable(pose_pub src/pose_pub.cpp)
add_executable(pose_sub src/pose_sub.cpp)

target_link_libraries(pose_pub
  ${
    
    catkin_LIBRARIES}
)
target_link_libraries(pose_sub
  ${
    
    catkin_LIBRARIES}
)

3.4、执行

  1. 启动 roscore;

  2. 启动发布节点;

  3. 启动订阅节点。

在这里插入图片描述

PS:可以在终端中使用 rqt_graph 查看节点关系;使用rostopic list查看话题列表;使用rostopic echo pose订阅话题内容。记住使用前先source ./devel/setup.bash

猜你喜欢

转载自blog.csdn.net/qq_42257666/article/details/131489036
今日推荐