Apollo Canbus模块技术深度解析
本文基于Apollo自动驾驶平台,深度剖析Canbus模块的架构设计、核心组件、通信机制与实现细节。适合自动驾驶工程师、嵌入式开发者和对CAN总线通信感兴趣的技术人员。
目录
- 一、模块概述
- 二、整体架构设计
- 三、目录结构与文件组织
- 四、核心组件详解
- 五、CAN驱动层设计
- 六、通信机制与数据流
- 七、协议解析与编码
- 八、配置系统
- 九、实际应用案例
- 十、总结与最佳实践
一、模块概述
1.1 什么是Canbus模块
Canbus(Controller Area Network Bus)模块是Apollo自动驾驶系统中负责车辆底盘通信的核心模块。它承担着以下关键职责:
- 接收控制指令:从Planning/Control模块接收车辆控制命令(油门、刹车、转向等)
- CAN报文编码:将控制命令编码为符合车型协议的CAN报文
- 硬件通信:通过CAN卡驱动发送报文到车辆底盘
- 状态反馈:接收底盘反馈报文并解析为标准化的Chassis消息
- 安全监控:监控通信健康状态、超时检测、故障处理
1.2 在Apollo架构中的位置
┌─────────────────────────────────────────────────┐
│ Planning & Control │ 规划与控制层
└──────────────────┬──────────────────────────────┘│ ControlCommand▼
┌─────────────────────────────────────────────────┐
│ Canbus Component │ CAN总线模块
│ ┌──────────────┐ ┌────────────────────────┐ │
│ │VehicleControl│ │ Message Manager │ │
│ └──────┬───────┘ └───────┬────────────────┘ │
│ │ │ │
│ ┌──────▼───────┐ ┌───────▼────────────────┐ │
│ │ CanSender │ │ CanReceiver │ │
│ └──────┬───────┘ └───────▲────────────────┘ │
└─────────┼──────────────────┼────────────────────┘│ CAN Frames │ CAN Frames▼ │
┌─────────────────────────────────────────────────┐
│ CAN Driver Layer │ 驱动层
│ (ESD/Hermes/SocketCAN/Fake) │
└──────────────────┬──────────────▲───────────────┘│ │▼ │┌────────────────────────┐│ Vehicle Chassis │ 车辆底盘└────────────────────────┘
二、整体架构设计
2.1 架构层次
Canbus模块采用分层架构设计,自顶向下分为:
-
应用层(
modules/canbus/)- CanbusComponent:主组件,基于CyberRT的TimerComponent
- 处理控制命令、发布底盘状态
-
车型适配层(
modules/canbus_vehicle/)- AbstractVehicleFactory:抽象工厂模式
- VehicleController:车型特定的控制逻辑
- MessageManager:车型特定的消息管理
-
通信层(
modules/drivers/canbus/can_comm/)- CanSender:发送器(独立线程)
- CanReceiver:接收器(独立线程)
- MessageManager:消息管理与解析
- ProtocolData:协议数据基类
-
驱动层(
modules/drivers/canbus/can_client/)- CanClient:驱动接口
- EsdCanClient、HermesCanClient、SocketCanClient等具体实现
2.2 设计模式应用
抽象工厂模式
目的:支持多车型适配,实现车型无关的上层逻辑。
// 抽象工厂基类
class AbstractVehicleFactory {public:virtual bool Init(const CanbusConf *canbus_conf) = 0;virtual bool Start() = 0;virtual void Stop() = 0;virtual void UpdateCommand(const ControlCommand *cmd) = 0;virtual Chassis publish_chassis() = 0;
};// Lincoln车型实现
class LincolnVehicleFactory : public AbstractVehicleFactory {std::unique_ptr<CanClient> can_client_;CanSender<Lincoln> can_sender_;CanReceiver<Lincoln> can_receiver_;std::unique_ptr<MessageManager<Lincoln>> message_manager_;std::unique_ptr<VehicleController<Lincoln>> vehicle_controller_;
};
优势:
- 新增车型只需实现工厂类,无需修改核心代码
- 通过配置文件动态加载车型库(.so)
- 车型特定逻辑完全隔离
模板方法模式
VehicleController作为模板基类,定义了控制流程框架:
template <typename SensorType>
class VehicleController {public:// 模板方法:定义控制流程virtual ErrorCode Update(const ControlCommand &command) {if (driving_mode() == Chassis::COMPLETE_AUTO_DRIVE) {Gear(command.gear_location()); // 纯虚函数,子类实现Throttle(command.throttle()); // 纯虚函数Brake(command.brake()); // 纯虚函数Steer(command.steering_target()); // 纯虚函数}return ErrorCode::OK;}private:virtual void Gear(Chassis::GearPosition state) = 0;virtual void Throttle(double throttle) = 0;virtual void Brake(double brake) = 0;virtual void Steer(double angle) = 0;
};
工厂方法模式
CanClientFactory负责创建不同类型的CAN驱动:
class CanClientFactory {public:void RegisterCanClients() {Register(CANCardParameter::ESD_CAN,[]() -> CanClient* { return new EsdCanClient(); });Register(CANCardParameter::SOCKET_CAN_RAW,[]() -> CanClient* { return new SocketCanClientRaw(); });Register(CANCardParameter::HERMES_CAN,[]() -> CanClient* { return new HermesCanClient(); });Register(CANCardParameter::FAKE_CAN,[]() -> CanClient* { return new FakeCanClient(); });}CanClient* CreateObject(CANCardParameter::CANCardBrand brand);
};
三、目录结构与文件组织
3.1 应用层目录(modules/canbus/)
modules/canbus/
├── canbus_component.h/cc # 主组件(TimerComponent)
├── vehicle/
│ ├── abstract_vehicle_factory.h/cc # 车型工厂抽象基类
│ └── vehicle_controller.h # 车辆控制器基类
├── proto/
│ ├── canbus_conf.proto # 模块配置
│ └── vehicle_parameter.proto # 车辆参数
├── common/
│ ├── canbus_gflags.h/cc # 全局标志
├── conf/
│ ├── canbus_conf.pb.txt # 配置文件
│ └── canbus.conf # 命令行参数
├── dag/
│ └── canbus.dag # DAG启动配置
├── tools/
│ ├── teleop.cc # 远程操控工具
│ └── canbus_tester.cc # CAN测试工具
└── BUILD # Bazel构建文件
3.2 驱动层目录(modules/drivers/canbus/)
modules/drivers/canbus/
├── can_client/ # CAN客户端
│ ├── can_client.h # 驱动接口
│ ├── can_client_factory.h/cc # 工厂类
│ ├── esd/
│ │ └── esd_can_client.h/cc # ESD CAN驱动
│ ├── hermes_can/
│ │ ├── hermes_can_client.h/cc # Hermes CAN驱动
│ │ ├── bcan.h # BCAN库头文件
│ │ └── bcan_lib.h # BCAN库接口
│ ├── socket/
│ │ └── socket_can_client_raw.h/cc # Linux SocketCAN
│ └── fake/
│ └── fake_can_client.h/cc # 虚拟驱动(测试)
├── can_comm/ # 通信层
│ ├── can_sender.h # 发送器
│ ├── can_receiver.h # 接收器
│ ├── message_manager.h # 消息管理器
│ └── protocol_data.h # 协议数据基类
├── common/
│ ├── byte.h/cc # 字节操作工具
│ └── canbus_consts.h # 常量定义
└── proto/└── sensor_canbus_conf.proto # 传感器CAN配置
3.3 车型适配层目录(modules/canbus_vehicle/)
以Lincoln车型为例:
modules/canbus_vehicle/lincoln/
├── lincoln_vehicle_factory.h/cc # Lincoln工厂
├── lincoln_controller.h/cc # Lincoln控制器
├── lincoln_message_manager.h/cc # Lincoln消息管理器
├── proto/
│ └── lincoln.proto # Lincoln消息定义
└── protocol/ # CAN协议实现├── brake_60.h/cc # 刹车控制(发送 ID:0x60)├── brake_61.h/cc # 刹车反馈(接收 ID:0x61)├── throttle_62.h/cc # 油门控制(发送 ID:0x62)├── throttle_63.h/cc # 油门反馈(接收 ID:0x63)├── steering_64.h/cc # 转向控制(发送 ID:0x64)├── steering_65.h/cc # 转向反馈(接收 ID:0x65)├── gear_66.h/cc # 档位控制(发送 ID:0x66)├── gear_67.h/cc # 档位反馈(接收 ID:0x67)├── turnsignal_68.h/cc # 转向灯(ID:0x68)├── wheelspeed_6a.h/cc # 轮速(接收 ID:0x6A)├── gyro_6c.h/cc # 陀螺仪(ID:0x6C)└── ... # 更多协议
其他支持的车型:GE3、CH、GEM、Lexus、WEY、Zhongyun、Transit、DevKit等。
四、核心组件详解
4.1 CanbusComponent - 主组件
文件路径:modules/canbus/canbus_component.h
继承关系:
class CanbusComponent : public apollo::cyber::TimerComponent
核心职责:
- 定时处理(10ms周期):
Proc()方法 - 管理控制命令订阅
- 发布底盘状态消息
- 超时检测与安全处理
关键方法
Init() - 初始化
bool CanbusComponent::Init() {// 1. 加载配置文件GetProtoConfig(&canbus_conf_);// 2. 动态加载车型库(.so文件)ClassLoader loader(FLAGS_load_vehicle_library);auto vehicle_object = loader.CreateClassObj<AbstractVehicleFactory>(FLAGS_load_vehicle_class_name); // "LincolnVehicleFactory"// 3. 初始化车型工厂(包含CanClient、CanSender、CanReceiver)vehicle_object_->Init(&canbus_conf_);// 4. 创建订阅器if (FLAGS_receive_guardian) {guardian_cmd_reader_ = node_->CreateReader<GuardianCommand>(FLAGS_guardian_topic,[this](const auto &cmd) { OnGuardianCommand(*cmd); });} else {control_command_reader_ = node_->CreateReader<ControlCommand>(FLAGS_control_topic,[this](const auto &cmd) { OnControlCommand(*cmd); });}chassis_command_reader_ = node_->CreateReader<ChassisCommand>(FLAGS_chassis_command_topic,[this](const auto &cmd) { OnChassisCommand(*cmd); });// 5. 创建发布器chassis_writer_ = node_->CreateWriter<Chassis>(FLAGS_chassis_topic);// 6. 启动车型���启动CAN通信线程)vehicle_object_->Start();return true;
}
Proc() - 定时处理(100Hz)
bool CanbusComponent::Proc() {// 1. 检查控制命令超时if (FLAGS_receive_guardian) {OnGuardianCommandCheck(*guardian_cmd_reader_->GetLatestObserved());} else {OnControlCommandCheck(*control_command_reader_->GetLatestObserved());}// 2. 检查底盘通信故障if (vehicle_object_->CheckChassisCommunicationFault()) {AERROR << "Chassis communication error!";}// 3. 发布底盘状态(/apollo/chassis)PublishChassis();// 4. 可选:发布详细底盘信息(/apollo/chassis_detail)if (FLAGS_enable_chassis_detail_pub) {vehicle_object_->PublishChassisDetail();}// 5. 更新发送器心跳(保证CAN报文定期发送)vehicle_object_->UpdateHeartbeat();return true;
}
OnControlCommand() - 处理控制指令
void CanbusComponent::OnControlCommand(const ControlCommand &cmd) {double current_timestamp = Time::Now().ToMicrosecond();// 检查命令间隔(防止过于频繁,默认5ms)if (current_timestamp - last_timestamp_controlcmd_ <FLAGS_min_cmd_interval * 1000) {return;}last_timestamp_controlcmd_ = current_timestamp;// 检查是否超时if (!is_control_cmd_time_delay_) {// 更新控制命令到车型控制器vehicle_object_->UpdateCommand(&cmd);}
}
消息订阅与发布
| 类型 | Topic | 消息类型 | 频率 | 说明 |
|---|---|---|---|---|
| 订阅 | /apollo/control | ControlCommand | 100Hz | 控制指令 |
| 订阅 | /apollo/guardian | GuardianCommand | 100Hz | 安全指令(可选) |
| 订阅 | /apollo/chassis_control | ChassisCommand | - | 外部底盘命令 |
| 发布 | /apollo/chassis | Chassis | 100Hz | 底盘状态 |
| 发布 | /apollo/chassis_detail | 车型特定 | 100Hz | 详细底盘信息 |
4.2 VehicleController - 车辆控制器
文件路径:modules/canbus/vehicle/vehicle_controller.h
模板类:
template <typename SensorType>
class VehicleController;
核心接口
// 初始化和生命周期
virtual ErrorCode Init(const VehicleParameter ¶ms,CanSender<SensorType> *can_sender,MessageManager<SensorType> *msg_manager) = 0;
virtual bool Start() = 0;
virtual void Stop() = 0;// 获取底盘状态
virtual Chassis chassis() = 0;// 控制指令更新
virtual ErrorCode Update(const ControlCommand &cmd);
virtual ErrorCode Update(const ChassisCommand &cmd);// 驾驶模式管理
virtual ErrorCode SetDrivingMode(const Chassis::DrivingMode &mode);
纯虚函数(子类必须实现)
// 应急停车
virtual void Emergency() = 0;// 模式切换
virtual ErrorCode EnableAutoMode() = 0;
virtual ErrorCode DisableAutoMode() = 0;
virtual ErrorCode EnableSteeringOnlyMode() = 0;
virtual ErrorCode EnableSpeedOnlyMode() = 0;// 车辆控制
virtual void Gear(Chassis::GearPosition state) = 0;
virtual void Brake(double brake) = 0; // 0~100%
virtual void Throttle(double throttle) = 0; // 0~100%
virtual void Acceleration(double acc) = 0; // m/s²
virtual void Steer(double angle) = 0; // -100~100%
virtual void Steer(double angle, double rate) = 0; // 带速率// 信号灯控制
virtual void SetBeam(const VehicleSignal &signal) = 0;
virtual void SetHorn(const VehicleSignal &signal) = 0;
virtual void SetTurningSignal(const VehicleSignal &signal) = 0;// VIN码验证
virtual bool VerifyID() = 0;
Update()方法实现
template <typename SensorType>
ErrorCode VehicleController<SensorType>::Update(const ControlCommand &control_command) {// 1. 处理Pad消息(驾驶模式切换)if (control_command.has_pad_msg()) {if (control_command.pad_msg().action() == DrivingAction::START) {SetDrivingMode(Chassis::COMPLETE_AUTO_DRIVE);} else if (control_command.pad_msg().action() == DrivingAction::RESET) {SetDrivingMode(Chassis::COMPLETE_MANUAL);} else if (control_command.pad_msg().action() == DrivingAction::VIN_REQ) {VerifyID();}}// 2. 完全自动或速度控制模式if (driving_mode() == Chassis::COMPLETE_AUTO_DRIVE ||driving_mode() == Chassis::AUTO_SPEED_ONLY) {Gear(control_command.gear_location());Throttle(control_command.throttle());Acceleration(control_command.acceleration());Brake(control_command.brake());SetEpbBreak(control_command);}// 3. 完全自动或转向控制模式if (driving_mode() == Chassis::COMPLETE_AUTO_DRIVE ||driving_mode() == Chassis::AUTO_STEER_ONLY) {if (control_command.steering_rate() > 1.0) {Steer(control_command.steering_target(),control_command.steering_rate());} else {Steer(control_command.steering_target());}}// 4. 处理车辆信号(灯光、喇叭)if (control_command.has_signal()) {HandleVehicleSignal(control_command.signal());}return ErrorCode::OK;
}
4.3 MessageManager - 消息管理器
文件路径:modules/drivers/canbus/can_comm/message_manager.h
核心职责:
- 维护协议数据集合(发送/接收)
- 解析CAN报文到Protobuf消息
- 线程安全的数据访问
- 周期监控与异常检测
数据结构
template <typename SensorType>
class MessageManager {protected:// 协议数据集合std::vector<std::unique_ptr<ProtocolData<SensorType>>> send_protocol_data_;std::vector<std::unique_ptr<ProtocolData<SensorType>>> recv_protocol_data_;// ID映射表(快速查询)std::unordered_map<uint32_t, ProtocolData<SensorType>*> protocol_data_map_;std::unordered_map<uint32_t, ProtocolData<SensorType>*> recv_protocol_data_map_;std::unordered_map<uint32_t, ProtocolData<SensorType>*> sender_protocol_data_map_;// 周期检查std::unordered_map<uint32_t, CheckIdArg> check_ids_;// 线程安全的数据缓存std::mutex sensor_data_mutex_;SensorType sensor_data_; // 所有数据std::mutex sensor_data_recv_mutex_;SensorType sensor_recv_data_; // 接收数据std::mutex sensor_data_sender_mutex_;SensorType sensor_sender_data_; // 发送数据
};
核心方法
注册协议
// 添加接收协议
template <class T, bool need_check>
void AddRecvProtocolData() {recv_protocol_data_.emplace_back(new T());auto *protocol = recv_protocol_data_.back().get();recv_protocol_data_map_[T::ID] = protocol;protocol_data_map_[T::ID] = protocol;if (need_check) {check_ids_[T::ID].period = protocol->GetPeriod();check_ids_[T::ID].last_time = 0;check_ids_[T::ID].error_count = 0;}
}// 添加发送协议
template <class T, bool need_check>
void AddSendProtocolData() {send_protocol_data_.emplace_back(new T());auto *protocol = send_protocol_data_.back().get();sender_protocol_data_map_[T::ID] = protocol;protocol_data_map_[T::ID] = protocol;if (need_check) {check_ids_[T::ID].period = protocol->GetPeriod();}
}
使用示例(LincolnMessageManager):
LincolnMessageManager::LincolnMessageManager() {// 注册发送协议AddSendProtocolData<Brake60, true>(); // 刹车控制AddSendProtocolData<Throttle62, true>(); // 油门控制AddSendProtocolData<Steering64, true>(); // 转向控制AddSendProtocolData<Gear66, true>(); // 档位控制// 注册接收协议AddRecvProtocolData<Brake61, true>(); // 刹车反馈AddRecvProtocolData<Throttle63, true>(); // 油门反馈AddRecvProtocolData<Steering65, true>(); // 转向反馈AddRecvProtocolData<Gear67, true>(); // 档位反馈AddRecvProtocolData<Wheelspeed6a, true>(); // 轮速AddRecvProtocolData<Gyro6c, false>(); // 陀螺仪
}
解析接收报文
void MessageManager<SensorType>::Parse(const uint32_t message_id, const uint8_t *data, int32_t length) {// 1. 根据ID获取协议对象ProtocolData<SensorType> *protocol = GetMutableProtocolDataById(message_id);if (protocol == nullptr) return;// 2. 解析到所有数据缓存{std::lock_guard<std::mutex> lock(sensor_data_mutex_);protocol->Parse(data, length, &sensor_data_);}// 3. 解析到接收数据缓存{std::lock_guard<std::mutex> lock(sensor_data_recv_mutex_);protocol->Parse(data, length, &sensor_recv_data_);}// 4. 周期检查auto it = check_ids_.find(message_id);if (it != check_ids_.end()) {int64_t current_time = Time::Now().ToNanosecond() / 1000;it->second.real_period = current_time - it->second.last_time;// 如果实际周期 > 1.5倍基准周期,增加错误计数if (it->second.real_period > it->second.period * 1.5) {it->second.error_count++;} else {it->second.error_count = 0;}it->second.last_time = current_time;}
}
获取数据(线程安全)
ErrorCode GetSensorData(SensorType *sensor_data) {if (sensor_data == nullptr) {return ErrorCode::CANBUS_ERROR;}std::lock_guard<std::mutex> lock(sensor_data_mutex_);sensor_data->CopyFrom(sensor_data_); // Protobuf深拷贝return ErrorCode::OK;
}ErrorCode GetSensorRecvData(SensorType *sensor_recv_data) {std::lock_guard<std::mutex> lock(sensor_data_recv_mutex_);sensor_recv_data->CopyFrom(sensor_recv_data_);return ErrorCode::OK;
}
五、CAN驱动层设计
5.1 CanClient接口
文件路径:modules/drivers/canbus/can_client/can_client.h
class CanClient {public:// 初始化virtual bool Init(const CANCardParameter ¶meter) = 0;// 启动/停止virtual ErrorCode Start() = 0;virtual void Stop() = 0;// 发送virtual ErrorCode Send(const std::vector<CanFrame> &frames,int32_t *frame_num) = 0;virtual ErrorCode SendSingleFrame(const std::vector<CanFrame> &frames);// 接收virtual ErrorCode Receive(std::vector<CanFrame> *frames,int32_t *frame_num) = 0;// 错误处理virtual std::string GetErrorString(int32_t status) = 0;
};
CAN帧结构
struct CanFrame {uint32_t id; // CAN ID(11位或29位)uint8_t len; // 数据长度(0~8)uint8_t data[8]; // 数据内容struct timeval timestamp; // 时间戳// 工具方法std::string CanFrameString() const {return absl::StrCat("id:0x", absl::Hex(id)," data:", BytesToHex(data, len));}
};
5.2 支持的CAN卡类型
5.2.1 ESD CAN
特点:
- 德国ESD公司的NTCAN库
- 支持PCI/USB接口
- 高性能、低延迟
- 需要编译时启用
USE_ESD_CAN
实现:modules/drivers/canbus/can_client/esd/esd_can_client.cc
class EsdCanClient : public CanClient {public:bool Init(const CANCardParameter ¶m) override {port_ = param.channel_id();// 打开CAN设备NTCAN_RESULT res = canOpen(port_, 0, TX_QUEUE_SIZE, RX_QUEUE_SIZE,TX_TIMEOUT, RX_TIMEOUT, &dev_handler_);if (res != NTCAN_SUCCESS) {AERROR << "Open ESD CAN failed, error: " << res;return false;}// 设置波特率(默认500kbps)res = canSetBaudrate(dev_handler_, NTCAN_BAUD_500);return res == NTCAN_SUCCESS;}ErrorCode Send(const std::vector<CanFrame> &frames,int32_t *frame_num) override {// 转换为ESD格式for (size_t i = 0; i < frames.size(); ++i) {send_frames_[i].id = frames[i].id;send_frames_[i].len = frames[i].len;memcpy(send_frames_[i].data, frames[i].data, frames[i].len);}// 发送NTCAN_RESULT res = canWrite(dev_handler_, send_frames_,frame_num, nullptr);return (res == NTCAN_SUCCESS) ? ErrorCode::OK : ErrorCode::CAN_CLIENT_ERROR_SEND_FAILED;}private:NTCAN_HANDLE dev_handler_;CANCardParameter::CANChannelId port_;CMSG send_frames_[MAX_CAN_SEND_FRAME_LEN];CMSG recv_frames_[MAX_CAN_RECV_FRAME_LEN];
};
5.2.2 Hermes CAN(BCAN)
特点:
- 基于Zynq FPGA平台
- BCAN协议(高性能CAN通信协议)
- 支持多端口(最多8个)
- 适用于高带宽场景
实现:modules/drivers/canbus/can_client/hermes_can/hermes_can_client.cc
class HermesCanClient : public CanClient {public:bool Init(const CANCardParameter ¶m) override {port_ = param.channel_id();int num_ports = param.num_ports(); // 端口数量// 初始化BCAN库bcan_status_t ret = bcan_init(&dev_handler_, num_ports);if (ret != BCAN_OK) {AERROR << "BCAN init failed: " << ret;return false;}// 配置CAN参数bcan_cfg_t cfg;cfg.baudrate = BCAN_BAUDRATE_500K;cfg.mode = BCAN_MODE_NORMAL;bcan_set_cfg(dev_handler_, port_, &cfg);is_init_ = true;return true;}ErrorCode Receive(std::vector<CanFrame> *frames,int32_t *frame_num) override {// 接收BCAN消息bcan_status_t ret = bcan_recv(dev_handler_, port_, _recv_frames,frame_num, BCAN_TIMEOUT_NONE);if (ret != BCAN_OK && ret != BCAN_ERR_NO_DATA) {return ErrorCode::CAN_CLIENT_ERROR_RECV_FAILED;}// 转换为通用格式frames->resize(*frame_num);for (int i = 0; i < *frame_num; ++i) {(*frames)[i].id = _recv_frames[i].can_id;(*frames)[i].len = _recv_frames[i].can_dlc;memcpy((*frames)[i].data, _recv_frames[i].data, _recv_frames[i].can_dlc);(*frames)[i].timestamp = _recv_frames[i].timestamp;}return ErrorCode::OK;}private:bcan_hdl_t dev_handler_;CANCardParameter::CANChannelId port_;bcan_msg_t _send_frames[MAX_CAN_SEND_FRAME_LEN];bcan_msg_t _recv_frames[MAX_CAN_RECV_FRAME_LEN];
};
5.2.3 Socket CAN(Linux原生)
特点:
- Linux内核原生支持
- 无需专用硬件驱动
- 跨平台兼容性好
- 适用于开发测试
实现:modules/drivers/canbus/can_client/socket/socket_can_client_raw.cc
class SocketCanClientRaw : public CanClient {public:bool Init(const CANCardParameter ¶m) override {port_ = param.channel_id();// 创建Socketdev_handler_ = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (dev_handler_ < 0) {AERROR << "Create socket failed";return false;}// 绑定CAN接口struct ifreq ifr;std::string port_name = "can" + std::to_string(port_);strcpy(ifr.ifr_name, port_name.c_str());ioctl(dev_handler_, SIOCGIFINDEX, &ifr);struct sockaddr_can addr;addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(dev_handler_, (struct sockaddr *)&addr, sizeof(addr)) < 0) {AERROR << "Bind socket failed";return false;}return true;}ErrorCode Send(const std::vector<CanFrame> &frames,int32_t *frame_num) override {for (size_t i = 0; i < frames.size(); ++i) {send_frames_[i].can_id = frames[i].id;send_frames_[i].can_dlc = frames[i].len;memcpy(send_frames_[i].data, frames[i].data, frames[i].len);int nbytes = write(dev_handler_, &send_frames_[i],sizeof(struct can_frame));if (nbytes != sizeof(struct can_frame)) {AERROR << "Send CAN frame failed";return ErrorCode::CAN_CLIENT_ERROR_SEND_FAILED;}}*frame_num = frames.size();return ErrorCode::OK;}private:int dev_handler_;CANCardParameter::CANChannelId port_;struct can_frame send_frames_[MAX_CAN_SEND_FRAME_LEN];struct can_frame recv_frames_[MAX_CAN_RECV_FRAME_LEN];
};
5.2.4 Fake CAN(虚拟驱动)
特点:
- 纯内存模拟,无需硬件
- 用于单元测试
- 可预设返回数据
class FakeCanClient : public CanClient {public:ErrorCode Send(const std::vector<CanFrame> &frames,int32_t *frame_num) override {*frame_num = frames.size();send_counter_ += *frame_num;return ErrorCode::OK;}ErrorCode Receive(std::vector<CanFrame> *frames,int32_t *frame_num) override {// 返回预定义的测试数据*frame_num = 1;frames->resize(1);(*frames)[0].id = 0x100;(*frames)[0].len = 8;memset((*frames)[0].data, 0xAA, 8);recv_counter_++;return ErrorCode::OK;}private:int32_t send_counter_ = 0;int32_t recv_counter_ = 0;
};
5.3 CanClientFactory - 工厂类
class CanClientFactory {public:void RegisterCanClients() {Register(CANCardParameter::FAKE_CAN,[]() { return new FakeCanClient(); });#ifdef USE_ESD_CANRegister(CANCardParameter::ESD_CAN,[]() { return new EsdCanClient(); });
#endifRegister(CANCardParameter::SOCKET_CAN_RAW,[]() { return new SocketCanClientRaw(); });Register(CANCardParameter::HERMES_CAN,[]() { return new HermesCanClient(); });}CanClient* CreateObject(CANCardParameter::CANCardBrand brand) {auto it = factory_map_.find(brand);if (it == factory_map_.end()) {return nullptr;}return it->second();}private:std::unordered_map<CANCardParameter::CANCardBrand,std::function<CanClient*()>> factory_map_;
};
六、通信机制与数据流
6.1 CanSender - 发送器
文件路径:modules/drivers/canbus/can_comm/can_sender.h
设计特点:
- 独立发送线程(实时优先级SCHED_FIFO,优先级99)
- 周期性发送管理
- 精确定时控制
核心方法
template <typename SensorType>
class CanSender {public:ErrorCode Init(CanClient *can_client,MessageManager<SensorType> *msg_manager,bool enable_log) {can_client_ = can_client;pt_manager_ = msg_manager;enable_log_ = enable_log;return ErrorCode::OK;}// 添加待发送消息void AddMessage(uint32_t message_id,ProtocolData<SensorType> *protocol_data,bool init_with_one = false) {if (init_with_one) {send_messages_.emplace_back(protocol_data, true);} else {send_messages_.emplace_back(protocol_data);}}// 启动发送线程ErrorCode Start() {if (is_running_) return ErrorCode::OK;is_running_ = true;// 创建高优先级线程thread_.reset(new std::thread([this] { PowerSendThreadFunc(); }));return ErrorCode::OK;}// 停止发送线程void Stop() {if (!is_running_) return;is_running_ = false;if (thread_ && thread_->joinable()) {thread_->join();}}// 更新协议数据void Update() {for (auto &message : send_messages_) {message.Update(); // 调用ProtocolData::UpdateData()}}private:void PowerSendThreadFunc(); // 发送线程函数CanClient *can_client_ = nullptr;MessageManager<SensorType> *pt_manager_ = nullptr;std::vector<SenderMessage<SensorType>> send_messages_;bool enable_log_ = false;bool is_running_ = false;std::unique_ptr<std::thread> thread_;
};
发送线程实现
template <typename SensorType>
void CanSender<SensorType>::PowerSendThreadFunc() {AINFO << "Can client sender thread starts";// 设置实时线程优先级sched_param sch_param;sch_param.sched_priority = 99; // 最高优先级if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_param) != 0) {AWARN << "Failed to set thread priority";}const int32_t INIT_PERIOD = 5000; // 初始周期5ms(微秒)int32_t delta_period = INIT_PERIOD;while (is_running_) {int64_t tm_start = cyber::Time::Now().ToNanosecond() / 1000; // 微秒int32_t new_delta_period = INIT_PERIOD;// 遍历所有待发送消息for (auto &message : send_messages_) {// 检查是否需要发送(周期判断)bool need_send = NeedSend(message, delta_period);// 更新当前周期计数message.UpdateCurrPeriod(delta_period);// 计算下一次最小周期new_delta_period = std::min(new_delta_period, message.curr_period());if (!need_send) continue;// 发送CAN帧std::vector<CanFrame> can_frames;CanFrame can_frame = message.CanFrame();can_frames.push_back(can_frame);if (can_client_->SendSingleFrame(can_frames) != ErrorCode::OK) {AERROR << "Send frame failed: " << can_frame.CanFrameString();}if (enable_log_) {AINFO << "TX: " << can_frame.CanFrameString();}// 记录发送数据到MessageManagerpt_manager_->ParseSender(can_frame.id, can_frame.data, can_frame.len);}// 精确定时休眠delta_period = new_delta_period;int64_t tm_end = cyber::Time::Now().ToNanosecond() / 1000;int64_t sleep_interval = delta_period - (tm_end - tm_start);if (sleep_interval > 0) {std::this_thread::sleep_for(std::chrono::microseconds(sleep_interval));} else {AWARN << "Send cycle overrun: " << (tm_end - tm_start)<< "us > " << delta_period << "us";}}AINFO << "Can client sender thread stopped";
}
SenderMessage类
template <typename SensorType>
class SenderMessage {public:SenderMessage(ProtocolData<SensorType> *protocol_data,bool init_with_one = false): protocol_data_(protocol_data) {period_ = protocol_data_->GetPeriod();curr_period_ = init_with_one ? 0 : period_; // 初始周期}void Update() {protocol_data_->UpdateData(data_); // 编码数据}CanFrame CanFrame() const {struct CanFrame frame;frame.id = protocol_data_->ID();frame.len = protocol_data_->GetLength();memcpy(frame.data, data_, frame.len);return frame;}void UpdateCurrPeriod(int32_t delta) {curr_period_ -= delta;if (curr_period_ <= 0) {curr_period_ = period_;}}int32_t curr_period() const { return curr_period_; }private:ProtocolData<SensorType> *protocol_data_;uint8_t data_[8] = {0};int32_t period_; // 基准周期(微秒)int32_t curr_period_; // 当前周期计数
};
6.2 CanReceiver - 接收器
文件路径:modules/drivers/canbus/can_comm/can_receiver.h
设计特点:
- 异步接收线程(cyber::Async)
- 批量接收优化(最多10帧)
- 自动错误恢复
核心实现
template <typename SensorType>
class CanReceiver {public:ErrorCode Init(CanClient *can_client,MessageManager<SensorType> *msg_manager,bool enable_log) {can_client_ = can_client;pt_manager_ = msg_manager;enable_log_ = enable_log;return ErrorCode::OK;}ErrorCode Start() {if (is_running_) return ErrorCode::OK;is_running_ = true;// 使用CyberRT异步启动接收线程async_result_ = cyber::Async(&CanReceiver::RecvThreadFunc, this);return ErrorCode::OK;}void Stop() {if (!is_running_) return;is_running_ = false;}private:void RecvThreadFunc();CanClient *can_client_ = nullptr;MessageManager<SensorType> *pt_manager_ = nullptr;bool enable_log_ = false;std::atomic<bool> is_running_{false};std::atomic<bool> is_finish_recv_once_{false};
};
接收线程实现
template <typename SensorType>
void CanReceiver<SensorType>::RecvThreadFunc() {AINFO << "Can client receiver thread starts";int32_t receive_error_count = 0;int32_t receive_none_count = 0;const int32_t ERROR_COUNT_MAX = 10;auto default_period = 10 * 1000; // 10mswhile (is_running_) {is_finish_recv_once_.exchange(false);// 接收CAN报文(最多10帧)std::vector<CanFrame> buf;int32_t frame_num = MAX_CAN_RECV_FRAME_LEN; // 10ErrorCode ret = can_client_->Receive(&buf, &frame_num);// 错误处理if (ret != ErrorCode::OK) {LOG_IF_EVERY_N(ERROR, receive_error_count++ > ERROR_COUNT_MAX,ERROR_COUNT_MAX)<< "Received " << receive_error_count << " error messages";cyber::USleep(default_period);continue;}receive_error_count = 0;// 检查帧数if (buf.size() != static_cast<size_t>(frame_num)) {AERROR_EVERY(100) << "Buffer size mismatch: " << buf.size()<< " vs " << frame_num;}// 无数据if (frame_num == 0) {LOG_IF_EVERY_N(ERROR, receive_none_count++ > ERROR_COUNT_MAX,ERROR_COUNT_MAX)<< "Received " << receive_none_count << " empty messages";cyber::USleep(default_period);continue;}receive_none_count = 0;// 处理每个接收到的CAN帧for (const auto &frame : buf) {uint32_t id = frame.id;const uint8_t *data = frame.data;uint8_t len = frame.len;// 通过MessageManager解析pt_manager_->Parse(id, data, len);// 可选:记录接收日志if (enable_log_) {AINFO << "RX: " << frame.CanFrameString();}}// 标记一次接收完成is_finish_recv_once_.exchange(true);cyber::Yield(); // 让出CPU时间片}AINFO << "Can client receiver thread stopped";
}
6.3 完整数据流图
┌─────────────────────────────────────────────────────────────────┐
│ CanbusComponent::Proc() │
│ (100Hz定时器) │
└───────────────────────┬─────────────────────────────────────────┘│┌───────────────┴────────────────┐│ │▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ OnControlCommand │ │ PublishChassis │
└────────┬─────────┘ └──────────▲───────────┘│ │▼ │
┌────────────────────────────┐ │
│ VehicleController::Update()│ │
│ - Throttle(cmd.throttle) │ │
│ - Brake(cmd.brake) │ │
│ - Steer(cmd.steering) │ │
│ - Gear(cmd.gear) │ │
└────────┬───────────────────┘ ││ │▼ │
┌─────────────────��──────────┐ │
│ Throttle62::set_pedal() │ │
│ (设置油门命令到协议对象) │ │
└────────┬───────────────────┘ ││ │▼ │
┌────────────────────────────┐ │
│ CanSender::Update() │ │
│ (更新所有发送消息) │ │
└────────┬───────────────────┘ ││ │
┌────────▼───────────────────────────────┼────────────────┐
│ CanSender发送线程 (优先级99) │ │
│ ┌─────────────────────────────────┐ │ │
│ │ for (auto &msg : send_messages)│ │ │
│ │ if (NeedSend(msg, period)) │ │ │
│ │ CanClient->Send(CanFrame) │ │ │
│ │ MessageManager->ParseSender│ │ │
│ └─────────────────────────────────┘ │ │
└────────┬───────────────────────────────┼────────────────┘│ │��� │
┌────────────────────────────┐ │
│ CanClient::Send() │ │
│ (ESD/Hermes/Socket) │ │
└────────┬───────────────────┘ ││ │▼ │CAN总线 ││ ││ │车辆底盘 ││ ││ (反馈报文) │▼ │
┌────────────────────────────┐ │
│ CanClient::Receive() │ │
└────────┬───────────────────┘ ││ │▼ │
┌────────────────────────────────────────┼────────────────┐
│ CanReceiver接收线程 │ │
│ ┌─────────────────────────────────┐ │ │
│ │ CanClient->Receive(&frames, &n)│ │ │
│ │ for (auto &frame : frames) │ │ │
│ │ MessageManager->Parse(id,...) │ │ │
│ └─────────────────────────────────┘ │ │
└────────┬───────────────────────────────┼────────────────┘│ │▼ │
┌────────────────────────────┐ │
│ MessageManager::Parse() │ │
│ - Throttle63::Parse() │ │
│ - Brake61::Parse() │ │
│ - Steering65::Parse() │ │
│ (更新sensor_recv_data_) │ │
└────────┬───────────────────┘ ││ │└───────────────────────────────┘
七、协议解析与编码
7.1 ProtocolData基类
文件路径:modules/drivers/canbus/can_comm/protocol_data.h
template <typename SensorType>
class ProtocolData {public:virtual ~ProtocolData() = default;// 配置查询virtual uint32_t GetPeriod() const {return 20 * 1000; // 默认20ms}virtual int32_t GetLength() const {return 8; // 默认8字节}// 报文处理virtual void Parse(const uint8_t *bytes, int32_t length,SensorType *sensor_data) const {}virtual void UpdateData(uint8_t *data) {}virtual void UpdateData_Heartbeat(uint8_t *data) {}virtual void Reset() {}// 工具方法static uint8_t CalculateCheckSum(const uint8_t *input,const uint32_t length) {uint8_t sum = 0;for (uint32_t i = 0; i < length; ++i) {sum += input[i];}return sum;}template <typename T>static T BoundedValue(T lower, T upper, T val) {return std::max(lower, std::min(upper, val));}
};
7.2 实际案例:Lincoln Brake协议
7.2.1 Brake60(发送 - 刹车控制)
CAN ID: 0x60
周期: 10ms
长度: 8字节
数据格式:
| 字节 | 位 | 字段 | 类型 | 范围 | 说明 |
|---|---|---|---|---|---|
| 0-1 | 0-15 | pedal_cmd | uint16 | 0-65535 | 制动踏板命令(0-100%) |
| 2 | 16 | boo_cmd | bool | 0-1 | 制动请求 |
| 3 | 24 | enable | bool | 0-1 | 使能标志 |
| 3 | 25 | clear_override | bool | 0-1 | 清除驾驶员接管 |
| 3 | 26 | ignore_override | bool | 0-1 | 忽略接管 |
| 7 | 56-63 | watchdog_counter | uint8 | 0-255 | 看门狗计数器 |
实现:modules/canbus_vehicle/lincoln/protocol/brake_60.h
class Brake60 : public ProtocolData<Lincoln> {public:static const int32_t ID = 0x60;Brake60();uint32_t GetPeriod() const override {return 10 * 1000; // 10ms}void UpdateData(uint8_t *data) override;void Reset() override;// 链式调用设置器Brake60 *set_pedal(double pedal); // 0~100%Brake60 *set_enable();Brake60 *set_disable();Brake60 *set_boo_cmd(bool cmd);Brake60 *set_clear_driver_override_flag(bool flag);Brake60 *set_ignore_driver_override(bool ignore);private:// 字段编码方法void set_pedal_p(uint8_t *data, double pedal);void set_boo_cmd_p(uint8_t *data, bool cmd);void set_enable_p(uint8_t *data, bool enable);void set_clear_driver_override_flag_p(uint8_t *data, bool flag);void set_ignore_driver_override_p(uint8_t *data, bool ignore);void set_watchdog_counter_p(uint8_t *data, uint8_t counter);// 数值转换double pedal_to_value(double pedal); // 百分比 -> 原始值private:double pedal_cmd_;bool boo_cmd_;bool pedal_enable_;bool clear_driver_override_flag_;bool ignore_driver_override_;uint8_t watchdog_counter_;
};
编码实现:
void Brake60::UpdateData(uint8_t *data) {// 清空数据memset(data, 0, 8);// 编码制动踏板命令(bit 0-15)set_pedal_p(data, pedal_cmd_);// 编码制动请求(bit 16)set_boo_cmd_p(data, boo_cmd_);// 编码使能标志(bit 24)set_enable_p(data, pedal_enable_);// 编码清除接管标志(bit 25)set_clear_driver_override_flag_p(data, clear_driver_override_flag_);// 编码忽略接管标志(bit 26)set_ignore_driver_override_p(data, ignore_driver_override_);// 编码看门狗计数器(bit 56-63)watchdog_counter_ = (watchdog_counter_ + 1) % 16;set_watchdog_counter_p(data, watchdog_counter_);
}void Brake60::set_pedal_p(uint8_t *data, double pedal) {// 百分比转换为原始值 [0, 100] -> [0, 65535]int32_t raw_value = static_cast<int32_t>(pedal / 100.0 * 65535);raw_value = ProtocolData::BoundedValue(0, 65535, raw_value);// 编码为两个字节(小端)data[0] = raw_value & 0xFF; // 低字节data[1] = (raw_value >> 8) & 0xFF; // 高字节
}void Brake60::set_enable_p(uint8_t *data, bool enable) {// bit 24 (byte 3, bit 0)if (enable) {data[3] |= 0x01;} else {data[3] &= ~0x01;}
}void Brake60::set_watchdog_counter_p(uint8_t *data, uint8_t counter) {// bit 56-63 (byte 7)data[7] = counter & 0x0F; // 只使用低4位
}// 链式调用实现
Brake60 *Brake60::set_pedal(double pedal) {pedal_cmd_ = BoundedValue(0.0, 100.0, pedal);return this;
}Brake60 *Brake60::set_enable() {pedal_enable_ = true;return this;
}
7.2.2 Brake61(接收 - 刹车反馈)
CAN ID: 0x61
周期: 10ms(接收)
长度: 8字节
数据格式:
| 字节 | 位 | 字段 | 类型 | 范围 | 说明 |
|---|---|---|---|---|---|
| 0-1 | 0-15 | pedal_input | uint16 | 0-65535 | 制动踏板输入 |
| 2-3 | 16-31 | pedal_cmd | uint16 | 0-65535 | 制动命令回显 |
| 4-5 | 32-47 | pedal_output | uint16 | 0-65535 | 实际制动输出 |
| 6 | 48 | boo_input | bool | 0-1 | 制动请求输入 |
| 6 | 49 | boo_cmd | bool | 0-1 | 制动请求回显 |
| 6 | 50 | boo_output | bool | 0-1 | 实际制动状态 |
| 7 | 56 | enabled | bool | 0-1 | 系统使能状态 |
| 7 | 57 | driver_override | bool | 0-1 | 驾驶员接管 |
| 7 | 58 | fault | bool | 0-1 | 故障标志 |
实现:
class Brake61 : public ProtocolData<Lincoln> {public:static const int32_t ID = 0x61;Brake61();void Parse(const uint8_t *bytes, int32_t length,Lincoln *chassis_detail) const override;private:// 字段解析方法double pedal_input(const uint8_t *bytes, int32_t length) const;double pedal_cmd(const uint8_t *bytes, int32_t length) const;double pedal_output(const uint8_t *bytes, int32_t length) const;bool boo_input(const uint8_t *bytes, int32_t length) const;bool boo_cmd(const uint8_t *bytes, int32_t length) const;bool boo_output(const uint8_t *bytes, int32_t length) const;bool is_enabled(const uint8_t *bytes, int32_t length) const;bool is_driver_override(const uint8_t *bytes, int32_t length) const;bool is_fault(const uint8_t *bytes, int32_t length) const;// 数值转换double parse_two_frames(uint8_t low_byte, uint8_t high_byte) const;
};
解析实现:
void Brake61::Parse(const uint8_t *bytes, int32_t length,Lincoln *chassis_detail) const {// 解析数值字段chassis_detail->mutable_brake()->set_brake_input(pedal_input(bytes, length));chassis_detail->mutable_brake()->set_brake_cmd(pedal_cmd(bytes, length));chassis_detail->mutable_brake()->set_brake_output(pedal_output(bytes, length));// 解析布尔字段chassis_detail->mutable_brake()->set_boo_input(boo_input(bytes, length));chassis_detail->mutable_brake()->set_boo_cmd(boo_cmd(bytes, length));chassis_detail->mutable_brake()->set_boo_output(boo_output(bytes, length));// 解析状态字段chassis_detail->mutable_brake()->set_brake_enabled(is_enabled(bytes, length));chassis_detail->mutable_brake()->set_driver_override(is_driver_override(bytes, length));chassis_detail->mutable_brake()->set_brake_fault(is_fault(bytes, length));// 健康检查chassis_detail->mutable_check_response()->set_is_esp_online(!is_driver_override(bytes, length) && !is_fault(bytes, length));
}// 解析16位数值(0-65535 -> 0-100%)
double Brake61::pedal_input(const uint8_t *bytes, int32_t length) const {DCHECK_GE(length, 2);return parse_two_frames(bytes[0], bytes[1]);
}double Brake61::parse_two_frames(uint8_t low_byte, uint8_t high_byte) const {// 组合16位数据int32_t value = (high_byte << 8) | low_byte; // [0, 65535]// 转换为百分比double output = 100.0 * value * 1.52590218966964e-05; // 100/65535// 限制范围output = ProtocolData::BoundedValue(0.0, 100.0, output);return output;
}// 解析布尔字段
bool Brake61::boo_input(const uint8_t *bytes, int32_t length) const {DCHECK_GE(length, 7);Byte frame(bytes + 6);return frame.is_bit_1(0); // bit 48
}bool Brake61::is_enabled(const uint8_t *bytes, int32_t length) const {DCHECK_GE(length, 8);Byte frame(bytes + 7);return frame.is_bit_1(0); // bit 56
}bool Brake61::is_driver_override(const uint8_t *bytes, int32_t length) const {DCHECK_GE(length, 8);Byte frame(bytes + 7);return frame.is_bit_1(1); // bit 57
}
7.3 Byte工具类
文件路径:modules/drivers/canbus/common/byte.h
提供位操作和字节转换工具:
class Byte {public:explicit Byte(const uint8_t *data) : data_(data) {}// 位操作bool is_bit_1(int32_t pos) const {return (data_[0] & (1 << pos)) != 0;}void set_bit_1(int32_t pos, uint8_t *data) const {data[0] |= (1 << pos);}void set_bit_0(int32_t pos, uint8_t *data) const {data[0] &= ~(1 << pos);}// 获取多位值uint8_t get_byte(int32_t start_bit, int32_t length) const;// 字节序转换static void set_value(uint8_t *data, int32_t start_byte,int32_t length, int64_t value);static int64_t get_value(const uint8_t *data, int32_t start_byte,int32_t length);// 十六进制转换static std::string byte_to_hex(uint32_t value);static std::string byte_to_binary(uint8_t value);private:const uint8_t *data_;
};
八、配置系统
8.1 Protobuf配置定义
canbus_conf.proto
文件路径:modules/canbus/proto/canbus_conf.proto
syntax = "proto2";package apollo.canbus;import "modules/common_msgs/chassis_msgs/chassis.proto";
import "modules/common_msgs/drivers_msgs/can_card_parameter.proto";message CanbusConf {optional VehicleParameter vehicle_parameter = 1;optional apollo.drivers.canbus.CANCardParameter can_card_parameter = 2;optional bool enable_debug_mode = 3 [default = false];optional bool enable_receiver_log = 4 [default = false];optional bool enable_sender_log = 5 [default = false];
}message VehicleParameter {// 最大启用失败尝试次数optional int32 max_enable_fail_attempt = 1 [default = 5];// 驾驶模式optional apollo.canbus.Chassis.DrivingMode driving_mode = 2[default = COMPLETE_MANUAL];
}
can_card_parameter.proto
message CANCardParameter {enum CANCardBrand {FAKE_CAN = 0;ESD_CAN = 1;SOCKET_CAN_RAW = 2;HERMES_CAN = 3;}enum CANCardType {PCI_CARD = 0;USB_CARD = 1;}enum CANChannelId {CHANNEL_ID_ZERO = 0;CHANNEL_ID_ONE = 1;CHANNEL_ID_TWO = 2;CHANNEL_ID_THREE = 3;}enum CANInterface {NATIVE = 0;VIRTUAL = 1;}optional CANCardBrand brand = 1;optional CANCardType type = 2;optional CANChannelId channel_id = 3;optional int32 num_ports = 4 [default = 1];optional CANInterface interface = 5 [default = NATIVE];
}
8.2 配置文件示例
canbus_conf.pb.txt
文件路径:modules/canbus/conf/canbus_conf.pb.txt
vehicle_parameter {max_enable_fail_attempt: 5driving_mode: COMPLETE_AUTO_DRIVE
}can_card_parameter {brand: HERMES_CANtype: PCI_CARDchannel_id: CHANNEL_ID_ZEROnum_ports: 8interface: NATIVE
}enable_debug_mode: false
enable_receiver_log: false
enable_sender_log: false
不同CAN卡配置示例:
# ESD CAN配置
can_card_parameter {brand: ESD_CANtype: USB_CARDchannel_id: CHANNEL_ID_ZEROinterface: NATIVE
}# Socket CAN配置
can_card_parameter {brand: SOCKET_CAN_RAWtype: VIRTUALchannel_id: CHANNEL_ID_ZEROinterface: NATIVE
}# 虚拟CAN配置(测试)
can_card_parameter {brand: FAKE_CANtype: VIRTUALchannel_id: CHANNEL_ID_ZEROinterface: VIRTUAL
}
8.3 Gflags参数
文件路径:modules/canbus/common/canbus_gflags.cc
// 系统配置
DEFINE_string(canbus_node_name, "canbus", "CyberRT node name");
DEFINE_string(canbus_module_name, "canbus", "Module name");
DEFINE_string(canbus_conf_file,"/apollo/modules/canbus/conf/canbus_conf.pb.txt","Default canbus conf file");// 周期参数
DEFINE_double(chassis_freq, 100, "Chassis publish frequency (Hz)");// 命令输入检查
DEFINE_int64(min_cmd_interval, 5, "Minimum command interval (ms)");
DEFINE_int32(max_control_miss_num, 10, "Max control command miss number");
DEFINE_double(control_period, 0.01, "Control command period (s)");
DEFINE_int32(max_guardian_miss_num, 10, "Max guardian command miss number");
DEFINE_double(guardian_period, 0.01, "Guardian command period (s)");
DEFINE_bool(use_control_cmd_check, false, "Enable control command check");
DEFINE_bool(use_guardian_cmd_check, false, "Enable guardian command check");
DEFINE_double(estop_brake, 30.0, "Emergency stop brake percentage");// 消息发布
DEFINE_bool(enable_chassis_detail_pub, true,"Publish chassis detail message");
DEFINE_bool(enable_chassis_detail_sender_pub, false,"Publish chassis detail sender message");// 接收模式
DEFINE_bool(receive_guardian, false, "Receive guardian command");// 动态加载
DEFINE_string(load_vehicle_library, "", "Vehicle library path");
DEFINE_string(load_vehicle_class_name, "", "Vehicle class name");// 调试模式
DEFINE_bool(chassis_debug_mode, false, "Enable chassis debug mode");
DEFINE_double(pad_msg_delay_interval, 0.5, "Pad message delay threshold (s)");
8.4 命令行参数文件
文件路径:modules/canbus/conf/canbus.conf
--flagfile=modules/common/data/global_flagfile.txt# 配置文件路径
--canbus_conf_file=modules/canbus/conf/canbus_conf.pb.txt# 车型动态加载(Lincoln示例)
--load_vehicle_library=/apollo/bazel-bin/modules/canbus_vehicle/lincoln/liblincoln_vehicle_factory_lib.so
--load_vehicle_class_name=LincolnVehicleFactory# 消息发布控制
--enable_chassis_detail_pub=true
--enable_chassis_detail_sender_pub=false# 接收Guardian指令(安全模式)
--receive_guardian=false# 调试模式
--chassis_debug_mode=false
--use_control_cmd_check=false
--use_guardian_cmd_check=false# 紧急停车参数
--estop_brake=30.0# 发布频率
--chassis_freq=100
8.5 DAG配置
文件路径:modules/canbus/dag/canbus.dag
module_config {module_library: "/apollo/bazel-bin/modules/canbus/libcanbus_component.so"timer_components {class_name: "CanbusComponent"config {name: "canbus"flag_file_path: "/apollo/modules/canbus/conf/canbus.conf"interval: 10 # 10ms周期}}
}
九、实际应用案例
9.1 添加新车型支持
假设我们要添加一个名为"MyCar"的新车型:
步骤1:创建车型目录
mkdir -p modules/canbus_vehicle/mycar/protocol
mkdir -p modules/canbus_vehicle/mycar/proto
步骤2:定义Proto消息
mycar.proto:
syntax = "proto2";package apollo.canbus;message MyCar {optional Brake brake = 1;optional Throttle throttle = 2;optional Steering steering = 3;optional Gear gear = 4;message Brake {optional double brake_input = 1;optional double brake_output = 2;optional bool enabled = 3;}message Throttle {optional double throttle_input = 1;optional double throttle_output = 2;}message Steering {optional double steering_angle = 1;optional double steering_torque = 2;}message Gear {optional int32 gear_state = 1;}
}
步骤3:实现协议类
brake_cmd.h(发送ID: 0x100):
class BrakeCmd : public ProtocolData<MyCar> {public:static const int32_t ID = 0x100;BrakeCmd();uint32_t GetPeriod() const override {return 20 * 1000; // 20ms}void UpdateData(uint8_t *data) override {// 编码制动命令set_brake_enable_p(data, brake_enable_);set_brake_pedal_p(data, brake_pedal_);}void Reset() override {brake_enable_ = false;brake_pedal_ = 0.0;}BrakeCmd *set_enable(bool enable) {brake_enable_ = enable;return this;}BrakeCmd *set_pedal(double pedal) {brake_pedal_ = ProtocolData::BoundedValue(0.0, 100.0, pedal);return this;}private:void set_brake_enable_p(uint8_t *data, bool enable);void set_brake_pedal_p(uint8_t *data, double pedal);bool brake_enable_;double brake_pedal_;
};
brake_rpt.h(接收ID: 0x101):
class BrakeRpt : public ProtocolData<MyCar> {public:static const int32_t ID = 0x101;BrakeRpt();void Parse(const uint8_t *bytes, int32_t length,MyCar *chassis) const override {chassis->mutable_brake()->set_brake_input(brake_input(bytes, length));chassis->mutable_brake()->set_brake_output(brake_output(bytes, length));chassis->mutable_brake()->set_enabled(is_enabled(bytes, length));}private:double brake_input(const uint8_t *bytes, int32_t length) const;double brake_output(const uint8_t *bytes, int32_t length) const;bool is_enabled(const uint8_t *bytes, int32_t length) const;
};
步骤4:实现MessageManager
mycar_message_manager.h:
class MyCarMessageManager : public MessageManager<MyCar> {public:MyCarMessageManager() {// 注册发送协议AddSendProtocolData<BrakeCmd, true>();AddSendProtocolData<ThrottleCmd, true>();AddSendProtocolData<SteeringCmd, true>();AddSendProtocolData<GearCmd, true>();// 注册接收协议AddRecvProtocolData<BrakeRpt, true>();AddRecvProtocolData<ThrottleRpt, true>();AddRecvProtocolData<SteeringRpt, true>();AddRecvProtocolData<GearRpt, true>();}
};
步骤5:实现VehicleController
mycar_controller.h:
class MyCarController : public VehicleController<MyCar> {public:MyCarController() {}ErrorCode Init(const VehicleParameter ¶ms,CanSender<MyCar> *can_sender,MessageManager<MyCar> *msg_manager) override {params_ = params;can_sender_ = can_sender;message_manager_ = msg_manager;// 获取协议对象brake_cmd_ = dynamic_cast<BrakeCmd*>(message_manager_->GetMutableProtocolDataById(BrakeCmd::ID));throttle_cmd_ = dynamic_cast<ThrottleCmd*>(message_manager_->GetMutableProtocolDataById(ThrottleCmd::ID));// ...is_initialized_ = true;return ErrorCode::OK;}bool Start() override { return true; }void Stop() override {}Chassis chassis() override {// 从MessageManager获取最新数据MyCar mycar_data;message_manager_->GetSensorData(&mycar_data);// 转换为通用Chassis消息Chassis chassis;chassis.set_speed_mps(calculate_speed(mycar_data));chassis.set_brake_percentage(mycar_data.brake().brake_output());chassis.set_throttle_percentage(mycar_data.throttle().throttle_output());chassis.set_steering_percentage(mycar_data.steering().steering_angle());// ...return chassis;}private:void Emergency() override {brake_cmd_->set_enable(true)->set_pedal(100.0);}ErrorCode EnableAutoMode() override {// 发送使能命令brake_cmd_->set_enable(true);throttle_cmd_->set_enable(true);steering_cmd_->set_enable(true);set_driving_mode(Chassis::COMPLETE_AUTO_DRIVE);return ErrorCode::OK;}ErrorCode DisableAutoMode() override {brake_cmd_->set_enable(false);throttle_cmd_->set_enable(false);steering_cmd_->set_enable(false);set_driving_mode(Chassis::COMPLETE_MANUAL);return ErrorCode::OK;}void Brake(double brake) override {brake_cmd_->set_pedal(brake);}void Throttle(double throttle) override {throttle_cmd_->set_pedal(throttle);}void Steer(double angle) override {steering_cmd_->set_angle(angle);}void Gear(Chassis::GearPosition gear) override {gear_cmd_->set_gear(convert_gear(gear));}// ... 其他方法实现private:BrakeCmd *brake_cmd_ = nullptr;ThrottleCmd *throttle_cmd_ = nullptr;SteeringCmd *steering_cmd_ = nullptr;GearCmd *gear_cmd_ = nullptr;
};
步骤6:实现VehicleFactory
mycar_vehicle_factory.h:
class MyCarVehicleFactory : public AbstractVehicleFactory {public:MyCarVehicleFactory() {}bool Init(const CanbusConf *canbus_conf) override {// 1. 创建CanClientauto can_factory = CanClientFactory::Instance();can_factory->RegisterCanClients();can_client_ = can_factory->CreateObject(canbus_conf->can_card_parameter().brand());if (!can_client_->Init(canbus_conf->can_card_parameter())) {AERROR << "Failed to init CAN client";return false;}// 2. 创建MessageManagermessage_manager_.reset(new MyCarMessageManager());// 3. 创建CanSender和CanReceivercan_sender_.Init(can_client_.get(), message_manager_.get(),canbus_conf->enable_sender_log());can_receiver_.Init(can_client_.get(), message_manager_.get(),canbus_conf->enable_receiver_log());// 4. 创建VehicleControllervehicle_controller_.reset(new MyCarController());vehicle_controller_->Init(canbus_conf->vehicle_parameter(),&can_sender_, message_manager_.get());// 5. 添加发送消息can_sender_.AddMessage(BrakeCmd::ID,message_manager_->GetMutableProtocolDataById(BrakeCmd::ID), false);can_sender_.AddMessage(ThrottleCmd::ID,message_manager_->GetMutableProtocolDataById(ThrottleCmd::ID), false);// ...return true;}bool Start() override {if (can_client_->Start() != ErrorCode::OK) {AERROR << "Failed to start CAN client";return false;}if (can_sender_.Start() != ErrorCode::OK) {AERROR << "Failed to start CAN sender";return false;}if (can_receiver_.Start() != ErrorCode::OK) {AERROR << "Failed to start CAN receiver";return false;}if (!vehicle_controller_->Start()) {AERROR << "Failed to start vehicle controller";return false;}return true;}void Stop() override {can_sender_.Stop();can_receiver_.Stop();can_client_->Stop();vehicle_controller_->Stop();}void UpdateCommand(const ControlCommand *cmd) override {vehicle_controller_->Update(*cmd);}Chassis publish_chassis() override {return vehicle_controller_->chassis();}void PublishChassisDetail() override {// 发布MyCar详细信息}private:std::unique_ptr<CanClient> can_client_;CanSender<MyCar> can_sender_;CanReceiver<MyCar> can_receiver_;std::unique_ptr<MessageManager<MyCar>> message_manager_;std::unique_ptr<VehicleController<MyCar>> vehicle_controller_;
};// 注册到ClassLoader
CYBER_REGISTER_VEHICLE_FACTORY(MyCarVehicleFactory)
步骤7:配置文件
modules/canbus/conf/mycar_canbus.conf:
--load_vehicle_library=/apollo/bazel-bin/modules/canbus_vehicle/mycar/libmycar_vehicle_factory_lib.so
--load_vehicle_class_name=MyCarVehicleFactory
modules/canbus/conf/mycar_canbus_conf.pb.txt:
vehicle_parameter {max_enable_fail_attempt: 5driving_mode: COMPLETE_MANUAL
}can_card_parameter {brand: SOCKET_CAN_RAWtype: VIRTUALchannel_id: CHANNEL_ID_ZEROinterface: NATIVE
}enable_debug_mode: false
enable_receiver_log: true
enable_sender_log: true
9.2 调试技巧
9.2.1 启用CAN日志
# 在canbus.conf中设置
--enable_receiver_log=true
--enable_sender_log=true
日志输出示例:
[INFO] TX: id:0x100 data:00 64 01 00 00 00 00 0A
[INFO] RX: id:0x101 data:00 5F 00 5F 01 00 00 00
9.2.2 使用canbus_tester工具
编译:
bazel build //modules/canbus/tools:canbus_tester
使用:
./bazel-bin/modules/canbus/tools/canbus_tester \--can_card_parameter="brand:SOCKET_CAN_RAW channel_id:CHANNEL_ID_ZERO"
输出:
Sending brake command: 50%
Received brake feedback: input=50.2%, output=49.8%
9.2.3 使用candump监控(Linux)
# 监控can0接口
candump can0# 过滤特定ID
candump can0,100:7FF# 保存到文件
candump -l can0
9.2.4 调试模式
# 启用调试模式(跳过某些检查)
--chassis_debug_mode=true
十、总结与最佳实践
10.1 架构优势
- 分层设计:应用层、车型适配层、通信层、驱动层清晰分离
- 抽象工厂模式:轻松支持多车型,无需修改核心代码
- 模板方法模式:统一控制流程,车型特定逻辑由子类实现
- 线程安全:MessageManager使用mutex保护共享数据
- 实时性能:CanSender使用SCHED_FIFO实时调度,精确定时
- 可扩展性:新增车型只需实现工厂类和协议
10.2 开发最佳实践
10.2.1 协议开发规范
// ✅ 推荐:使用链式调用
brake_cmd_->set_enable(true)->set_pedal(50.0);// ✅ 推荐:使用BoundedValue限制范围
double value = ProtocolData::BoundedValue(0.0, 100.0, input);// ✅ 推荐:使用Byte工具类操作位
Byte frame(data);
frame.set_bit_1(3, data); // 设置bit 3// ❌ 避免:直接操作位(易出错)
data[0] |= (1 << 3);
10.2.2 线程安全
// ✅ 推荐:使用MessageManager提供的线程安全接口
MyCar data;
message_manager_->GetSensorData(&data);// ❌ 避免:直接访问共享数据
auto &data = message_manager_->sensor_data_; // 不安全!
10.2.3 错误处理
// ✅ 推荐:检查返回值
if (can_client_->Send(frames, &num) != ErrorCode::OK) {AERROR << "Send failed";// 错误恢复逻辑
}// ✅ 推荐:使用LOG_IF_EVERY_N避免日志风暴
LOG_IF_EVERY_N(ERROR, error_count++ > 10, 10)<< "Received " << error_count << " errors";
10.2.4 周期配置
// 常见CAN报文周期:
// - 关键控制(刹车、转向):10ms
// - 一般控制(油门、档位):20ms
// - 状态反馈:20ms
// - 传感器数据:100msuint32_t GetPeriod() const override {return 10 * 1000; // 10ms,单位:微秒
}
10.3 性能优化建议
- 批量接收:CanReceiver一次最多接收10帧,减少系统调用
- 实时线程:发送线程使用SCHED_FIFO,确保实时性
- 精确定时:基于时间戳计算休眠时间,避免累积误差
- 零拷贝:CAN帧数据直接操作,避免不必要的内存拷贝
- 日志控制:生产环境关闭详细日志,避免性能损失
10.4 常见问题排查
问题1:CAN通信无数据
排查步骤:
# 1. 检查CAN接口状态
ip link show can0# 2. 检查波特率配置
ip -details link show can0# 3. 使用candump测试
candump can0# 4. 检查Apollo日志
cyber_monitor # 查看/apollo/chassis是否有数据
问题2:控制命令不生效
排查步骤:
- 检查驾驶模式:
chassis.driving_mode == COMPLETE_AUTO_DRIVE - 检查使能状态:
chassis.brake_enabled == true - 检查超时:控制命令是否持续发送
- 检查驾驶员接管:
chassis.driver_override == false
问题3:底盘通信故障
原因分析:
// VehicleController::CheckChassisCommunicationError()
// 如果连续100个周期(1秒)未收到底盘反馈,报告通信错误if (lost_chassis_reveive_detail_count_ > 100) {is_chassis_communication_error_ = true;AERROR << "Chassis communication error";
}
解决方法:
- 检查CAN线缆连接
- 检查CAN波特率匹配
- 检查底盘电源状态
- 使用candump验证底盘是否发送数据
10.5 参考资源
Apollo官方文档:
- Canbus模块介绍:https://github.com/ApolloAuto/apollo/tree/master/modules/canbus
- CyberRT开发指南:https://cyber-rt.readthedocs.io/
CAN总线资源:
- CAN总线协议:ISO 11898
- Linux SocketCAN文档:https://www.kernel.org/doc/Documentation/networking/can.txt
相关工具:
- can-utils:Linux CAN调试工具集
- Kvaser CANking:Windows CAN分析工具
- PCAN-View:PEAK CAN调试工具
结语
Apollo Canbus模块体现了优秀的软件工程设计:
- 架构清晰:分层设计、职责分明
- 高度抽象:支持多车型、多CAN卡
- 实时可靠:精确定时、线程安全
- 易于扩展:新增车型只需实现接口
掌握Canbus模块的设计思想,不仅有助于理解Apollo系统,也为开发其他嵌入式实时通信系统提供了宝贵参考。
希望本文能够帮助你深入理解Apollo Canbus模块的技术细节!
作者:BruceWoo
日期:2025
关键词:Apollo、Canbus、CAN总线、自动驾驶、嵌入式系统、实时通信
如果觉得有帮助,欢迎点赞、收藏、关注!
