第十三部分:Dreamview Plus模块详解
免责声明
本文档仅供学习和技术研究使用,内容基于 Apollo 开源项目的公开资料和代码分析整理而成。文档中的技术实现细节、架构设计等内容可能随 Apollo 项目更新而变化。使用本文档所述技术方案时,请以 Apollo 官方最新文档为准。
声明事项:
- 本文档不构成任何商业使用建议
- 涉及自动驾驶技术的应用需遵守当地法律法规
- 作者不对因使用本文档内容产生的任何后果承担责任
- Apollo 为百度公司注册商标,本文档为非官方技术解析
1. Dreamview模块概述
1.1 模块定位
Dreamview Plus是Apollo 10.0的新一代可视化人机交互平台,提供了全方位的自动驾驶开发、调试和监控能力。
┌────────────────────────────────────────────────────────────┐
│ Dreamview Plus 平台 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Web Frontend (React + TypeScript) │ │
│ │ ┌─────────┐ ┌────────┐ ┌─────────┐ ┌──────────┐ │ │
│ │ │ 地图 │ │ 感知 │ │ 规划 │ │ HMI │ │ │
│ │ │ 可视化 │ │ 可视化│ │ 可视化 │ │ 控制台 │ │ │
│ │ └─────────┘ └────────┘ └─────────┘ └──────────┘ │ │
│ └─────────────────────┬────────────────────────────────┘ │
│ │ WebSocket │
│ ┌─────────────────────┴────────────────────────────────┐ │
│ │ Backend Server (C++ + CivetWeb) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │ │
│ │ │Socket │ │ Plugin │ │ Updater │ │ │
│ │ │Manager │ │ Manager │ │ Manager │ │ │
│ │ └──────────┘ └──────────┘ └────────────────────┘ │ │
│ └─────────────────────┬────────────────────────────────┘ │
│ │ CyberRT Topics │
└────────────────────────┴────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│Planning │ │Perception│ │ Canbus │
│ Module │ │ Module │ │ Module │
└─────────┘ └──────────┘ └──────────┘
1.2 核心功能
| 功能分类 | 具体功能 | 说明 |
|---|---|---|
| 可视化 | 3D地图渲染 | 高精地图、道路网络、车道线可视化 |
| 车辆状态显示 | 实时位置、速度、加速度、转向角 | |
| 障碍物显示 | 检测到的车辆、行人、障碍物 | |
| 规划轨迹显示 | 规划路径、决策信息、速度曲线 | |
| 感知结果显示 | 相机图像、点云、融合结果 | |
| 控制 | 模块启停控制 | 启动/停止各功能模块 |
| 驾驶模式切换 | 手动/自动驾驶模式切换 | |
| 仿真控制 | 仿真车辆控制、场景重放 | |
| 路由设置 | 起点终点设置、路径规划 | |
| 调试 | 数据录制回放 | 录制bag文件、回放分析 |
| 实时监控 | 模块状态、系统性能监控 | |
| 参数调节 | 在线调整算法参数 | |
| 日志查看 | 实时查看系统日志 | |
| 扩展 | 插件系统 | 自定义插件扩展 |
| 多车管理 | 支持多车辆管理 |
1.3 技术栈
后端:
- C++17: 核心业务逻辑
- CyberRT: Apollo通信框架
- CivetWeb: 轻量级HTTP/WebSocket服务器
- Protocol Buffers: 数据序列化
- nlohmann/json: JSON处理
前端:
- React 18: UI框架
- TypeScript: 类型安全
- Three.js: 3D渲染引擎
- WebSocket: 实时通信
- Ant Design: UI组件库
- Lerna: Monorepo管理
2. 系统架构设计
2.1 整体架构
┌─────────────────────────────────────────────────────────────┐
│ Dreamview Plus │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Frontend Layer (前端层) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ UI │ │ 3D视图 │ │ HMI │ │ │
│ │ │ 组件库 │ │ 渲染器 │ │ 控制台 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ WebSocket Client Manager │ │ │
│ │ │ (多WebSocket连接池管理) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ WebSocket (多通道) │
│ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Communication Layer (通信层) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │/websocket│ │ /map │ │ /plugin │ ... 9个 │ │
│ │ │WebSocket │ │WebSocket │ │WebSocket │ WebSocket │ │
│ │ │ Handler │ │ Handler │ │ Handler │ 通道 │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ CivetWeb Server │ │ │
│ │ │ HTTP静态文件服务 + WebSocket服务 │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Business Logic Layer (业务层) │ │
│ │ ┌──────────────┐ ┌────────────────┐ │ │
│ │ │SocketManager │ │ PluginManager │ │ │
│ │ │(WebSocket │ │(插件管理) │ │ │
│ │ │ 路由管理) │ │ │ │ │
│ │ └──────────────┘ └────────────────┘ │ │
│ │ ┌──────────────┐ ┌────────────────┐ │ │
│ │ │UpdaterManager│ │ HMI Worker │ │ │
│ │ │(更新器管理) │ │(人机交互逻辑) │ │ │
│ │ └──────────────┘ └────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Data Processing Layer (数据层) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │SimulationWorld│ │ MapUpdater │ │ObstacleUpdater│ │ │
│ │ │ Updater │ │ │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └─────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │PointCloud │ │CameraUpdater│ │ChannelsUpdater│ │ │
│ │ │ Updater │ │ │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ CyberRT Integration Layer │ │
│ │ (CyberRT消息订阅与发布) │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│ CyberRT Topics
┌─────────────────┼─────────────────┐
▼ ▼ ▼
/apollo/planning /apollo/perception /apollo/chassis
2.2 9个WebSocket通道
Dreamview Plus使用9个独立的WebSocket通道来实现不同数据的并发传输:
| 通道名称 | URL路径 | 功能 | 数据类型 | 频率 |
|---|---|---|---|---|
| MainWebSocket | /websocket |
主通道,通用消息 | JSON | 按需 |
| MapWebSocket | /map |
地图数据传输 | JSON/Binary | 按需 |
| PointCloudWS | /pointcloud |
点云数据传输 | Binary | 10Hz |
| CameraWS | /camera |
相机图像传输 | JPEG/Binary | 10Hz |
| PluginWS | /plugin |
插件消息传输 | JSON | 按需 |
| SimWorldWS | /simworld |
仿真世界状态 | JSON | 10-30Hz |
| HMI_WS | /hmi |
人机交互控制 | JSON | 按需 |
| SocketManagerWS | /socketmanager |
Socket管理 | JSON | 按需 |
| ObstacleWS | /obstacle |
障碍物信息 | JSON | 10Hz |
| ChannelsInfoWS | /channelsinfo |
通道信息 | JSON | 1Hz |
通道分离的优势:
- 并发传输: 不同类型数据互不阻塞
- 优先级控制: 高优先级数据独立通道
- 带宽优化: 大数据量独立通道(点云、图像)
- 解耦设计: 模块间松耦合
2.3 核心组件关系
cpp
// 文件: modules/dreamview_plus/backend/dreamview.h:54-102
class Dreamview {
public:
~Dreamview();
apollo::common::Status Init();
apollo::common::Status Start();
void Stop();
void RegisterUpdaters();
private:
// ========== Web服务器 ==========
std::unique_ptr<CivetServer> server_; // HTTP/WebSocket服务器
// ========== 9个WebSocket处理器 ==========
std::unique_ptr<WebSocketHandler> websocket_; // 主通道
std::unique_ptr<WebSocketHandler> map_ws_; // 地图
std::unique_ptr<WebSocketHandler> point_cloud_ws_; // 点云
std::unique_ptr<WebSocketHandler> camera_ws_; // 相机
std::unique_ptr<WebSocketHandler> plugin_ws_; // 插件
std::unique_ptr<WebSocketHandler> sim_world_ws_; // 仿真世界
std::unique_ptr<WebSocketHandler> obstacle_ws_; // 障碍物
std::unique_ptr<WebSocketHandler> hmi_ws_; // HMI
std::unique_ptr<WebSocketHandler> socket_manager_ws_;// Socket管理
std::unique_ptr<WebSocketHandler> channels_info_ws_; // 通道信息
// ========== 核心服务 ==========
std::unique_ptr<MapService> map_service_; // 地图服务
std::unique_ptr<HMI> hmi_; // 人机交互
std::unique_ptr<SocketManager> socket_manager_; // Socket管理
std::unique_ptr<DvPluginManager> dv_plugin_manager_; // 插件管理
std::unique_ptr<UpdaterManager> updater_manager_; // 更新器管理
std::unique_ptr<PluginManager> plugin_manager_; // 旧插件管理
// ========== 数据更新器 ==========
std::unique_ptr<SimulationWorldUpdater> sim_world_updater_; // 仿真世界
std::unique_ptr<MapUpdater> map_updater_; // 地图
std::unique_ptr<ObstacleUpdater> obstacle_updater_; // 障碍物
std::unique_ptr<PerceptionCameraUpdater> perception_camera_updater_; // 相机
std::unique_ptr<PointCloudUpdater> point_cloud_updater_; // 点云
std::unique_ptr<ChannelsUpdater> channels_info_updater_; // 通道信息
// ========== HTTP处理器 ==========
std::unique_ptr<ImageHandler> image_; // 图片HTTP接口
std::unique_ptr<ProtoHandler> proto_handler_; // Protobuf HTTP接口
};
3. 后端架构详解
3.1 主入口程序
cpp
// 文件: modules/dreamview_plus/main.cc:27-81
int main(int argc, char* argv[]) {
// 1. 设置工作目录
const char* apollo_runtime_path = std::getenv("APOLLO_RUNTIME_PATH");
if (apollo_runtime_path != nullptr) {
std::filesystem::current_path(apollo_runtime_path);
}
google::ParseCommandLineFlags(&argc, &argv, true);
// 2. 性能分析支持 (可选)
std::signal(SIGTERM, [](int sig) {
apollo::cyber::OnShutdown(sig);
if (FLAGS_dv_cpu_profile) {
ProfilerStop(); // 停止CPU性能分析
}
if (FLAGS_dv_heap_profile) {
HeapProfilerDump("Befor shutdown"); // 转储堆内存分析
HeapProfilerStop();
}
});
// 启动性能分析
if (FLAGS_dv_cpu_profile) {
auto base_name_cpu = std::string(argv[0]) + "_cpu.prof";
ProfilerStart(base_name_cpu.c_str());
}
if (FLAGS_dv_heap_profile) {
auto base_name_heap = std::string(argv[0]) + "_heap.prof";
HeapProfilerStart(base_name_heap.c_str());
}
// 3. 初始化CyberRT
apollo::cyber::GlobalData::Instance()->SetProcessGroup("dreamview_sched");
apollo::cyber::Init(argv[0]);
// 4. 创建并启动Dreamview
apollo::dreamview::Dreamview dreamview;
const bool init_success = dreamview.Init().ok() && dreamview.Start().ok();
if (!init_success) {
AERROR << "Failed to initialize dreamview server";
return -1;
}
// 5. 等待shutdown信号
apollo::cyber::WaitForShutdown();
return 0;
}
启动流程:
- 设置工作目录为
$APOLLO_RUNTIME_PATH - 注册信号处理器(SIGTERM/SIGINT)
- 可选启动性能分析(CPU/Heap)
- 初始化CyberRT框架
- 创建Dreamview实例并初始化
- 启动所有服务
- 等待shutdown信号
3.2 Dreamview::Init() 初始化流程
cpp
// 文件: modules/dreamview_plus/backend/dreamview.cc:57-141
Status Dreamview::Init() {
// 1. 初始化车辆配置
VehicleConfigHelper::Init();
// 2. 配置profiling模式 (可选)
if (FLAGS_dreamview_profiling_mode &&
FLAGS_dreamview_profiling_duration > 0.0) {
exit_timer_.reset(new cyber::Timer(
FLAGS_dreamview_profiling_duration,
[this]() { this->TerminateProfilingMode(); }, false));
exit_timer_->Start();
}
// 3. 初始化HTTP/WebSocket服务器
std::vector<std::string> options = {
"document_root", FLAGS_static_file_dir, // 静态文件目录
"listening_ports", FLAGS_server_ports, // 监听端口(默认8888)
"websocket_timeout_ms", FLAGS_websocket_timeout_ms, // WebSocket超时
"request_timeout_ms", FLAGS_request_timeout_ms, // HTTP超时
"enable_keep_alive", "yes", // 保持连接
"tcp_nodelay", "1", // TCP_NODELAY
"keep_alive_timeout_ms", "500" // Keep-Alive超时
};
// SSL证书 (HTTPS支持)
if (PathExists(FLAGS_ssl_certificate)) {
options.push_back("ssl_certificate");
options.push_back(FLAGS_ssl_certificate);
}
server_.reset(new CivetServer(options));
// 4. 创建9个WebSocket处理器
websocket_.reset(new WebSocketHandler("websocket"));
map_ws_.reset(new WebSocketHandler("Map"));
point_cloud_ws_.reset(new WebSocketHandler("PointCloud"));
camera_ws_.reset(new WebSocketHandler("Camera"));
plugin_ws_.reset(new WebSocketHandler("Plugin"));
hmi_ws_.reset(new WebSocketHandler("HMI"));
sim_world_ws_.reset(new WebSocketHandler("SimWorld"));
obstacle_ws_.reset(new WebSocketHandler("Obstacle"));
channels_info_ws_.reset(new WebSocketHandler("ChannelsInfo"));
// 5. 创建核心服务
map_service_.reset(new MapService()); // 地图服务
image_.reset(new ImageHandler()); // 图片HTTP接口
proto_handler_.reset(new ProtoHandler()); // Protobuf HTTP接口
// 6. 创建数据更新器
perception_camera_updater_.reset(
new PerceptionCameraUpdater(camera_ws_.get())); // 相机更新器
hmi_.reset(new HMI(websocket_.get(), map_service_.get(), hmi_ws_.get()));
plugin_manager_.reset(new PluginManager(plugin_ws_.get()));
sim_world_updater_.reset(new SimulationWorldUpdater(
websocket_.get(), map_ws_.get(), plugin_ws_.get(), map_service_.get(),
plugin_manager_.get(), sim_world_ws_.get(), hmi_.get(),
FLAGS_routing_from_file));
point_cloud_updater_.reset(new PointCloudUpdater(point_cloud_ws_.get()));
map_updater_.reset(new MapUpdater(map_ws_.get(), map_service_.get()));
obstacle_updater_.reset(new ObstacleUpdater(obstacle_ws_.get()));
channels_info_updater_.reset(new ChannelsUpdater(channels_info_ws_.get()));
updater_manager_.reset(new UpdaterManager());
RegisterUpdaters(); // 注册所有更新器
dv_plugin_manager_.reset(
new DvPluginManager(server_.get(), updater_manager_.get()));
// 7. 注册WebSocket路由
server_->addWebSocketHandler("/websocket", *websocket_);
server_->addWebSocketHandler("/map", *map_ws_);
server_->addWebSocketHandler("/pointcloud", *point_cloud_ws_);
server_->addWebSocketHandler("/camera", *camera_ws_);
server_->addWebSocketHandler("/plugin", *plugin_ws_);
server_->addWebSocketHandler("/simworld", *sim_world_ws_);
server_->addWebSocketHandler("/hmi", *hmi_ws_);
server_->addWebSocketHandler("/socketmanager", *socket_manager_ws_);
server_->addWebSocketHandler("/obstacle", *obstacle_ws_);
server_->addWebSocketHandler("/channelsinfo", *channels_info_ws_);
// 8. 注册HTTP路由
server_->addHandler("/image", *image_);
server_->addHandler("/proto", *proto_handler_);
// 9. 初始化插件管理器
dv_plugin_manager_->Init();
// 10. 创建SocketManager
socket_manager_.reset(new SocketManager(
websocket_.get(), updater_manager_.get(), dv_plugin_manager_.get()));
return Status::OK();
}
初始化步骤总结:
- 初始化车辆配置
- 创建CivetWeb HTTP/WebSocket服务器
- 创建9个WebSocket处理器
- 创建核心服务(Map、HMI、Plugin等)
- 创建数据更新器(SimWorld、Map、Obstacle等)
- 注册WebSocket路由
- 注册HTTP路由
- 初始化插件系统
- 创建Socket管理器
3.3 CivetWeb服务器配置
cpp
// CivetWeb服务器选项
std::vector<std::string> options = {
// 静态文件目录 (前端HTML/JS/CSS)
"document_root", "/apollo/modules/dreamview_plus/frontend/dist",
// 监听端口 (默认8888)
"listening_ports", "8888",
// WebSocket超时 (毫秒)
"websocket_timeout_ms", "3600000", // 1小时
// HTTP请求超时 (毫秒)
"request_timeout_ms", "2000",
// 启用HTTP Keep-Alive
"enable_keep_alive", "yes",
// 启用TCP_NODELAY (禁用Nagle算法)
"tcp_nodelay", "1",
// Keep-Alive超时
"keep_alive_timeout_ms", "500"
};
关键配置说明:
- document_root: 静态文件根目录,serve前端build产物
- listening_ports : 支持多端口,格式:
8888,8889s(s表示SSL) - websocket_timeout_ms: WebSocket空闲超时,默认1小时
- tcp_nodelay: 禁用Nagle算法,降低延迟
- SSL证书: 可选HTTPS支持
4. 前端架构详解
4.1 Monorepo结构
Dreamview Plus前端采用Lerna管理的Monorepo结构:
frontend/
├── packages/
│ ├── dreamview/ # 主应用
│ ├── dreamview-core/ # 核心逻辑
│ ├── dreamview-ui/ # UI组件库
│ ├── dreamview-carviz/ # 3D可视化
│ ├── dreamview-theme/ # 主题配置
│ ├── dreamview-lang/ # 国际化
│ ├── dreamview-log/ # 日志系统
│ ├── dreamview-analysis/ # 数据分析
│ ├── dreamview-web/ # Web工具
│ ├── dreamview-debug-extension/ # 调试扩展
│ ├── dreamview-mock/ # Mock数据
│ ├── eslint-config-dreamview/ # ESLint配置
│ ├── tslint-config-dreamview/ # TSLint配置
│ └── platforms/ # 平台适配
├── lerna.json # Lerna配置
├── package.json # 根package.json
└── tsconfig.json # TypeScript配置
Monorepo优势:
- 代码复用: 共享组件、工具、配置
- 统一管理: 统一版本、依赖、构建
- 原子提交: 跨包修改一次提交
- 类型安全: TypeScript跨包类型检查
4.2 核心包说明
| 包名 | 职责 | 主要内容 |
|---|---|---|
| dreamview | 主应用入口 | 应用启动、路由、全局状态 |
| dreamview-core | 核心业务逻辑 | WebSocket管理、数据处理、状态管理 |
| dreamview-ui | UI组件库 | 可复用React组件 |
| dreamview-carviz | 3D可视化引擎 | Three.js封装、地图渲染、车辆渲染 |
| dreamview-theme | 主题系统 | 颜色、字体、尺寸配置 |
| dreamview-lang | 国际化 | 中英文语言包 |
| dreamview-log | 日志系统 | 前端日志收集与上报 |
4.3 技术栈详细说明
核心框架:
json
{
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.0.0"
}
3D渲染:
json
{
"three": "^0.150.0",
"@types/three": "^0.150.0"
}
状态管理:
json
{
"mobx": "^6.9.0",
"mobx-react": "^7.6.0"
}
UI组件:
json
{
"antd": "^5.4.0",
"@ant-design/icons": "^5.0.0"
}
通信:
json
{
"ws": "^8.13.0", // WebSocket客户端
"axios": "^1.4.0" // HTTP客户端
}
5. WebSocket通信机制
5.1 WebSocket处理器基类
cpp
// 文件: modules/dreamview/backend/common/handlers/websocket_handler.h
class WebSocketHandler : public CivetWebSocketHandler {
public:
// 连接类型定义
struct Connection {
std::mutex mutex;
CivetServer* server;
struct mg_connection* connection;
};
explicit WebSocketHandler(const std::string& name);
// ========== CivetWebSocketHandler接口 ==========
// WebSocket连接建立
bool handleConnection(CivetServer* server,
const struct mg_connection* conn) override;
// WebSocket连接就绪
void handleReadyState(CivetServer* server,
struct mg_connection* conn) override;
// 接收WebSocket数据
bool handleData(CivetServer* server,
struct mg_connection* conn,
int bits,
char* data,
size_t data_len) override;
// WebSocket连接关闭
void handleClose(CivetServer* server,
const struct mg_connection* conn) override;
// ========== 消息发送接口 ==========
// 广播消息给所有连接
void BroadcastData(const std::string& data, bool skippable = false);
void BroadcastBinaryData(const std::string& data, bool skippable = false);
// 发送消息给特定连接
void SendData(Connection* conn, const std::string& data,
bool skippable = false);
void SendBinaryData(Connection* conn, const std::string& data,
bool skippable = false);
// ========== 消息处理注册 ==========
// 注册JSON消息处理器
void RegisterMessageHandler(
const std::string& message_type,
std::function<void(const nlohmann::json&, Connection*)> handler);
// 注册二进制消息处理器
void RegisterBinaryMessageHandler(
const std::string& message_type,
std::function<void(const std::string&, Connection*)> handler);
// ========== 连接管理 ==========
size_t GetConnectionCount() const;
bool HasConnection() const;
private:
void ProcessData(struct mg_connection* conn, const std::string& data);
std::string name_; // WebSocket名称
// 连接池
std::vector<std::unique_ptr<Connection>> connections_;
mutable std::mutex mutex_;
// 消息处理器映射
std::unordered_map<std::string,
std::function<void(const nlohmann::json&, Connection*)>>
message_handlers_;
std::unordered_map<std::string,
std::function<void(const std::string&, Connection*)>>
binary_message_handlers_;
};
5.2 WebSocket消息格式
5.2.1 JSON消息格式
json
{
"type": "MessageType", // 消息类型
"data": { // 消息数据
"field1": "value1",
"field2": 123
},
"timestamp": 1234567890.123 // 时间戳 (可选)
}
常见消息类型:
RequestSimulationWorld: 请求仿真世界数据RequestRoutePath: 请求路径规划SendRoutingRequest: 发送路由请求SubscribeChannel: 订阅数据通道UnsubscribeChannel: 取消订阅通道ChangeMode: 切换驾驶模式StartModule: 启动模块StopModule: 停止模块
5.2.2 二进制消息格式
┌────────────────┬─────────────────────────────┐
│ Header (4B) │ Payload (N bytes) │
├────────────────┼─────────────────────────────┤
│ Message Type │ Binary Data (Protobuf) │
│ (uint32) │ or Raw Binary (点云/图像) │
└────────────────┴─────────────────────────────┘
用于传输:
- 点云数据 (PointCloud2)
- 相机图像 (JPEG/PNG)
- Protobuf序列化数据
5.3 消息处理流程
┌──────────────┐
│ Frontend │
│ 发送消息 │
└──────┬───────┘
│ WebSocket Message
▼
┌─────────────────────────────────────────┐
│ WebSocketHandler::handleData() │
│ 1. 接收WebSocket数据 │
│ 2. 解析消息类型 │
└──────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ WebSocketHandler::ProcessData() │
│ 1. JSON解析 │
│ 2. 查找消息处理器 │
└──────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Registered Message Handler │
│ 1. 业务逻辑处理 │
│ 2. 调用CyberRT发布消息 (如需要) │
│ 3. 生成响应消息 │
└──────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ WebSocketHandler::SendData() / │
│ WebSocketHandler::BroadcastData() │
│ 1. 发送响应消息 │
└──────┬──────────────────────────────────┘
│ WebSocket Response
▼
┌──────────────┐
│ Frontend │
│ 接收响应 │
└──────────────┘
5.4 消息处理器注册示例
cpp
// 文件: modules/dreamview_plus/backend/hmi/hmi.cc
void HMI::RegisterMessageHandlers() {
// 注册"ChangeMode"消息处理器
websocket_->RegisterMessageHandler(
"ChangeMode",
[this](const nlohmann::json& json, WebSocketHandler::Connection* conn) {
// 解析消息
std::string mode = json["mode"];
// 处理业务逻辑
if (mode == "auto") {
hmi_worker_->ChangeToAutoDriveMode();
} else if (mode == "manual") {
hmi_worker_->ChangeToManualMode();
}
// 发送响应
nlohmann::json response;
response["type"] = "ChangeModeResponse";
response["success"] = true;
websocket_->SendData(conn, response.dump());
});
// 注册"StartModule"消息处理器
websocket_->RegisterMessageHandler(
"StartModule",
[this](const nlohmann::json& json, WebSocketHandler::Connection* conn) {
std::string module_name = json["module"];
bool success = hmi_worker_->StartModule(module_name);
nlohmann::json response;
response["type"] = "StartModuleResponse";
response["module"] = module_name;
response["success"] = success;
websocket_->SendData(conn, response.dump());
});
}
5.5 前端WebSocket客户端
typescript
// 前端WebSocket管理器伪代码
class WebSocketManager {
private connections: Map<string, WebSocket> = new Map();
// 创建WebSocket连接
connect(url: string, name: string) {
const ws = new WebSocket(url);
ws.onopen = () => {
console.log(`${name} WebSocket connected`);
this.connections.set(name, ws);
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(name, message);
};
ws.onerror = (error) => {
console.error(`${name} WebSocket error:`, error);
};
ws.onclose = () => {
console.log(`${name} WebSocket closed`);
this.connections.delete(name);
// 自动重连
setTimeout(() => this.connect(url, name), 5000);
};
}
// 发送消息
send(name: string, message: any) {
const ws = this.connections.get(name);
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(message));
}
}
// 处理接收到的消息
handleMessage(name: string, message: any) {
// 根据消息类型分发到不同的处理器
switch (message.type) {
case 'SimulationWorld':
this.handleSimulationWorld(message.data);
break;
case 'MapData':
this.handleMapData(message.data);
break;
// ... 其他消息类型
}
}
}
// 初始化所有WebSocket连接
const wsManager = new WebSocketManager();
wsManager.connect('ws://localhost:8888/websocket', 'main');
wsManager.connect('ws://localhost:8888/map', 'map');
wsManager.connect('ws://localhost:8888/simworld', 'simworld');
// ... 其他连接
6. 核心功能模块
6.1 数据更新器架构
所有数据更新器继承自UpdaterBase基类:
cpp
// 文件: modules/dreamview_plus/backend/updater/updater_base.h:38-65
class UpdaterBase {
public:
UpdaterBase();
// ========== 核心接口 ==========
// 启动数据流
// time_interval_ms: 数据流发送频率 (0表示单次订阅)
// channel_name: 通道名称 (可选)
// subscribe_param: 订阅参数 (可选)
virtual void StartStream(const double& time_interval_ms,
const std::string& channel_name = "",
nlohmann::json* subscribe_param = nullptr) = 0;
// 停止数据流
virtual void StopStream(const std::string& channel_name = "") = 0;
// 发布消息到前端
virtual void PublishMessage(const std::string& channel_name = "") = 0;
virtual ~UpdaterBase() {}
};
更新器工作流程:
┌─────────────────┐
│ Frontend │
│ 发送订阅请求 │
└────────┬────────┘
│ WebSocket: {"type": "Subscribe", "channel": "SimWorld"}
▼
┌─────────────────────────────────────────┐
│ SocketManager │
│ 处理订阅请求 │
└────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ UpdaterManager │
│ 查找对应的Updater │
└────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ SimulationWorldUpdater::StartStream() │
│ 1. 创建定时器 (10-30Hz) │
│ 2. 订阅CyberRT话题 │
└────────┬────────────────────────────────┘
│
▼ 定时器触发
┌─────────────────────────────────────────┐
│ SimulationWorldUpdater::OnTimer() │
│ 1. 从CyberRT读取最新数据 │
│ 2. 数据转换和聚合 │
│ 3. 生成JSON │
└────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ SimulationWorldUpdater::PublishMessage()│
│ 通过WebSocket发送给前端 │
└────────┬────────────────────────────────┘
│ WebSocket: SimulationWorld JSON
▼
┌─────────────────┐
│ Frontend │
│ 渲染可视化 │
└─────────────────┘
6.2 SimulationWorldUpdater (仿真世界更新器)
职责: 聚合并推送车辆状态、障碍物、规划轨迹等核心数据
cpp
// 文件: modules/dreamview_plus/backend/simulation_world/simulation_world_updater.h:61-91
class SimulationWorldUpdater : public UpdaterBase {
public:
SimulationWorldUpdater(WebSocketHandler *websocket,
WebSocketHandler *map_ws,
WebSocketHandler *plugin_ws,
const MapService *map_service,
PluginManager *plugin_manager,
WebSocketHandler *sim_world_ws,
HMI *hmi,
bool routing_from_file = false);
// 启动数据流 (默认30Hz)
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
// 定时器回调
void OnTimer(const std::string &channel_name = "");
// 数据流推送间隔 (毫秒)
double time_interval_ms_;
// 上次推送的ADC时间戳
double last_pushed_adc_timestamp_sec_;
// WebSocket处理器
WebSocketHandler *websocket_;
WebSocketHandler *sim_world_ws_;
// 核心服务
std::unique_ptr<SimulationWorldService> sim_world_service_;
const MapService *map_service_;
// 定时器
std::unique_ptr<cyber::Timer> timer_;
};
SimulationWorld数据结构 (JSON格式):
json
{
"timestamp": 1234567890.123,
"sequenceNum": 12345,
"autoDrivingCar": {
"positionX": 587123.45,
"positionY": 4141234.56,
"heading": 1.57,
"speed": 12.5,
"speedAcceleration": 0.5,
"speedJerk": 0.1,
"steeringAngle": -0.2,
"steeringPercentage": 10.5,
"steeringTorque": 2.3,
"throttlePercentage": 30.0,
"brakePercentage": 0.0,
"gearLocation": "GEAR_DRIVE"
},
"object": [
{
"id": "obstacle_001",
"type": "VEHICLE",
"positionX": 587130.0,
"positionY": 4141240.0,
"heading": 1.6,
"length": 4.5,
"width": 1.8,
"height": 1.5,
"speed": 10.0,
"speedHeading": 1.6,
"confidence": 0.95,
"trajectoryPoint": [...]
}
],
"planningTrajectory": [
{
"positionX": 587123.5,
"positionY": 4141234.6,
"heading": 1.57,
"speed": 12.6,
"accelerationS": 0.5,
"relativeTime": 0.1
}
],
"routePath": [...],
"trafficSignal": [...],
"speedLimit": 60.0,
"engageAdvice": "READY_TO_ENGAGE",
"disengageType": "DISENGAGE_NONE"
}
数据聚合流程:
cpp
// 伪代码: SimulationWorldUpdater::OnTimer()
void SimulationWorldUpdater::OnTimer() {
nlohmann::json sim_world_json;
// 1. 获取车辆状态 (订阅 /apollo/chassis)
auto chassis = GetLatestChassis();
sim_world_json["autoDrivingCar"]["speed"] = chassis.speed_mps();
sim_world_json["autoDrivingCar"]["throttlePercentage"] =
chassis.throttle_percentage();
// 2. 获取定位信息 (订阅 /apollo/localization/pose)
auto localization = GetLatestLocalization();
sim_world_json["autoDrivingCar"]["positionX"] =
localization.pose().position().x();
sim_world_json["autoDrivingCar"]["positionY"] =
localization.pose().position().y();
// 3. 获取障碍物 (订阅 /apollo/perception/obstacles)
auto obstacles = GetLatestObstacles();
for (const auto& obstacle : obstacles.perception_obstacle()) {
nlohmann::json obj;
obj["id"] = obstacle.id();
obj["type"] = obstacle.type();
obj["positionX"] = obstacle.position().x();
obj["positionY"] = obstacle.position().y();
sim_world_json["object"].push_back(obj);
}
// 4. 获取规划轨迹 (订阅 /apollo/planning)
auto planning = GetLatestPlanning();
for (const auto& point : planning.trajectory_point()) {
nlohmann::json tp;
tp["positionX"] = point.path_point().x();
tp["positionY"] = point.path_point().y();
tp["speed"] = point.v();
sim_world_json["planningTrajectory"].push_back(tp);
}
// 5. 获取路由路径 (订阅 /apollo/routing_response)
auto routing = GetLatestRouting();
// ... 处理路由数据
// 6. 通过WebSocket发送
PublishMessage();
}
订阅的CyberRT话题:
| 话题名称 | 消息类型 | 频率 | 用途 |
|---|---|---|---|
/apollo/localization/pose |
LocalizationEstimate | 100Hz | 车辆位置、姿态 |
/apollo/chassis |
Chassis | 100Hz | 车辆底盘状态 |
/apollo/perception/obstacles |
PerceptionObstacles | 10Hz | 障碍物信息 |
/apollo/planning |
ADCTrajectory | 10Hz | 规划轨迹 |
/apollo/routing_response |
RoutingResponse | 按需 | 路由路径 |
/apollo/control |
ControlCommand | 10Hz | 控制命令 |
/apollo/prediction |
PredictionObstacles | 10Hz | 预测轨迹 |
/apollo/perception/traffic_light |
TrafficLightDetection | 10Hz | 红绿灯检测 |
6.3 MapUpdater (地图更新器)
职责: 加载和推送高精地图数据
cpp
class MapUpdater : public UpdaterBase {
public:
MapUpdater(WebSocketHandler *map_ws, const MapService *map_service);
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
// 地图元素提取
void ExtractLanes(nlohmann::json &map_json);
void ExtractCrosswalks(nlohmann::json &map_json);
void ExtractJunctions(nlohmann::json &map_json);
void ExtractStopSigns(nlohmann::json &map_json);
void ExtractYieldSigns(nlohmann::json &map_json);
void ExtractSpeedBumps(nlohmann::json &map_json);
WebSocketHandler *map_ws_;
const MapService *map_service_;
};
地图数据格式:
json
{
"type": "MapData",
"lane": [
{
"id": "lane_001",
"centralCurve": [
{"x": 587123.0, "y": 4141234.0, "z": 10.5},
{"x": 587124.0, "y": 4141235.0, "z": 10.5}
],
"leftBoundary": [...],
"rightBoundary": [...],
"speedLimit": 60.0,
"type": "CITY_DRIVING",
"turn": "NO_TURN",
"predecessorId": ["lane_000"],
"successorId": ["lane_002"]
}
],
"crosswalk": [
{
"id": "crosswalk_001",
"polygon": [
{"x": 587120.0, "y": 4141230.0},
{"x": 587125.0, "y": 4141230.0},
{"x": 587125.0, "y": 4141235.0},
{"x": 587120.0, "y": 4141235.0}
]
}
],
"junction": [...],
"stopSign": [...],
"speedBump": [...]
}
6.4 PointCloudUpdater (点云更新器)
职责: 推送激光雷达点云数据
cpp
class PointCloudUpdater : public UpdaterBase {
public:
explicit PointCloudUpdater(WebSocketHandler *point_cloud_ws);
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
// 点云消息回调
void OnPointCloud(const std::shared_ptr<PointCloud>& point_cloud);
// 点云数据压缩
std::string CompressPointCloud(const PointCloud& point_cloud);
WebSocketHandler *point_cloud_ws_;
std::shared_ptr<cyber::Reader<PointCloud>> point_cloud_reader_;
};
点云数据传输:
┌────────────────┐
│ LiDAR Driver │
│ 发布点云数据 │
└────────┬───────┘
│ /apollo/sensor/lidar/compensator/PointCloud2
▼
┌─────────────────────────────────────────┐
│ PointCloudUpdater │
│ 1. 订阅点云话题 │
│ 2. 点云下采样 (减少数据量) │
│ 3. 二进制编码 │
│ 4. 可选压缩 (zlib) │
└────────┬────────────────────────────────┘
│ Binary WebSocket Message
▼
┌─────────────────┐
│ Frontend │
│ Three.js渲染 │
└─────────────────┘
点云二进制格式:
┌───────────────┬──────────────┬────────────────────────────┐
│ Header (12B) │ Points Count │ Point Data (N * 16B) │
├───────────────┼──────────────┼────────────────────────────┤
│ Magic (4B) │ uint32_t │ x (float) + y (float) + │
│ Version (4B) │ │ z (float) + intensity(f) │
│ Flags (4B) │ │ (重复N次) │
└───────────────┴──────────────┴────────────────────────────┘
6.5 CameraUpdater (相机更新器)
职责: 推送相机图像数据
cpp
class PerceptionCameraUpdater : public UpdaterBase {
public:
explicit PerceptionCameraUpdater(WebSocketHandler *camera_ws);
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
// 相机图像回调
void OnCameraImage(const std::shared_ptr<Image>& image);
// JPEG编码
std::string EncodeToJPEG(const Image& image, int quality = 80);
WebSocketHandler *camera_ws_;
// 支持多个相机
std::map<std::string, std::shared_ptr<cyber::Reader<Image>>> camera_readers_;
};
相机图像传输:
┌────────────────┐
│ Camera Driver │
│ 发布图像数据 │
└────────┬───────┘
│ /apollo/sensor/camera/front_6mm/image
▼
┌─────────────────────────────────────────┐
│ PerceptionCameraUpdater │
│ 1. 订阅相机话题 │
│ 2. 图像解码 (如果是压缩格式) │
│ 3. 缩放 (可选, 减少带宽) │
│ 4. JPEG编码 (质量80) │
└────────┬────────────────────────────────┘
│ Binary WebSocket Message
▼
┌─────────────────┐
│ Frontend │
│ Canvas渲染 │
└─────────────────┘
图像数据格式:
┌───────────────┬────────────────────────────┐
│ Header (JSON) │ JPEG Data (Binary) │
├───────────────┼────────────────────────────┤
│ { │ │
│ "channel": │ JPEG Encoded Image │
│ "timestamp": │ │
│ "width": │ │
│ "height": │ │
│ } │ │
└───────────────┴────────────────────────────┘
6.6 ObstacleUpdater (障碍物更新器)
职责: 推送感知障碍物详细信息
cpp
class ObstacleUpdater : public UpdaterBase {
public:
explicit ObstacleUpdater(WebSocketHandler *obstacle_ws);
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
void OnTimer();
WebSocketHandler *obstacle_ws_;
std::unique_ptr<cyber::Timer> timer_;
// 订阅感知障碍物
std::shared_ptr<cyber::Reader<PerceptionObstacles>> perception_obstacles_reader_;
};
障碍物数据格式:
json
{
"type": "ObstacleData",
"timestamp": 1234567890.123,
"obstacles": [
{
"id": 1001,
"type": "VEHICLE",
"position": {"x": 587130.0, "y": 4141240.0, "z": 10.5},
"velocity": {"x": 10.0, "y": 0.5, "z": 0.0},
"acceleration": {"x": 0.5, "y": 0.0, "z": 0.0},
"theta": 1.57,
"length": 4.5,
"width": 1.8,
"height": 1.5,
"confidence": 0.95,
"trackingTime": 2.5,
"polygon": [
{"x": 587128.0, "y": 4141238.5},
{"x": 587132.0, "y": 4141238.5},
{"x": 587132.0, "y": 4141241.5},
{"x": 587128.0, "y": 4141241.5}
],
"predictedTrajectory": [
{
"time": 0.5,
"position": {"x": 587135.0, "y": 4141240.5}
},
{
"time": 1.0,
"position": {"x": 587140.0, "y": 4141241.0}
}
]
}
]
}
6.7 ChannelsUpdater (通道信息更新器)
职责: 推送CyberRT所有通道信息
cpp
class ChannelsUpdater : public UpdaterBase {
public:
explicit ChannelsUpdater(WebSocketHandler *channels_info_ws);
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
private:
void OnTimer();
// 获取所有通道信息
void GetAllChannels(nlohmann::json &channels_json);
WebSocketHandler *channels_info_ws_;
std::unique_ptr<cyber::Timer> timer_;
};
通道信息格式:
json
{
"type": "ChannelsInfo",
"timestamp": 1234567890.123,
"channels": [
{
"name": "/apollo/planning",
"messageType": "apollo.planning.ADCTrajectory",
"readerNum": 3,
"writerNum": 1,
"messageRate": 10.5
},
{
"name": "/apollo/perception/obstacles",
"messageType": "apollo.perception.PerceptionObstacles",
"readerNum": 5,
"writerNum": 1,
"messageRate": 10.2
}
]
}
7. HMI人机交互
7.1 HMI架构
HMI (Human Machine Interface) 模块负责处理所有用户交互操作。
cpp
// 文件: modules/dreamview_plus/backend/hmi/hmi.h:35-75
class HMI : public UpdaterBase {
public:
using DvCallback = std::function<nlohmann::json(
const std::string &function_name, const nlohmann::json ¶m_json)>;
HMI(WebSocketHandler *websocket, MapService *map_service,
WebSocketHandler *hmi_websocket);
void Start(DvCallback callback_api);
void Stop();
void StartStream(const double &time_interval_ms,
const std::string &channel_name = "",
nlohmann::json *subscribe_param = nullptr) override;
void StopStream(const std::string &channel_name = "") override;
void PublishMessage(const std::string &channel_name = "") override;
void OnTimer(const std::string &channel_name = "");
// ========== 状态更新 ==========
bool UpdateDynamicModelToStatus(const std::string &dynamic_model_name);
bool UpdateMapToStatus(const std::string &map_name = "");
bool UpdateRecordToStatus();
bool UpdateVehicleToStatus();
bool UpdateCameraChannelToStatus(const std::string &channel_name);
bool UpdatePointChannelToStatus(const std::string &channel_name);
bool isProcessRunning(const std::string &process_name);
private:
// 发送车辆参数
void SendVehicleParam(WebSocketHandler::Connection *conn = nullptr);
// 注册消息处理器
void RegisterMessageHandlers();
void RegisterDBMessageHandlers();
void RegisterFrontendConfMessageHandlers();
std::unique_ptr<HMIWorker> hmi_worker_;
WebSocketHandler *websocket_;
MapService *map_service_;
WebSocketHandler *hmi_ws_;
double time_interval_ms_;
std::unique_ptr<cyber::Timer> timer_;
};
7.2 HMI消息类型
| 消息类型 | 方向 | 功能 | 示例 |
|---|---|---|---|
| StartModule | 前端→后端 | 启动模块 | {"type":"StartModule","module":"planning"} |
| StopModule | 前端→后端 | 停止模块 | {"type":"StopModule","module":"planning"} |
| ChangeMode | 前端→后端 | 切换驾驶模式 | {"type":"ChangeMode","mode":"auto"} |
| SendRoutingRequest | 前端→后端 | 发送路由请求 | {"type":"SendRoutingRequest","start":{...},"end":{...}} |
| ResetAllMode | 前端→后端 | 重置所有模块 | {"type":"ResetAllMode"} |
| ChangeMap | 前端→后端 | 切换地图 | {"type":"ChangeMap","map":"sunnyvale"} |
| ChangeVehicle | 前端→后端 | 切换车辆 | {"type":"ChangeVehicle","vehicle":"lincoln"} |
| HMIStatus | 后端→前端 | HMI状态推送 | {"type":"HMIStatus","modules":{...},"hardware":{...}} |
| VehicleParam | 后端→前端 | 车辆参数 | {"type":"VehicleParam","brand":"lincoln",...} |
7.3 HMI状态数据结构
json
{
"type": "HMIStatus",
"timestamp": 1234567890.123,
"currentMode": "NAVIGATION",
"currentMap": "sunnyvale",
"currentVehicle": "lincoln_mkz",
"currentRecordFile": "",
"modules": {
"Canbus": "STARTED",
"GPS": "STARTED",
"Localization": "STARTED",
"Perception": "STARTED",
"Prediction": "STARTED",
"Planning": "STARTED",
"Routing": "STARTED",
"Control": "STARTED",
"Transform": "STARTED",
"Guardian": "STARTED"
},
"hardware": {
"GPS": {
"status": "OK",
"message": "GPS signal good"
},
"CAN": {
"status": "OK",
"message": "CAN bus connected"
},
"Camera": {
"status": "OK",
"message": "6 cameras online"
},
"Lidar": {
"status": "WARNING",
"message": "Lidar frequency low"
}
},
"systemStatus": "OK",
"errorMessage": ""
}
7.4 模块启停控制
cpp
// 伪代码: HMI模块启停
void HMI::RegisterMessageHandlers() {
// 注册"StartModule"消息处理器
websocket_->RegisterMessageHandler(
"StartModule",
[this](const nlohmann::json& json, WebSocketHandler::Connection* conn) {
std::string module_name = json["module"];
// 1. 构造启动命令
std::string start_cmd = absl::StrCat(
"cyber_launch start ",
"/apollo/modules/", module_name, "/launch/", module_name, ".launch");
// 2. 执行启动命令
int ret = system(start_cmd.c_str());
// 3. 等待模块启动
std::this_thread::sleep_for(std::chrono::seconds(2));
// 4. 检查模块状态
bool is_running = hmi_worker_->IsModuleRunning(module_name);
// 5. 发送响应
nlohmann::json response;
response["type"] = "StartModuleResponse";
response["module"] = module_name;
response["success"] = is_running;
response["message"] = is_running ? "Module started" : "Failed to start";
websocket_->SendData(conn, response.dump());
// 6. 广播HMI状态更新
BroadcastHMIStatus();
});
// 注册"StopModule"消息处理器
websocket_->RegisterMessageHandler(
"StopModule",
[this](const nlohmann::json& json, WebSocketHandler::Connection* conn) {
std::string module_name = json["module"];
// 1. 构造停止命令
std::string stop_cmd = absl::StrCat(
"cyber_launch stop ",
"/apollo/modules/", module_name, "/launch/", module_name, ".launch");
// 2. 执行停止命令
int ret = system(stop_cmd.c_str());
// 3. 等待模块停止
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// 4. 检查模块状态
bool is_stopped = !hmi_worker_->IsModuleRunning(module_name);
// 5. 发送响应
nlohmann::json response;
response["type"] = "StopModuleResponse";
response["module"] = module_name;
response["success"] = is_stopped;
websocket_->SendData(conn, response.dump());
// 6. 广播HMI状态更新
BroadcastHMIStatus();
});
}
7.5 路由请求处理
cpp
// 伪代码: 路由请求处理
void HMI::RegisterMessageHandlers() {
websocket_->RegisterMessageHandler(
"SendRoutingRequest",
[this](const nlohmann::json& json, WebSocketHandler::Connection* conn) {
// 1. 解析起点和终点
double start_x = json["start"]["x"];
double start_y = json["start"]["y"];
double end_x = json["end"]["x"];
double end_y = json["end"]["y"];
// 2. 构造RoutingRequest消息
apollo::routing::RoutingRequest routing_request;
auto* start_point = routing_request.add_waypoint();
start_point->mutable_pose()->set_x(start_x);
start_point->mutable_pose()->set_y(start_y);
auto* end_point = routing_request.add_waypoint();
end_point->mutable_pose()->set_x(end_x);
end_point->mutable_pose()->set_y(end_y);
// 3. 通过CyberRT发布路由请求
auto routing_request_writer =
node_->CreateWriter<apollo::routing::RoutingRequest>(
"/apollo/routing_request");
routing_request_writer->Write(routing_request);
// 4. 发送响应
nlohmann::json response;
response["type"] = "SendRoutingRequestResponse";
response["success"] = true;
response["message"] = "Routing request sent";
websocket_->SendData(conn, response.dump());
AINFO << "Routing request sent: (" << start_x << "," << start_y
<< ") -> (" << end_x << "," << end_y << ")";
});
}
8. 插件系统
8.1 插件架构
Dreamview Plus支持动态插件系统,允许扩展功能而无需修改核心代码。
cpp
// 文件: modules/dreamview_plus/backend/dv_plugin/dv_plugin_manager.h
class DvPluginManager {
public:
DvPluginManager(CivetServer* server, UpdaterManager* updater_manager);
// 初始化插件管理器
void Init();
// 加载插件
bool LoadPlugin(const std::string& plugin_path);
// 卸载插件
bool UnloadPlugin(const std::string& plugin_name);
// 获取插件列表
std::vector<std::string> GetPluginList() const;
private:
// 扫描插件目录
void ScanPluginDirectory();
// 插件配置
std::map<std::string, PluginConfig> plugin_configs_;
// 已加载的插件
std::map<std::string, std::unique_ptr<DvPluginBase>> loaded_plugins_;
CivetServer* server_;
UpdaterManager* updater_manager_;
};
8.2 插件基类
cpp
// 文件: modules/dreamview_plus/backend/dv_plugin/dv_plugin_base.h
class DvPluginBase {
public:
DvPluginBase() = default;
virtual ~DvPluginBase() = default;
// ========== 生命周期 ==========
// 初始化插件
virtual bool Init(CivetServer* server,
UpdaterManager* updater_manager) = 0;
// 启动插件
virtual bool Start() = 0;
// 停止插件
virtual void Stop() = 0;
// ========== 插件信息 ==========
// 获取插件名称
virtual std::string GetName() const = 0;
// 获取插件版本
virtual std::string GetVersion() const = 0;
// 获取插件描述
virtual std::string GetDescription() const = 0;
// ========== 消息处理 ==========
// 处理WebSocket消息
virtual bool HandleMessage(const std::string& message_type,
const nlohmann::json& message_data) = 0;
// 处理HTTP请求
virtual bool HandleHttpRequest(const std::string& uri,
CivetServer* server,
struct mg_connection* conn) = 0;
};
8.3 插件示例: 数据录制插件
cpp
// 示例插件: 数据录制插件
class RecordPlugin : public DvPluginBase {
public:
RecordPlugin() = default;
~RecordPlugin() override = default;
bool Init(CivetServer* server, UpdaterManager* updater_manager) override {
server_ = server;
updater_manager_ = updater_manager;
// 创建CyberRT节点
node_ = cyber::CreateNode("record_plugin");
// 注册HTTP路由
server_->addHandler("/api/record/start", *this);
server_->addHandler("/api/record/stop", *this);
server_->addHandler("/api/record/list", *this);
return true;
}
bool Start() override {
AINFO << "RecordPlugin started";
return true;
}
void Stop() override {
if (is_recording_) {
StopRecording();
}
AINFO << "RecordPlugin stopped";
}
std::string GetName() const override {
return "RecordPlugin";
}
std::string GetVersion() const override {
return "1.0.0";
}
std::string GetDescription() const override {
return "Data recording plugin for Dreamview";
}
bool HandleMessage(const std::string& message_type,
const nlohmann::json& message_data) override {
if (message_type == "StartRecord") {
return StartRecording(message_data["channels"]);
} else if (message_type == "StopRecord") {
return StopRecording();
}
return false;
}
bool HandleHttpRequest(const std::string& uri,
CivetServer* server,
struct mg_connection* conn) override {
if (uri == "/api/record/start") {
// 处理录制启动请求
nlohmann::json response;
response["success"] = StartRecording({"/apollo/planning", "/apollo/perception/obstacles"});
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n");
mg_printf(conn, "%s", response.dump().c_str());
return true;
} else if (uri == "/api/record/stop") {
// 处理录制停止请求
nlohmann::json response;
response["success"] = StopRecording();
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n");
mg_printf(conn, "%s", response.dump().c_str());
return true;
}
return false;
}
private:
bool StartRecording(const std::vector<std::string>& channels) {
if (is_recording_) {
AWARN << "Already recording";
return false;
}
// 创建录制文件名
auto now = std::time(nullptr);
std::string filename = absl::StrCat(
"/apollo/data/bag/",
std::put_time(std::localtime(&now), "%Y%m%d_%H%M%S"),
".record");
// 启动cyber_recorder
std::string cmd = "cyber_recorder record";
for (const auto& channel : channels) {
cmd += " -c " + channel;
}
cmd += " -o " + filename + " &";
int ret = system(cmd.c_str());
if (ret == 0) {
is_recording_ = true;
current_record_file_ = filename;
AINFO << "Recording started: " << filename;
return true;
}
return false;
}
bool StopRecording() {
if (!is_recording_) {
AWARN << "Not recording";
return false;
}
// 停止cyber_recorder
system("pkill -INT cyber_recorder");
is_recording_ = false;
AINFO << "Recording stopped: " << current_record_file_;
current_record_file_.clear();
return true;
}
CivetServer* server_ = nullptr;
UpdaterManager* updater_manager_ = nullptr;
std::shared_ptr<cyber::Node> node_;
bool is_recording_ = false;
std::string current_record_file_;
};
// 插件导出宏
CYBER_PLUGIN_EXPORT(RecordPlugin)
8.4 插件配置
json
// 文件: modules/dreamview_plus/conf/plugin_config.json
{
"plugins": [
{
"name": "RecordPlugin",
"path": "/apollo/bazel-bin/modules/dreamview_plus/plugins/librecord_plugin.so",
"enabled": true,
"config": {
"default_channels": [
"/apollo/planning",
"/apollo/perception/obstacles",
"/apollo/localization/pose"
],
"max_recording_duration": 3600,
"auto_split_size_mb": 1024
}
},
{
"name": "PerformanceMonitorPlugin",
"path": "/apollo/bazel-bin/modules/dreamview_plus/plugins/libperf_monitor_plugin.so",
"enabled": true,
"config": {
"sample_interval_ms": 1000,
"metrics": ["cpu", "memory", "network"]
}
}
]
}
9. 配置与部署
9.1 Dreamview Plus配置文件
9.1.1 主配置文件
bash
# 文件: modules/dreamview_plus/conf/dreamview.conf
# ========== Web服务器配置 ==========
# 静态文件目录
--static_file_dir=/apollo/modules/dreamview_plus/frontend/dist
# 监听端口
--server_ports=8888
# WebSocket超时 (毫秒)
--websocket_timeout_ms=3600000
# HTTP请求超时 (毫秒)
--request_timeout_ms=2000
# SSL证书 (可选)
--ssl_certificate=
# ========== 数据更新频率 ==========
# SimulationWorld更新频率 (Hz)
--sim_world_update_rate=30
# 地图更新频率 (Hz)
--map_update_rate=1
# 点云更新频率 (Hz)
--pointcloud_update_rate=10
# 相机更新频率 (Hz)
--camera_update_rate=10
# ========== 功能开关 ==========
# 启用点云可视化
--enable_pointcloud=true
# 启用相机可视化
--enable_camera=true
# 启用性能分析
--dv_cpu_profile=false
--dv_heap_profile=false
# Profiling模式 (自动退出)
--dreamview_profiling_mode=false
--dreamview_profiling_duration=60.0
# ========== 路由配置 ==========
# 从文件读取初始路由
--routing_from_file=false
# ========== 地图配置 ==========
# 默认地图
--default_map=sunnyvale
# ========== 车辆配置 ==========
# 默认车辆
--default_vehicle=lincoln_mkz
9.1.2 前端配置
javascript
// 文件: modules/dreamview_plus/frontend/packages/dreamview/src/config.ts
export const DreamviewConfig = {
// WebSocket服务器地址
websocket: {
host: window.location.hostname,
port: 8888,
secure: window.location.protocol === 'https:',
},
// 更新频率
updateInterval: {
simWorld: 33, // 30Hz
map: 1000, // 1Hz
pointcloud: 100, // 10Hz
camera: 100, // 10Hz
obstacle: 100, // 10Hz
hmiStatus: 200, // 5Hz
},
// 3D渲染配置
rendering: {
camera: {
fov: 60,
near: 1,
far: 10000,
height: 3,
},
pointcloud: {
size: 0.05,
colorMode: 'intensity',
},
obstacle: {
showId: true,
showVelocity: true,
showPrediction: true,
},
},
// 地图配置
map: {
center: [0, 0],
zoom: 18,
tileSize: 512,
},
};
9.2 部署流程
9.2.1 Docker部署
bash
# 1. 构建Docker镜像
cd /apollo
bash docker/scripts/dev_start.sh
# 2. 进入Docker容器
bash docker/scripts/dev_into.sh
# 3. 编译Dreamview Plus
bash apollo.sh build_opt dreamview_plus
# 4. 启动Dreamview Plus
cyber_launch start modules/dreamview_plus/launch/dreamview_plus.launch
# 5. 访问Web界面
# 浏览器打开: http://localhost:8888
9.2.2 编译配置
python
# 文件: modules/dreamview_plus/BUILD
load("//tools:apollo_package.bzl", "apollo_package")
load("//tools:cpplint.bzl", "cpplint")
apollo_cc_binary(
name = "dreamview_plus",
srcs = ["main.cc"],
linkopts = [
"-lpthread",
"-lboost_system",
"-lboost_thread",
],
deps = [
"//cyber",
"//modules/common/configs:vehicle_config_helper",
"//modules/dreamview_plus/backend:dreamview_lib",
"@com_github_gflags_gflags//:gflags",
"@com_github_google_glog//:glog",
"@com_google_googletest//:gtest_main",
],
)
# 前端构建
genrule(
name = "frontend",
srcs = glob([
"frontend/**/*",
]),
outs = ["frontend_dist"],
cmd = """
cd modules/dreamview_plus/frontend && \
npm install && \
npm run build && \
cp -r dist $(location frontend_dist)
""",
)
9.2.3 Launch文件
xml
<!-- 文件: modules/dreamview_plus/launch/dreamview_plus.launch -->
<cyber>
<module>
<name>dreamview_plus</name>
<dag_conf>modules/dreamview_plus/dag/dreamview_plus.dag</dag_conf>
<process_name>dreamview_plus</process_name>
</module>
</cyber>
xml
<!-- 文件: modules/dreamview_plus/dag/dreamview_plus.dag -->
<dag>
<module_config module_library="bazel-bin/modules/dreamview_plus/libdreamview_plus_lib.so">
<components>
<component class_name="DreamviewPlus" config="">
<flag_file>modules/dreamview_plus/conf/dreamview.conf</flag_file>
</component>
</components>
</module_config>
</dag>
9.3 性能优化
9.3.1 WebSocket性能优化
cpp
// 1. 消息批处理
class WebSocketBatcher {
public:
void AddMessage(const std::string& message) {
std::lock_guard<std::mutex> lock(mutex_);
batch_.push_back(message);
if (batch_.size() >= batch_size_ || ShouldFlush()) {
Flush();
}
}
private:
void Flush() {
if (batch_.empty()) return;
// 合并多个消息
nlohmann::json batch_json;
batch_json["type"] = "Batch";
batch_json["messages"] = batch_;
// 发送批量消息
websocket_->BroadcastData(batch_json.dump());
batch_.clear();
last_flush_time_ = std::chrono::steady_clock::now();
}
bool ShouldFlush() {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_flush_time_).count();
return elapsed >= flush_interval_ms_;
}
std::vector<std::string> batch_;
size_t batch_size_ = 10;
int flush_interval_ms_ = 50;
std::chrono::steady_clock::time_point last_flush_time_;
std::mutex mutex_;
WebSocketHandler* websocket_;
};
9.3.2 数据压缩
cpp
// 2. 点云数据压缩
#include <zlib.h>
std::string CompressPointCloud(const PointCloud& point_cloud) {
// 1. 序列化点云数据
std::string raw_data;
point_cloud.SerializeToString(&raw_data);
// 2. 计算压缩后大小 (保守估计)
uLongf compressed_size = compressBound(raw_data.size());
std::vector<Bytef> compressed_data(compressed_size);
// 3. zlib压缩
int result = compress2(
compressed_data.data(),
&compressed_size,
reinterpret_cast<const Bytef*>(raw_data.data()),
raw_data.size(),
Z_BEST_SPEED); // 快速压缩
if (result != Z_OK) {
AERROR << "Compression failed";
return raw_data;
}
// 4. 返回压缩后的数据
compressed_data.resize(compressed_size);
return std::string(compressed_data.begin(), compressed_data.end());
}
9.3.3 前端性能优化
typescript
// 3. Three.js渲染优化
class PointCloudRenderer {
private geometry: THREE.BufferGeometry;
private material: THREE.PointsMaterial;
private points: THREE.Points;
updatePointCloud(data: PointCloudData) {
// 使用BufferGeometry避免重新分配内存
const positions = this.geometry.attributes.position.array;
const colors = this.geometry.attributes.color.array;
// 直接更新顶点数据
for (let i = 0; i < data.points.length; i++) {
const point = data.points[i];
positions[i * 3] = point.x;
positions[i * 3 + 1] = point.y;
positions[i * 3 + 2] = point.z;
// 根据强度设置颜色
const intensity = point.intensity / 255.0;
colors[i * 3] = intensity;
colors[i * 3 + 1] = intensity;
colors[i * 3 + 2] = intensity;
}
// 标记需要更新
this.geometry.attributes.position.needsUpdate = true;
this.geometry.attributes.color.needsUpdate = true;
// 更新包围盒
this.geometry.computeBoundingSphere();
}
// 使用LOD (Level of Detail)
setupLOD() {
const lod = new THREE.LOD();
// 高详细度 (< 100m)
const highDetail = this.createPointCloud(this.fullData);
lod.addLevel(highDetail, 0);
// 中详细度 (100m - 500m)
const mediumDetail = this.createPointCloud(this.downsample(this.fullData, 0.5));
lod.addLevel(mediumDetail, 100);
// 低详细度 (> 500m)
const lowDetail = this.createPointCloud(this.downsample(this.fullData, 0.25));
lod.addLevel(lowDetail, 500);
return lod;
}
downsample(data: Point[], ratio: number): Point[] {
const step = Math.floor(1 / ratio);
return data.filter((_, index) => index % step === 0);
}
}
10. 开发与扩展
10.1 添加自定义Updater
cpp
// 1. 创建自定义Updater类
// 文件: modules/dreamview_plus/backend/my_updater/my_updater.h
#pragma once
#include "modules/dreamview_plus/backend/updater/updater_base.h"
#include "modules/dreamview/backend/common/handlers/websocket_handler.h"
namespace apollo {
namespace dreamview {
class MyUpdater : public UpdaterBase {
public:
explicit MyUpdater(WebSocketHandler* websocket);
void StartStream(const double& time_interval_ms,
const std::string& channel_name = "",
nlohmann::json* subscribe_param = nullptr) override;
void StopStream(const std::string& channel_name = "") override;
void PublishMessage(const std::string& channel_name = "") override;
private:
void OnTimer();
WebSocketHandler* websocket_;
std::unique_ptr<cyber::Timer> timer_;
// 订阅的CyberRT话题
std::shared_ptr<cyber::Reader<MyMessage>> my_message_reader_;
};
} // namespace dreamview
} // namespace apollo
cpp
// 文件: modules/dreamview_plus/backend/my_updater/my_updater.cc
#include "modules/dreamview_plus/backend/my_updater/my_updater.h"
namespace apollo {
namespace dreamview {
MyUpdater::MyUpdater(WebSocketHandler* websocket)
: websocket_(websocket) {
// 创建CyberRT Reader
auto node = cyber::CreateNode("my_updater");
my_message_reader_ = node->CreateReader<MyMessage>(
"/apollo/my_channel",
[this](const std::shared_ptr<MyMessage>& message) {
// 处理接收到的消息
this->OnMyMessage(message);
});
}
void MyUpdater::StartStream(const double& time_interval_ms,
const std::string& channel_name,
nlohmann::json* subscribe_param) {
if (timer_) {
AWARN << "MyUpdater already started";
return;
}
// 创建定时器
auto period = static_cast<uint64_t>(time_interval_ms * 1000); // 转换为微秒
timer_.reset(new cyber::Timer(
period,
[this]() { this->OnTimer(); },
false));
timer_->Start();
AINFO << "MyUpdater started with interval: " << time_interval_ms << " ms";
}
void MyUpdater::StopStream(const std::string& channel_name) {
if (timer_) {
timer_->Stop();
timer_.reset();
AINFO << "MyUpdater stopped";
}
}
void MyUpdater::PublishMessage(const std::string& channel_name) {
// 构造JSON数据
nlohmann::json json_data;
json_data["type"] = "MyData";
json_data["timestamp"] = cyber::Time::Now().ToSecond();
// 添加自定义数据
json_data["customField1"] = custom_value1_;
json_data["customField2"] = custom_value2_;
// 通过WebSocket发送
websocket_->BroadcastData(json_data.dump());
}
void MyUpdater::OnTimer() {
// 定时器回调,发布消息
PublishMessage();
}
} // namespace dreamview
} // namespace apollo
cpp
// 2. 在Dreamview中注册Updater
// 文件: modules/dreamview_plus/backend/dreamview.cc
Status Dreamview::Init() {
// ... 其他初始化代码 ...
// 创建自定义Updater
my_updater_.reset(new MyUpdater(websocket_.get()));
// 注册到UpdaterManager
updater_manager_->RegisterUpdater("MyUpdater", my_updater_.get());
return Status::OK();
}
10.2 添加前端组件
typescript
// 1. 创建自定义React组件
// 文件: packages/dreamview/src/components/MyComponent/index.tsx
import React, { useEffect, useState } from 'react';
import { useWebSocket } from '@dreamview/dreamview-core';
interface MyData {
timestamp: number;
customField1: string;
customField2: number;
}
export const MyComponent: React.FC = () => {
const [data, setData] = useState<MyData | null>(null);
const { subscribe, unsubscribe, send } = useWebSocket();
useEffect(() => {
// 订阅自定义数据
const handler = (message: any) => {
if (message.type === 'MyData') {
setData(message as MyData);
}
};
subscribe('MyUpdater', handler);
// 发送订阅请求
send({
type: 'Subscribe',
channel: 'MyUpdater',
interval: 100, // 100ms更新间隔
});
return () => {
// 取消订阅
send({
type: 'Unsubscribe',
channel: 'MyUpdater',
});
unsubscribe('MyUpdater', handler);
};
}, []);
return (
<div className="my-component">
<h2>My Custom Component</h2>
{data ? (
<div>
<p>Timestamp: {data.timestamp}</p>
<p>Field 1: {data.customField1}</p>
<p>Field 2: {data.customField2}</p>
</div>
) : (
<p>Loading...</p>
)}
</div>
);
};
typescript
// 2. 注册到主应用
// 文件: packages/dreamview/src/App.tsx
import { MyComponent } from './components/MyComponent';
function App() {
return (
<DreamviewLayout>
<DreamviewHeader />
<DreamviewContent>
{/* 其他组件 */}
<SimulationWorldPanel />
<MapPanel />
{/* 添加自定义组件 */}
<MyComponent />
</DreamviewContent>
</DreamviewLayout>
);
}
10.3 调试技巧
10.3.1 后端调试
bash
# 1. 启用详细日志
export GLOG_v=4
export GLOG_logtostderr=1
# 2. 使用GDB调试
gdb --args /apollo/bazel-bin/modules/dreamview_plus/dreamview_plus
# 3. 查看WebSocket连接
netstat -an | grep 8888
# 4. 查看CyberRT话题
cyber_channel list
# 5. 监控话题消息
cyber_monitor
# 6. 性能分析
# 启用CPU profiling
./dreamview_plus --dv_cpu_profile=true
# 查看性能报告
pprof --text dreamview_plus_cpu.prof
10.3.2 前端调试
typescript
// 1. 启用调试日志
localStorage.setItem('dreamview_debug', 'true');
// 2. 监控WebSocket消息
const originalSend = WebSocket.prototype.send;
WebSocket.prototype.send = function(data) {
console.log('[WS Send]', data);
return originalSend.call(this, data);
};
// 3. React DevTools
// 安装Chrome扩展: React Developer Tools
// 4. Redux DevTools (如果使用Redux)
// 安装Chrome扩展: Redux DevTools
// 5. 性能分析
// Chrome DevTools -> Performance tab
// 录制渲染性能
// 6. 内存分析
// Chrome DevTools -> Memory tab
// 查找内存泄漏
10.3.3 WebSocket调试工具
javascript
// WebSocket调试脚本
class WebSocketDebugger {
constructor(url) {
this.ws = new WebSocket(url);
this.setupListeners();
}
setupListeners() {
this.ws.onopen = () => {
console.log('[WS] Connected');
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('[WS] Received:', data.type, data);
};
this.ws.onerror = (error) => {
console.error('[WS] Error:', error);
};
this.ws.onclose = () => {
console.log('[WS] Disconnected');
};
}
send(type, data) {
const message = { type, ...data };
console.log('[WS] Sending:', message);
this.ws.send(JSON.stringify(message));
}
// 快捷测试方法
requestSimWorld() {
this.send('RequestSimulationWorld', {});
}
sendRoutingRequest(start, end) {
this.send('SendRoutingRequest', {
start: { x: start[0], y: start[1] },
end: { x: end[0], y: end[1] },
});
}
startModule(module) {
this.send('StartModule', { module });
}
stopModule(module) {
this.send('StopModule', { module });
}
}
// 使用示例
const debugger = new WebSocketDebugger('ws://localhost:8888/websocket');
debugger.requestSimWorld();
debugger.startModule('planning');
参考资料与引用
官方资源
-
Apollo 官方 GitHub 仓库
https://github.com/ApolloAuto/apollo
- Apollo 开源项目主仓库,包含完整源代码
-
Apollo 官方文档
- 官方技术文档和开发指南
-
Apollo 开发者社区
https://apollo.baidu.com/community
- 官方开发者论坛和技术交流平台
技术规范与标准
-
ISO 26262 - 道路车辆功能安全标准
-
ISO 21448 (SOTIF) - 预期功能安全标准
学术论文与技术资源
-
CenterPoint: Center-based 3D Object Detection and Tracking
Yin, T., Zhou, X., & Krähenbühl, P. (2021)
-
BEVFormer: Learning Bird's-Eye-View Representation from Multi-Camera Images
Li, Z., et al. (2022)
-
OpenDRIVE 地图标准
开源工具与库
-
Bazel 构建系统
-
Fast-DDS (eProsima)
https://www.eprosima.com/index.php/products-all/eprosima-fast-dds
-
PROJ 坐标转换库
-
TensorRT 开发指南
-
PCL 点云库文档
-
IPOPT 优化求解器
说明
本文档内容整理自上述官方资料、开源代码以及相关技术文档。所有代码示例和技术细节均基于 Apollo10.0 版本。如需获取最新信息,请访问 Apollo 官方网站和 GitHub 仓库。
版权说明
Apollo® 是百度公司的注册商标。本文档为基于开源项目的非官方技术研究文档,仅供学习参考使用。