自动驾驶中常用的:观察者模式,发布/订阅模式,与回调函数

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 回调函数

deepseek_mermaid_20251210_7e6ebd

deepseek_mermaid_20251210_e35003

+------------------+
| SensorDriver     |
+------------------+
| + setCallback() -----> (Function Pointer / std::function)
+------------------+

3.2 观察者模式

deepseek_mermaid_20251210_7615f0

3.3 发布/订阅 Pub/Sub

deepseek_mermaid_20251210_1a268d

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升级推送、新版本灰度测试,以及用户行为埋点采集,等。

posted @ 2025-12-13 15:23  _bob  阅读(8)  评论(0)    收藏  举报