自动驾驶中常用的:观察者模式,发布/订阅模式,与回调函数
0 摘要
回调函数适合简单、一对一的快速响应,比如摄像头采集完直接触发日志记录;
观察者模式适用于本地多模块联动,比如激光雷达数据同时供感知、定位和日志模块使用,互不干扰还易扩展;
发布/订阅模式通过中间件解耦,实现异步、跨系统通信,是大型无人车平台,如Apollo CyberRT的核心,适合OTA升级、数据统计等大规模、多目标场景。
这些模式本质上是不同程度的解耦策略,三者各有侧重,小用回调、中用观察者、大用Pub/Sub。
0.1 演进关系
回调函数
↓ 扩展为多订阅者
观察者模式
↓ 引入中间件解耦
发布/订阅模式
0.2 选型建议
- 简单的UI交互 → 回调函数
- 模块间的状态监听 → 观察者模式
- 系统间解耦通信 → 发布/订阅模式
这些模式本质上是不同程度的解耦策略,按需选择即可。
1 概念与特点
| 特性 | 观察者模式(Observer) | 发布/订阅模式(Pub/Sub) | 回调函数(Callback) |
|---|---|---|---|
| 定义 | 被观察对象维护多个观察者(监听者)列表,状态变化时主动通知所有已注册的observer | 发布者发送消息到消息代理/事件总线,中间件转发给所有该主题的订阅者,解耦了发布者和订阅者 | 回调是一种编程技术,将一个可执行对象(如函数指针,或lambda表达式)作为参数传递,在特定事件发生时被调用 |
| 耦合度 | 中(观察者知道主题) | 低(通过中间件解耦):发布者和订阅者互相不知道对方存在 | 高(直接依赖) |
| 通信方式 | 同步调用:需要实现统一的update方法 | 异步:通常通过消息队列或事件总线 | 同步或异步 |
| 关系 | 一对多 | 多对多 | 一对一(通常) |
| 复杂性 | 中等 | 高(需要消息系统) | 低 |
| 使用场景 | GUI事件、数据监听 | 微服务、分布式系统;可以按主题过滤消息;可以跨进程,甚至分布式部署,是现代无人车平台基础架构之一,如Apollo CyberRT,等 | 简单异步通知、事件处理 |
| 伸缩性 | 有限(所有观察者一起通知) | 好(可动态增减订阅者) | 差(硬编码关系) |
| 优点 | 支持多目标联动、便于功能拓展 | 扩展性好,低耦合、支持分布式部署 | 实现简单、效率高 |
| 缺点 | notify过程可能阻塞主线程 | 引入中间件复杂度高、调试难度增加 | 扩展性差、耦合度高 |
2 典型场景
| 场景 | 推荐模式 | 描述 |
|---|---|---|
| 日志记录 | 三种均可 | 每当关键数据流或异常发生时,通过回调/observer/subscriber写入日志 |
| 异常报警 | Callback,或Pub/Sub | 硬件故障实时推送报警信息;安全策略统一响应 |
| 数据统计分析 | Pub/Sub | 后台统计模块汇聚各路指标,为运维团队提供健康报告 |
| OTA升级广播 | Pub/Sub | 新固件上线后批量通知车辆终端依次下载更新 |
| UI界面刷新 | Observer | 路径规划结果产生,即时渲染路线信息 |
| 单设备采集处理 | Callback | 摄像头采集完成,快照并同步输出日志 |
3 UML类图
3.1 回调函数


+------------------+
| SensorDriver |
+------------------+
| + setCallback() -----> (Function Pointer / std::function)
+------------------+
3.2 观察者模式

3.3 发布/订阅 Pub/Sub

EventBUS/Broker为消息总线,可以是Channel/EventBus/MQ,等。
4 C++ Demo
4.1 回调函数 Demo —— 日志记录&异常监控
#include <iostream>
#include <functional>
class CameraDriver {
public:
void setFrameCallback(std::function<void(int)> cb) {
callback_ = cb;
}
void captureFrame(int frameId) {
// 通知外部业务,比如写日志
if (callback_) {
callback_(frameId);
}
}
private:
std::function<void(int)> callback_;
};
void logFrameEvent(int id) {
std::cout << "[LOG] Received camera frame: " << id << "\n";
}
int main() {
CameraDriver driver;
driver.setFrameCallback(logFrameEvent);
driver.captureFrame(101);
}
适用场景:“单设备数据流,一对一响应”,如摄像头采集完成直接打一次快照并同步输出日志。
4.2 观察者模式 Demo —— 多功能协同+日志
#include <iostream>
#include <vector>
#include <memory>
class PointCloudObserver {
public:
virtual void update(const std::string& cloudInfo) = 0;
};
class PointCloudManager {
public:
void attach(std::shared_ptr<PointCloudObserver> obs) {
observers_.push_back(obs);
}
// use remove
void detach(const std::shared_ptr<PointCloudObserver>& obs) {
observers_.erase(std::remove(observers_.begin(), observers_.end(), obs),
observers_.end());
}
// use remove_if
void detach(const std::shared_ptr<PointCloudObserver>& obs) {
observers_.erase(
std::remove_if(observers_.begin(), observers_.end(),
[&] (const std::shared_ptr<PointCloudObserver>& o) {
return o == obs;
}),
observers_.end()
);
}
// notify
void newPointCloudArrived(const std::string& info) {
for(auto &obs : observers_) {
obs->update(info);
}
}
private:
std::vector<std::shared_ptr<PointCloudObserver>> observers_;
};
class PerceptionModule : public PointCloudObserver {
void update(const std::string &info) override {
std::cout << "Perception got pointcloud: " << info << "\n";
}
};
class LocalizationModule : public PointCloudObserver{
void update(const std::string &info) override {
std::cout << "Localization got pointcloud: " << info << "\n";
}
};
class LogModule : public PointCloudObserver {
void update(const std::string &info) override {
// 实际项目应写文件,这里演示输出即可
std::cout << "[LOG] Event received, content: " << info << "\n";
}
};
int main() {
auto manager = std::make_shared<PointCloudManager>();
manager->attach(std::make_shared<PerceptionModule>());
manager->attach(std::make_shared<LocalizationModule>());
manager->attach(std::make_shared<LogModule>());
manager ->newPointCloudArrived("Lidar Frame #42");
}
适用场景:“本地功能协同,多业务并行”,如激光雷达帧既要供感知,又要定位,还要留痕,都只需挂载对应observer而无需改主体代码逻辑,大大提升迭代效率和稳定性。
4.3 发布/订阅 Demo —— 全局异步+多目标+集中Logger服务
#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <string>
#include <mutex>
#include <algorithm>
class EventBus {
public:
using Callback = std::function<void(const std::string&)>;
void subscribe(const std::string& topic, Callback cb) {
std::lock_guard<std::mutex> lock(mtx_);
subs_[topic].push_back(cb);
}
void publish(const std::string& topic, const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx_);
for(auto &cb : subs_[topic]) {
cb(msg);
}
}
private:
std::map<std::string, std::vector<Callback>> subs_;
std::mutex mtx_;
};
void perceptionHandler(const std::string& msg) {
std::cout << "Perception received: " << msg << std::endl;
}
void planningHandler(const std::string& msg) {
std::cout << "Planning received: " << msg << std::endl;
}
// Logger subscriber,可以把所有event落盘或上传远程服务器
void loggerHandler(const std::string& msg) {
// 演示直接打印,也可以改成logToFile(msg)
std::cout << "LOGGER EVENT: " << msg << "\n";
}
int main() {
EventBus bus;
bus.subscribe("pointcloud", perceptionHandler);
bus.subscribe("route", planningHandler);
// 所有pointcloud相关也会被logger捕捉
bus.subscribe("pointcloud", loggerHandler);
bus.subscribe("route", loggerHandler);
bus.publish("pointcloud", "new lidar frame");
bus.publish("route", "updated path info");
}
适用场景:“全局异步、多目标、大规模拓扑”,比如感知节点通过“obstacle”频道广播检测结果,下游任意数量子系统都能随时动态接收,不影响主流程结构,同时方便加入各种辅助服务——包括在线诊断、安全策略、人机交互以及海量运营级别的数据统计分析。
5 小结建议
1 对于简单任务、一对一快速反应,用回调即可;如硬件驱动层错误报警直连Logger接口;
2 本地多模块联动且需要灵活增删监听方,用观察者;例如激光雷达帧同时供多个算法使用又不相互干扰,提高开发效率和可靠性;
3 大规模无人车平台务必采用标准化Event Bus,如ROS Topic/Cyber Channel,以保证横向扩展能力和容错性,同时方便引入各种辅助服务,包括在线诊断、安全策略、人机交互以及大数据统计分析;
4 对于边缘计算需求,还可借助这些机制快速开发OTA升级推送、新版本灰度测试,以及用户行为埋点采集,等。
浙公网安备 33010602011771号