博客 / 詳情

返回

自動駕駛中常用的:觀察者模式,發佈/訂閲模式,與回調函數

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升級推送、新版本灰度測試,以及用户行為埋點採集,等。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.