【ROS】利用ROS标准消息std_msgs::String发送结构体

开发背景

在ROS进行结构体消息收发时,经常用书写proto文件,然后将proto字段与结构体字段一一对应,额外需要写一个adaptor转换程序,如果想要发送n个结构体,那么要写n个proto以及n个adaptor。这样很繁琐,能否有一个通用的方法?
下面我将对我在这个过程中碰到的问题,以及自己解决问题的思路做一个梳理,希望能帮到你。
(下面是临时写的,大致框架,具体后面在补充cmake,以及完整的例子)

简短版本

利用std_msgs::String发送结构体(最终版)

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <iostream>
#include <string>
/*
    发布方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建发布者对象;
        5.编写发布逻辑并发布数据v
*/
enum struct GradeClassification: uint8_t{
	PASS = 0,
	GOOD,
	EXCELLENT,
};

struct Junior {
	char name[32];
	int height;
	GradeClassification grade_classification;
};

int main(int argc, char  *argv[]) {
	const int rate_value = 20;
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点;
    ros::init(argc, argv,"string_ros_pub");
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建发布者对象;
    ros::Publisher pub = nh.advertise<std_msgs::String>("/test_string",10);
    // 5.编写发布逻辑并发布数据;
    // (5.1) 先创建ROS标准消息std_msgs::String
    std_msgs::String msg;

    // (5.2) 创建结构体变量
    Junior junior;

    // 以 20HZ 的频率发布数据
    ros::Rate rate(rate_value);
    // 设置编号
    int seq = 0;
    // 休眠以防没注册完已发布数据
    ros::Duration(3).sleep();
    // 编写循环,循环中发布数据
    while(ros::ok())    {      
		// (5.3) 给结构体变量赋值
		junior.height = 178;
		junior.grade_classification=GradeClassification::EXCELLENT;

		// (5.4) 创建 std::string
		std::string s_temp;
		// 这一步非常重要!!!
		s_temp.assign((char*)&junior, sizeof(Junior));
		// (5.5) 将std::string 赋值给ROS标准消息std_msgs::String的data成员变量 
        msg.data = s_temp;
        // (5.6) 将载有结构体变量junior数据的消息std_msgs::String 发送
        pub.publish(msg);
        // ROS_INFO 打印发出的消息编号
        ROS_INFO("[Pub data] talker sent a message ! No. %d", seq);
        ++seq;
        rate.sleep();
        ros::spinOnce();
    }

    return 0;
}
利用std_msgs::String接收结构体(最终版)
#include <iostream>
#include <string>
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"

/*
    订阅方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建订阅方对象;
        5.处理订阅的数据。
        6.spin()函数
*/
enum struct GradeClassification: uint8_t{
	PASS = 0,
	GOOD,
	EXCELLENT,
};

struct Junior {
	char name[32];
	int height;
	GradeClassification grade_classification;
};

// 5.处理订阅的数据。
void doMsg(const std_msgs::String::ConstPtr &msg) {
	// (5.1) 创建结构体Junior的智能指针
	static std::shared_ptr<Junior> junior_msg = std::make_shared<Junior>();
	// (5.2) 将载有结构体变量junior数据的std_msgs::String消息内容拷贝到junior_msg中
	memcpy((char*)junior_msg.get(), (char*)msg->data.c_str(), sizeof(Junior));
    // (5.3) 打印订阅到的消息
    ROS_INFO("[Sub data] junior_msg->height : %d", junior_msg->height);
    ROS_INFO("[Sub data] junior_msg->grade_classification : %d", (uint8_t)junior_msg->grade_classification);
}

int main(int argc, char *argv[]) {
  setlocale(LC_ALL,"");
  // 2.初始化ROS节点;
  ros::init(argc,argv,"string_ros_sub");
  // 3.创建节点句柄;
  ros::NodeHandle nh;
  // 4.创建订阅方对象;
  ros::Subscriber sub = nh.subscribe("/test_string",10,doMsg);

  ros::spin();
  
  return 0;
}

接下来是详细解决过程,碰到的问题,调试过程以及解决思路,不喜勿喷!!!!

解决过程

首先我想着ROS标准消息里有可以发送消息的例子,我找到了 std_msgs::String消息,下面是std_msgs::String 消息收发的Demo

std_msgs::String发送
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
    发布方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建发布者对象;
        5.编写发布逻辑并发布数据v
*/

int main(int argc, char  *argv[]) {
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点;
    ros::init(argc, argv,"string_ros_pub");
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建发布者对象;
    ros::Publisher pub = nh.advertise<std_msgs::String>("/test_string",10);
    // 5.编写发布逻辑并发布数据;
    //要求以 10HZ 的频率发布数据,并且文本后添加编号
    //先创建被发布的消息
    std_msgs::String msg;
    //发布频率
    ros::Rate rate(10);
    //设置编号
    int seq = 0;
    //休眠以防没注册完已发布数据
    ros::Duration(3).sleep();
    //编写循环,循环中发布数据
    while(ros::ok())    {
        count++;
        //msg.data="hello,I'm erGouZi";
        //实现字符串凭借数字
        std::stringstream ss;
        ss << "hello ...>" <<seq;
        msg.data = ss.str();
        pub.publish(msg);
        //添加日志
        ROS_INFO("发布的数据是:%s",ss.str().c_str());
        rate.sleep();
        ros::spinOnce();//官方建议,处理回调函数
    }

    return 0;
}
std_msgs::String接收
#include "ros/ros.h"
#include "std_msgs/String.h"
/*
    订阅方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建订阅方对象;
        5.处理订阅的数据。
        6.spin()函数
*/

void doMsg(const std_msgs::String::ConstPtr &msg) {
  //通过msg获取并操作订阅到的数据
  ROS_INFO("订阅的数据: %s",msg->data.c_str());
}

int main(int argc, char *argv[]) {
  setlocale(LC_ALL,"");
  // 2.初始化ROS节点;
  ros::init(argc,argv,"string_ros_sub");
  // 3.创建节点句柄;
  ros::NodeHandle nh;
  // 4.创建订阅方对象;
  ros::Subscriber sub = nh.subscribe("/test_string",10,doMsg);
  // 5.处理订阅的数据。

  ros::spin();
  
  return 0;
}

从上面可以看到,std_msgs::String 有成员变量 data,在pub程序中

std::stringstream ss;
ss << "hello ...>" <<seq;
msg.data = ss.str();

变量 ss 数据类型是 std::stringstream,ss.str() 返回的数据类型是 string,那么,说明std_msgs::String 的成员变量 data是可以接收数据类型是string类型的数据并进行消息收发。
接下来就行改动,发送一个结构体

利用std_msgs::String发送结构体(测试一)
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
    发布方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建发布者对象;
        5.编写发布逻辑并发布数据v
*/
enum struct GradeClassification: uint8_t{
	PASS = 0,
	GOOD,
	EXCELLENT,
};

struct Junior {
	char name[32];
	int height;
	GradeClassification grade_classification;
};

int main(int argc, char  *argv[]) {
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点;
    ros::init(argc, argv,"string_ros_pub");
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建发布者对象;
    ros::Publisher pub = nh.advertise<std_msgs::String>("/test_string",10);
    // 5.编写发布逻辑并发布数据;
    // 要求以 10HZ 的频率发布数据,并且文本后添加编号
    // 先创建被发布的消息
    std_msgs::String msg;

		// 创建结构体变量
		Junior junior;
		// 给结构体变量赋值
		junior.height = 178;
		junior.grade_classification=GradeClassification::EXCELLENT;
		// 创建 std::string
		std::string s_temp;
		s_temp(junior);


    // 发布频率
    ros::Rate rate(10);
    // 设置编号
    int seq = 0;
    // 休眠以防没注册完已发布数据
    ros::Duration(3).sleep();
    // 编写循环,循环中发布数据
    while(ros::ok())    {
        count++;
        //msg.data="hello,I'm erGouZi";
        // 实现字符串凭借数字
        std::stringstream ss;
        ss << "hello ...>" <<seq;
        msg.data = ss.str();
        pub.publish(msg);
        // 添加日志
        ROS_INFO("发布的数据是:%s",ss.str().c_str());
        rate.sleep();
        ros::spinOnce();//官方建议,处理回调函数
    }

    return 0;
}
利用std_msgs::String接收结构体(测试一)
#include "ros/ros.h"
#include "std_msgs/String.h"
/*
    订阅方实现:
        1.包含头文件;
          ROS中文本类型 ----> std_msgs/String.h
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建订阅方对象;
        5.处理订阅的数据。
        6.spin()函数
*/

void doMsg(const std_msgs::String::ConstPtr &msg) {
  //通过msg获取并操作订阅到的数据
  ROS_INFO("订阅的数据: %s",msg->data.c_str());
}

int main(int argc, char *argv[]) {
  setlocale(LC_ALL,"");
  // 2.初始化ROS节点;
  ros::init(argc,argv,"string_ros_sub");
  // 3.创建节点句柄;
  ros::NodeHandle nh;
  // 4.创建订阅方对象;
  ros::Subscriber sub = nh.subscribe("/test_string",10,doMsg);
  // 5.处理订阅的数据。

  ros::spin();
  
  return 0;
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值