Apollo如何通知/订阅主题topic

2023-10-31

转自:https://blog.csdn.net/u012423865/article/details/80024870

How to advertise and subscribe a topic
导读
众所周知,Apollo是基于ROS开发的,所以其底层也是基于消息的机制进行节点通信的。但是它在ROS的基础上做了一些改动,如下:
P2P——由于原生ROS的消息机制是通过主节点(Master)分发数据来实现的,这样一个强中心化的结构始终存在一个Master意外导致系统奔溃的隐患,为了解决这个问题,Apollo使用了Fast-RTPS,用P2P的方式抛弃了主节点实现通信。
Protobuf——原生支持Google的Protobuf,完美的解决了原来ROS的MD5验证导致消息不能后向兼容的问题。
共享内存——使用共享内存传输,效率更高。
上面简单的介绍了一些Apollo和ROS的渊源,如果想详细了解可以看Apollo-ROS官方详细文档。鉴于与ROS的关系,那么就不得不说如何在Apollo里面利用topic进行通信了。虽然出自ROS,但是Apollo在ROS的基础上进行了大量的封装和改动,所以,较原生的ROS,在advertise/subscribe(通知/订阅)topic有了较大的区别。
本文我将按照操作步骤、代码分析、流程图的步骤简述一下Apollo的topic使用方法,如果你只想知道如何使用,那么看前面的使用方法即可,如果你想刨根问题,那么请耐心的看完,结合上下文一起观看,效果更好。还有,代码的注释不能放过,有一些上下文联系的地方,我可能放在代码注释里面来解释了。
使用方法
订阅
订阅已配置好的Topic

实现回调函数

void ZuoTestSubnode::ImgTestCallback(const sensor_msgs::Image &msg){
    AINFO << "ImgTestCallback";
    //-- do sth
}
1
2
3
4
在InitInternal()内将回调函数添加到对应Topic的回调函数队列,如下:

bool ZuoTestSubnode::InitInternal(){
    AdapterManager::AddImageShortCallback(&ZuoTestSubnode::ImgTestCallback, this);
}
1
2
3
订阅未配置好的Topic

如果用户需要新增Topic,那就需要重新配置,而配置,本质上就是调用subscribe/advertise函数,然后生成空的回调函数队列和消息发布句柄。这些是Apollo对ROS做的封装,主要目的是为了方便使用,以及管理众多的topic,这部分主要是在adapter_manager.h、message_manager.h等实现的。具体代码后面再讲,先看步骤:

在adapter_gflags.cc和adapter_gflags.h里面添加新topic。如下:

//-- adapter_gflags.h

//-- Zuo added on 2018-04-15 for testPublishSubnode
DECLARE_string(zuo_test_topic);
1
2
3
4
//-- adapter_gflags.cc

//-- Zuo added on 2018-04-15 for testPublishSubnode
DEFINE_string(zuo_test_topic, "/apollo/zuo/zuo_test", "Zuo added for testPublishMsg");
1
2
3
4
在adapter_manager.h里面增加对ZuoTest的适配:

 class AdapterManager {
      public:
      ......
   //-- Zuo added on 2018-04-15 for testPublishSubnode
   //-- 这里的ZuoTest可以理解为对topic取了个别名。
     REGISTER_ADAPTER(ZuoTest);
 };
1
2
3
4
5
6
7
在adapter_manager.cc的**Init()**函数的有限状态机内添加一个分支,如下:

 void AdapterManager::Init(const AdapterManagerConfig &configs) {
     ......
     for (const auto &config : configs.config()) {
         switch (config.type()) {
           ......
           case AdapterConfig::ZUO_TEST:
             EnableZuoTest(FLAGS_zuo_test_topic, config);
             break;
             default:
             AERROR << "Unknown adapter config type!";
             break;
         }
     }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在adapter_config.proto里面增加ZUO_TEST标签:(不然上述的有限状态机分支就无法进入)

message AdapterConfig {
  enum MessageType {
      ......
    ZUO_TEST = 45;
  }
1
2
3
4
5
在message_adapters.h里面增加对应消息的别名:

using ZuoTestAdapter = Adapter<sensor_msgs::Image>;
1
剩下的就和上述已配置好的Topic的使用方法相同了,我就不赘述了。

在使用者的adapter.conf文件内添加一个config标签,这个conf文件是用来配置对应的topic。而config中的type,是用来在后文的有限状态机中,进入我们新添的topic分支。这里的adapter.conf是使用者用来配置对topic的subscribe和advertise的。

config {
    type: ZUO_TEST
    mode: PUBLISH_ONLY
    message_history_limit: 5
}
1
2
3
4
5
所谓的使用者,可以理解为某个模块,例如Perception模块里面就有用来配置这个的。这必定是每个模块的第一个操作,先subscribe和advertise之后才能操作topic咯。

Status Perception::Init() {
  AdapterManager::Init(FLAGS_perception_adapter_config_filename);
  ......
  }
1
2
3
4
通知
到这里就肯定都已经配置好topic了,所以都一样了就是调用Publish函数发布数据到topic。
  //-- 新建msg
  PerceptionObstacles obstacles;
  //-- publish msg
  common::adapter::AdapterManager::PublishPerceptionObstacles(obstacles);
  ```
1
2
3
4
5
代码分析
上一篇How_to_add_a_subnode已经讲到了如何添加一个subnode,但是还没有说到如何在这个subnode里面subscribe/publish(订阅/发布)消息,下面将根据上述的操作步骤讲解在Apollo里面为什么要这么用topic。

在说Apollo前,还是带一下ROS的通信机制,也方便我们后续的理解,详情可以了解ROS Wiki

原生的ROS其实是如上图一个消息传递流程,简而言之就是,一个节点发,一个节点收,中间是由Master进行转发,具体操作如下:

//-- Zuo added on 2018-04-13
void chatterCallback(const std_msgs::String::ConstPtr& msg)  
{
    //-- do sth
    printf("msg's data = %s", msg.data);
}
int main(int argc, char **argv)  
{
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    
    //-- 向Topic发布消息
    //-- 这里是告诉Master,我需要在'TopicfullName'上发布一个消息。
    //-- @param_0 Topic名字
    //-- @param_1 消息发布队列大小
    //-- 返回的句柄需要保存,用来调用publish函数
    ros::Publisher Zuo_pub = n.advertise<std_msgs::String>("TopicfullName", 1000);
    std_msgs::String msg;
    msg.data = "Hello World";
    //-- 用户根据返回的句柄调用publish函数,向对应的topic发送消息
    Zuo_pub.publish(msg);
    
    //-- 订阅一个Topic
    //-- 如果`TopicfullName`接收到消息,就会触发这里的回调函数chatterCallback()
    ros::Subscriber sub = n.subscribe("TopicfullName", 1000, chatterCallback);
    ros::spin();//ros::spin()进入自循环,循环获取事件
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
注意:上面的两个函数advertise/subscribe的方式我们可以多留意一下,之后在Apollo的代码讲解里面也会看到类似的方式。这个在Apollo里面都是放在AdapterManager::Init里面的EnableXXX()来调用了,注意看adapter_manager.h的内部实现。

在Apollo里面,为了降低subnode在通信过程中对Master节点的依赖,将上图的机制设计成两个subnode直接类似P2P通信,相当于对原来的网络做了一个去中心化的改变。但是这里我们不用管,对于用户来说,我们的使用方式没变,还是通过调用advertise/subscribe两个接口来进行节点通信。

诚然,在Apollo里面我们可以直接调用advertise/subscribe,但是可以用,不代表应该用。看代码我们会发现,在Apollo底层(adapter_manager.h)的确是调用了advertise/subscribe,但是在其之上还封装了几层,它将对不同Topic的advertise/subscribe使用封装成了对应的带有Topic名字标签的函数,例如:

common::adapter::AdapterManager::PublishPerceptionObstacles(obstacles);
1
上述这种本质上就是ROS里面的publish()函数,但是Apollo对publish()函数封装后,使得每个不同模块的publish都带有各自不同的标签,每一个PublishXXX()只能发送对应类型的Msg,也就是说,原生的ROS下,在大量的发送/订阅的场景下,需要用户管理大量的句柄(这里可以参考上述ROS的简例)。

上面是Publish,下面看看Apollo对subscribe是怎么封装的:
 AdapterManager::AddImageShortCallback(&ZuoTestSubnode::ZuoImgTestCallback,this);
1
Apollo将ROS的回调函数封装成一个函数队列,上面的函数就是往这个函数队列里添加回调函数。那么,这个函数在哪里定义的呢?如果直接搜索,是找不到的。这里Apollo用了一个技巧。AdapterManager::AddImageShortCallback()是在adapter_manager里面用宏展开和##拼接结合的方法而成。通过调用REGISTER_ADAPTER(name)来实现(REGISTER_ADAPTER的调用参考操作步骤里面的适配操作)。可以看到adapter_manager.h:#90开始,定义了三个不同参数的static void Add##name##Callback()函数。如下:
  static void Add##name##Callback(name##Adapter::Callback callback) {          \
    CHECK(instance()->name##_)                                                 \
        << "Initialize adapter before setting callback";                       \
    instance()->name##_->AddCallback(callback);                                \
  }                                                                            \
  template <class T>                                                           \
  static void Add##name##Callback(                                             \
      void (T::*fp)(const name##Adapter::DataType &data), T *obj) {            \
    Add##name##Callback(std::bind(fp, obj, std::placeholders::_1));            \
  }                                                                            \
  template <class T>                                                           \
  static void Add##name##Callback(                                             \
      void (T::*fp)(const name##Adapter::DataType &data)) {                    \
    Add##name##Callback(fp);                                                   \
  }  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我们先不管函数具体作用,但是从上述代码我们可以看到,后两个函数最终也是调用了第一个函数,也就是
  static void Add##name##Callback(name##Adapter::Callback callback) {          \
1
接着跟进去,我们看到这个函数实际上就是调用了:
    instance()->name##_->AddCallback(callback);                                \
1
再跟进,可以看到在adapter.h里:
  /**
   * @brief registers the provided callback function to the adapter,
   * so that the callback function will be called once right after the
   * message hits the adapter.
   * @param callback the callback with signature void(const D &).
   */  
  void AddCallback(Callback callback) {
    receive_callbacks_.push_back(callback);
  }    
1
2
3
4
5
6
7
8
9
​    receive_callbacks_的定义如下:

  /// User defined function when receiving a message
  std::vector<Callback> receive_callbacks_;
1
2
其实到这里,Apollo订阅的封装实现很明显了,其在adapter_manager.h通过宏展开和拼接生成带有不同名字标签的函数群,这些函数群包含一系列topic相关操作的功能,如订阅,压入回调函数队列等等,然后在adapter_manager.cc里面通过有限状态机根据读入的config文件(参考dag_config_path变量)配置需要启动的subnode。这里调用的Enable##name(topicName,config)实现了对不同topic的订阅以及生成通知句柄,然后就可以调用Add##name##Callback来订阅指定的topic,以及调用Publish##name向指定topic发送消息。

那么Apollo这样绕了一圈将advertise/subscribe封装起来,有什么用呢?原因有如下两点:

当系统里面的相似类型多了,变量的管理会变得复杂起来,那么用这种带有名字标签的命名方式,可以让变量名能够做到顾名思义。
本质上,不同的subscribe和publish函数都是一些相同代码,Apollo通过##拼接和宏展开结合的方式,提高了这一块的代码复用率,也方便整个框架后续的延伸扩展。
还有一个要注意,name##Adapter是不同的消息格式,在message_adapters.h定义的别名,这是为了配合整套方案而将不同的消息都别名成了nameAdapter的形式。

using PerceptionObstaclesAdapter = Adapter<perception::PerceptionObstacles>;
1
类似上面的这样取别名,这里的perception::PerceptionObstacles是在perception_obstacle.pb.h的文件内定义的了,这个和protobuf有关,暂且不说,这是下一章How_to_add_a_new_msg的内容。

流程图
下面是整个topic配置的整体流程图,最终呈现在用户面前的是两句Publish##Name()和Add##Name##Callbackc()。

--------------------- 
作者:Zuo丶 
来源:CSDN 
原文:https://blog.csdn.net/u012423865/article/details/80024870 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Apollo如何通知/订阅主题topic 的相关文章

  • ros 中ERROR: cannot download default sources list from: https://raw.githubusercontent.com/ros/rosdist

    ros 中ERROR cannot download default sources list from https raw githubusercontent com ros rosdistro master rosdep sources
  • 1-如何安装ROS

    如何安装ROS 大家好 我是如何 今天尝试在Ubantu下安装ROS Robot Operating System 测试环境 虚拟机VMware Ubantu20 04 准备步骤 添加ROS软件源 sudo sh c echo deb ht
  • GG-CNN代码学习

    文章目录 1 源码网址 https github com dougsm ggcnn 2 数据集格式转化 下载后的康奈尔数据集 解压完之后里面的格式 里面的 tiff图像通过 txt文件转化得到 python m utils dataset
  • Ubuntu16.04安装ROS Kinetic详细步骤

    文章目录 ROS安装 配置Ubuntu软件仓库 设置sources list 设置密钥 更新Debian软件包索引 安装ROS 初始化 rosdep 环境配置 构建工厂依赖 测试安装 开发环境 ROS安装 ROS Kinetic只支持Wil
  • V2X应用场景之协同式自动驾驶

    转自 http zhidx com p 96637 html V2X应用场景之协同式自动驾驶 这个应用场景我觉得是比较典型的 也想多花点时间给大家介绍一下 就是关于V2X在自动驾驶里面很典型的应用 我们管它叫协同式自动驾驶车队 什么意思呢
  • 清华大学开源软件镜像站网址

    清华大学 TUNA 协会原名清华大学学生网管会 注册名清华大学学生网络与开源软件协会 是由清华大学网络技术和开源软件爱好者 技术宅组成的团体 现阶段向校内外提供开源软件镜像等服务 清华大学 TUNA 协会清华大学 TUNA 协会原名清华大学
  • CARLA平台+Q-learning的尝试(gym-carla)

    接触强化学习大概有半年了 也了解了一些算法 一些简单的算法在gym框架也实现了 那么结合仿真平台Carla该怎么用呢 由于比较熟悉gym框架 就偷个懒先从这个开始写代码 项目地址 https github com cjy1992 gym c
  • rosprofiler 安装和使用

    rosprofiler wiki 页面 http wiki ros org rosprofiler rosprofiler package 下载rosprofiler和ros statistics msgs 放到工程目录下编译 https
  • 传感器超声波雷达

    转自 http www itsiwei com 21962 html 在上一次分享中 我介绍了毫米波雷达的原理 数据特性及优缺点 毫米波雷达的低环境敏感和低成本的特性使得其在ADAS和自动驾驶领域得到了广泛的应用 今天要介绍的是一款极其常见
  • ROS 第四天 ROS中的关键组件

    1 Launch文件 通过XML文件实现多节点的配置和启动 可自动启动ROS Master
  • Ubuntu16.04及ROS Kinetic环境下安装使用RealSense SR300

    Ubuntu16 04及ROS Kinetic环境下安装使用RealSense SR300 1 准备条件 需要安装Ubuntu16 04及ROS Kinetic 2 安装驱动 安装realsense的驱动流程可以根据Github上的官方推荐
  • 从 pcl::PointCloud 中删除点

    我是 PCL 新手 我正在使用 PCL 库 并且正在寻找一种从点云中提取点或将特定点复制到新点的方法 我想验证每个点是否符合条件 并且我想获得仅包含优点的点云 谢谢 使用 ExtractIndices 类 将要删除的点添加到 PointIn
  • 将 CUDA 添加到 ROS 包

    我想在 ros 包中使用 cuda 有人给我一个简单的例子吗 我尝试使用 cuda 函数构建一个静态库并将该库添加到我的包中 但总是出现链接错误 未定义的引用 cuda 我已经构建了一个可执行文件而不是库并且它可以工作 请帮忙 我自己找到了
  • 在 Ubuntu 18.10 上安装 ROS Melodic

    I can t是唯一对 Cosmic 与 Wayland 和 Melodic 的组合感兴趣的人 我会坦白说 我似乎已经在 XPS 13 9370 上成功管理了此操作 或者至少安装脚本 最终 成功完成 然而 有一个非常棘手的解决方法 无论结果
  • catkin_make 编译报错 Unable to find either executable ‘empy‘ or Python module ‘em‘...

    文章目录 写在前面 一 问题描述 二 解决方法 参考链接 写在前面 自己的测试环境 Ubuntu20 04 一 问题描述 自己安装完 anaconda 后 再次执行 catkin make 遇到如下问题 CMake Error at opt
  • 可以在catkin工作区之外创建ROS节点吗?

    我想在catkin工作区之外创建一个ROS发布者节点 可以创建吗 当然可以 像对待任何其他 cpp 库或 python 包一样对待 ROS 在Python中你必须保留PYTHONPATH环境变量指向ros包 opt ros kinetic
  • 如何订阅“/scan”主题、修改消息并发布到新主题?

    我想通过订阅message ranges来改进turtlebot3的LDS 01传感器 通过应用一些算法修改messange ranges并将其发布到新主题 如下所示 但是当我运行编码时出现错误 错误是 遇到溢出的情况 错误是 运行时警告
  • 如何使用一个凉亭同时创建两个地图?

    如下图所示 现在我的gazebo正在运行2个slam gmapping包 首先是 turtlebot slam gmapping 发布到 map 主题 第二个是 slam gmapping 发布到与第一个相同的 map 主题 我想创建一个新
  • VideoCapture 未检测到 uEye 摄像头

    我的 uEye 相机遇到了一个问题 使用我的笔记本电脑摄像头 id 0 或 USB 上的网络摄像头 id 1 此行完美运行 TheVideoCapturer open 1 TheVideoCapturer 属于 VideoCapture 类
  • 操作系统和元操作系统有什么区别

    最近听到这个词元操作系统当我学习ros时 你能帮我区分一下吗操作系统 and 元操作系统 ROS 是什么和不是什么最好的解释是这张纸 http www robotics stanford edu ang papers icraoss09 R

随机推荐

  • com.apple.installer.pagecontroller 错误 -1 pkg安装错误

    在网上下载了一个pkg 的安装文件 在mac上安装一打就出现错误 原因是 文件从网上直接下载的 会出权限问题 需要修复安装软件的安装权限 我的原因是 下载的是个rar的mac解压不了 就在线解压 得到了pkg 安装出错 我最后是下载了一个m
  • centos修改文件的最大打开数量

    我们首先先看一下我们现在的限制 root bogon ulimit n 1024 这肯定是不够的 所以我们要把这个数量给变成65535 首先我们一般查询到的方法是这个 ulimit n 65535 但是这个只能在本次开机有效 重启之后就不行
  • MATLAB算法实战应用案例精讲-【大模型】LLM算法(应用篇)

    目录 LLM推理加速 Medusa Medusa Marrying Simplicity with Efficiency 代码实现 参数配置 大模型LLM微调 微调方法 Freeze方法
  • http启动(重启)报错:Job for httpd.service failed because the control process exited with error code.

    http启动 重启 报错 Job for httpd service failed because the control process exited with error code See systemctl status httpd
  • JavaScript 中有趣的 9 个常用编码套路

    set对象 数组快速去重 常规情况下 我们想要筛选唯一值 一般会想到遍历数组然后逐个对比 或者使用成熟的库比如lodash之类的 不过 ES6带来了一个新玩意儿 它引入了一个全新的对象类型 Set 而且 如果结合上 展开运算符 我们可以超级
  • React 阻止默认事件和阻止冒泡

    给 a 标签添加一个事件 阻止默认事件 e preventDefault class view extends Component onAtag e 阻止默认事件 放置跳转 e preventDefault console log 烦烦烦滚
  • 防病毒服务器维护记录表,机房巡检记录表.doc

    机房巡检记录表 机房日常巡检记录表 值班人 值班时间 机房环境 检查项 结论 备注 检查项 结论 备注 温度 正常 异常 湿度 正常 异常 痕迹 正常 异常 清洁 正常 异常 异响 正常 异常 异味 正常 异常 门窗 正常 异常 照明 正常
  • noip模拟赛

    T1 给一个01矩阵 求一个最大子矩阵 矩阵内的和不超过k k leq n 2 n leq 500 sol O n 4 枚举左上角和右下角 发现后两维有单调性 可以用一个滑窗来搞 但其实非常优秀的枚举3个坐标然后二分第四个坐标的 O n 3
  • 黑盒白盒测试的区别

    一 黑盒测试 Black box Testing 黑盒测试也称功能测试 测试中把被测的软件当成一个黑盒子 不关心盒子的内部结构是什么 只关心软件的输入数据与输出数据 不看软件代码 只对功能进行测试 对软件进行操作 观察结果是否和我们想象的一
  • 【Python数据挖掘课程】五.线性回归知识及预测糖尿病实例

    今天主要讲述的内容是关于一元线性回归的知识 Python实现 包括以下内容 1 机器学习常用数据集介绍 2 什么是线性回顾 3 LinearRegression使用方法 4 线性回归判断糖尿病 前文推荐 Python数据挖掘课程 一 安装P
  • Ubuntu 20.04换国内源

    ubuntu默认的源是国处的源 更新下载速度较慢 因此安装好ubuntu20 04之后 将其源更新为国内的源 步骤如下 1 备份原始的源 源的路径 etc apt sources list 备份操作 cd etc apt cp source
  • 【电工技术】期末复习题

    1 电路是为实现人们的某种需求 由 电源 中间环节和负载三部分按一定方式组合起来 使电流流通的整体 2 在使用叠加定理对电路进行分析时 通常要对电源作除源处理 处理方法是将各个理想电压源 短接 将各个理想电流源 开路 3 利用戴维宁定理可以
  • 分析冰蝎三流量特征以及请求包

    1 使用wireshark获取冰蝎流量 首先在冰蝎中执行命令 捕捉流量 流量特征 可以看Accept字段的值 冰蝎脚本的这个请求值比较固定 还可以看user agent这个字段的值 还可以看content的值 2 分析流量 将数据包内容复制
  • 关于有些网站访问不了的问题

    1 网络攻击导致的 先要确定是不是仅仅一个用户的网页打不开 如果是一个用户的网页打不开 非常可能是这个网站的代码有问题 或许是域名没有分析好等原因 如果是大无数用户或全部用户的网页打不开 首要确定是不是攻击以致的 因为攻击有非常多种 例如C
  • 面试了一个00后,绝对能称为是内卷届的天花板

    前言 公司前段缺人 也面了不少测试 结果竟然没有一个合适的 一开始瞄准的就是中级的水准 也没指望来大牛 提供的薪资也不低 面试的人很多 但平均水平很让人失望 令我印象最深的是一个00后测试员 他技术基础方面确实还不错 面试也表现的非常自信
  • rsync服务

    文章目录 rsync简介 rsync特性 rsync的ssh认证协议 rsync命令 rsync简介 rsync是可以实现增量备份的工具 配合任务计划 rsync能实现定时或间隔同步 配合inotify或sersync 可以实现触发式的实时
  • 新一代树莓派 Raspberry Pi 2 性能测试之软件无线电追踪飞机信息

    新一代树莓派 Raspberry Pi 2 性能测试之软件无线电追踪飞机信息 转载自zza1003169 2015年02月28日 于 开源杂志 发表 安装开源软件无线电 GNU Radio 用电视棒追踪飞机轨迹 众所周知 树莓派 Raspb
  • Discuz!教程之当插件、门户或自定义页面设置成首页时手机版访问跳转到forum.php?mobile=yes的问题

    最近由于项目需要 将自定义单页设置为网站首页 测试过程中一直发现手机版无法访问 被强制跳转到了forum php mobile yes页面 仔细查看了一下Discuz 代码 source class discuz discuz applic
  • 免费python课程排行榜-重庆Python培训机构排行榜

    重庆千锋python全栈开发培训 0基础教学 带你玩转python开发 30天直追年薪20万 快速咨询 Python是一种非常强大的计算机语言 你可能已经听说过很多种流行编程语言 比如非常难学的C语言 非常流行的Java语言 适合初学者的B
  • Apollo如何通知/订阅主题topic

    转自 https blog csdn net u012423865 article details 80024870 How to advertise and subscribe a topic 导读 众所周知 Apollo是基于ROS开发