ROS第四课:话题通信

简单话题通信

话题通信用于不断更新的,少逻辑处理的数据传输场景。编写程序时,关注三方面:发布者、订阅者和通信数据。
对于简单的话题通信,即仅一段普通文本,其他步骤与前相同,这里仅整理出代码:

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

*/

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化ROS节点(创建节点)
    ros::init(argc,argv,"erGouZi");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //5.编写发布逻辑并发布数据
    //要求以10HZ的频率发布数据,并且hello文本后要添加编号
    //先创建被发布的信息
    std_msgs::String msg;
    //发布频率
    ros::Rate rate(10);
    //设置编号
    int count = 0;
    //编写循环,循环中发布数据
    while (ros::ok())
    {
        count++;
        //实现字符串拼接
        std::stringstream ss;
        ss <<"hello ---> " << count;
        //msg.data="hello";
        msg.data = ss.str();

        pub.publish(msg);
        //添加日志
        ROS_INFO("发布的数据是:%s",ss.str().c_str());
        rate.sleep();
        ros::spinOnce();//官方建议,处理回调函数
    }
    
    return 0;
}

  • 订阅方
#include "ros/ros.h"
#include "std_msgs/String.h"

/*
订阅方实现:
      1.包含头文件
        ROS中文本类型 ---> std_msgs/String.h
      2.初始化ROS节点
      3.创建节点句柄
      4.创建订阅者对象
      5.处理订阅到的数据
      6.声明spin函数,spin函数会回头反复执行doMsg函数

*/


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, "cuiHua");
      //3.创建节点句柄
      ros::NodeHandle nh;
      //4.创建订阅者对象
      ros::Subscriber sub = nh.subscribe("fang",10,doMsg);
      //5.处理订阅到的数据

      ros::spin();

    return 0;
}

  • TIPS
    ①const删去;
    ②有时,提示ros/ros.h No such file or dirctory…,此时注意检查CMakeList.txt中,find_package中出现的重复,删去内容较少的即可;
    ③订阅方对订阅到的数据进行处理时用到了回调函数,之后需加ros::spin();保证回调函数的正常运行
    ④使用rostopic echo <话题名> 来对没有日志输出的发布方是否正确发布信息进行检测,如本项目使用rostopic echo fang
    ⑤关于订阅者接收到的信息前几条数据丢失,是因为发布方发布数据时,还未完成注册,解决方法是,在信息发布之前,先休息一段时间ros::Duration(3).sleep(); //休眠3s
    ⑥防止中文乱码setlocale(LC_ALL," ");
    ⑦使用rqt_graph查看计算图
    ROS第四课:话题通信

自定义话题通信

ROS中std_msgs中已经封装了一些原生的数据类型,但是由于描述性较差,在多数情况下需要自定义消息,对于自定义消息的话题通信,需要以下三个步骤来完成准备工作:

  1. 按照固定格式创建msg文件
    功能包下新建msg文件,本项目新建person.msg,内容为
string name
int32 age
float32 height
  1. 编辑配置文件
    (1)在package.xml中添加以下依赖:
    <build_depend>message_generation</build_depend> <build_export_depend>std_msgs</build_export_depend>
    (2)在CMakeList.txt文件中进行以下修改:
    ①find package下添加message_generation
    ②add message_files下释放注释,并写入自己的message文件名:person.msg
    ③generatio_message下释放注释;
    ④catkin_package下释放CATKIN_DEPENDS行,并添加message_runtime
  2. 编译生成可被python或C++调用的中间文件
    编译一下即可生成中间文件,其中C++所需中间文件卫devel/include/plumbing_pub_sub下的person.h;python所需的中间文件为devel/lib/python2.7(不同版本,这里数字可能不同)/msg下的_person.py。
    接下来需要对vscode进行配置,这里仅介绍C++的情况:
    需将前边生成的head文件路径配置进c_cpp_properties.json的includePath中,可按系列步骤进行:
    ①将deve/include/plumbing_pub_sub在集成终端中打开(注意右键时,应在plumbing_pub_sub上右击);
    ②使用pwd命令打印路径;
    ③将路径粘贴到.cscode/c_cpp_properties中的includePath路径下(可仿照前几行将include/后改称**,意为包含include文件夹下的所有文件)

之后分别完成发布方和订阅方的实现

  • 发布方
  1. 新建文件
    在功能包下src文件夹中新建文件demo03_pub_person.cpp;
  2. 编辑代码
    键入代码:(见第3点后边)
  3. 配置CMakeList.txt
    ①add_excutable…同前规则
    add_executable(demo03_pub_person src/demo03_pub_person.cpp)
    ②target_link_libraries…同前规则
    target_link_libraries(demo03_pub_person ${catkin_LIBRARIES} )
    ③释放add_dependencies,并修改参数(建议复制出新的一行进行释放):第一个参数修改为映射名,第二个参数为${PROJECT_NAME}_generate_message_cpp(没看懂):
add_dependencies(demo03_pub_person ${PROJECT_NAME}_generate_message_cpp)
#保证编译时,先编译msg文件再编译发布文件,防止出现逻辑错误。
#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"
/*
     发布方:发布人的消息
         1.包含头文件
            #include "plumbing_pub_sub/person.h"
         2.初始化ROS节点
         3.创建节点句柄
         4.创建发布者对象
         5.编写发布逻辑,发布数据



*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ROS_INFO("这是消息的发布方");
    //2.初始化ROS节点
    ros::init(argc,argv,"banZhuRen");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<plumbing_pub_sub::person>("liaoTian",10);
    //5.编写发布逻辑,发布数据
    //5-1.创建被发布的数据
    plumbing_pub_sub::person person;
    person.name = "张三";
    person.age = 1;
    person.height = 1.73;
    //5-2.设置发布频率
    ros::Rate rate(1);
    //5-3.循环发布数据
    while(ros::ok()){

        //修改数据
        ros::Duration(3).sleep();
        //核心:发布数据
        pub.publish(person);
        ROS_INFO("发布的消息:%s,%d,%.2f",person.name.c_str(),person.age,person.height);
        //休眠
        rate.sleep();
        //建议
        person.age += 1;
        ros::spinOnce();
    }
    return 0;
}

  • 订阅方
  1. 新建文件
    略;
  2. 编辑代码
    见第3点后;
  3. 配置CMakeList.txt
    和发布方配置时完全相同,修改以下文件名即可。
#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"
/*
     发布订阅方:订阅消息
         1.包含头文件
            #include "plumbing_pub_sub/person.h"
         2.初始化ROS节点
         3.创建节点句柄
         4.创建订阅者对象
         5.编写回调函数,处理订阅数据
         6.调用spin()函数,spin()函数保证回调函数的正常运行

*/

void doPerson(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("订阅方实现");
        //  2.初始化ROS节点
        ros::init(argc,argv,"jiaZhang");
        //  3.创建节点句柄
        ros::NodeHandle nh;
        //  4.创建订阅者对象
        ros::Subscriber sub = nh.subscribe("liaoTian",10,doPerson);
        //  5.编写回调函数,处理订阅数据
        //  6.调用spin()函数,spin()函数保证回调函数的正常运行
        ros::spin();
    return 0;
}

编译后运行程序,记得先运行roscore。

参考:Autolabor

上一篇:Git检查现有 SSH 密钥


下一篇:从零开始实现简单 RPC 框架 8:网络通信之 Request-Response 模型