当前位置: 首页 > news >正文

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模块采用分层架构设计,自顶向下分为:

  1. 应用层modules/canbus/

    • CanbusComponent:主组件,基于CyberRT的TimerComponent
    • 处理控制命令、发布底盘状态
  2. 车型适配层modules/canbus_vehicle/

    • AbstractVehicleFactory:抽象工厂模式
    • VehicleController:车型特定的控制逻辑
    • MessageManager:车型特定的消息管理
  3. 通信层modules/drivers/canbus/can_comm/

    • CanSender:发送器(独立线程)
    • CanReceiver:接收器(独立线程)
    • MessageManager:消息管理与解析
    • ProtocolData:协议数据基类
  4. 驱动层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/controlControlCommand100Hz控制指令
订阅/apollo/guardianGuardianCommand100Hz安全指令(可选)
订阅/apollo/chassis_controlChassisCommand-外部底盘命令
发布/apollo/chassisChassis100Hz底盘状态
发布/apollo/chassis_detail车型特定100Hz详细底盘信息

4.2 VehicleController - 车辆控制器

文件路径:modules/canbus/vehicle/vehicle_controller.h

模板类

template <typename SensorType>
class VehicleController;
核心接口
// 初始化和生命周期
virtual ErrorCode Init(const VehicleParameter &params,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 &parameter) = 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 &param) 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 &param) 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 &param) 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-10-15pedal_cmduint160-65535制动踏板命令(0-100%)
216boo_cmdbool0-1制动请求
324enablebool0-1使能标志
325clear_overridebool0-1清除驾驶员接管
326ignore_overridebool0-1忽略接管
756-63watchdog_counteruint80-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-10-15pedal_inputuint160-65535制动踏板输入
2-316-31pedal_cmduint160-65535制动命令回显
4-532-47pedal_outputuint160-65535实际制动输出
648boo_inputbool0-1制动请求输入
649boo_cmdbool0-1制动请求回显
650boo_outputbool0-1实际制动状态
756enabledbool0-1系统使能状态
757driver_overridebool0-1驾驶员接管
758faultbool0-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 &params,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 架构优势

  1. 分层设计:应用层、车型适配层、通信层、驱动层清晰分离
  2. 抽象工厂模式:轻松支持多车型,无需修改核心代码
  3. 模板方法模式:统一控制流程,车型特定逻辑由子类实现
  4. 线程安全:MessageManager使用mutex保护共享数据
  5. 实时性能:CanSender使用SCHED_FIFO实时调度,精确定时
  6. 可扩展性:新增车型只需实现工厂类和协议

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 性能优化建议

  1. 批量接收:CanReceiver一次最多接收10帧,减少系统调用
  2. 实时线程:发送线程使用SCHED_FIFO,确保实时性
  3. 精确定时:基于时间戳计算休眠时间,避免累积误差
  4. 零拷贝:CAN帧数据直接操作,避免不必要的内存拷贝
  5. 日志控制:生产环境关闭详细日志,避免性能损失

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:控制命令不生效

排查步骤

  1. 检查驾驶模式:chassis.driving_mode == COMPLETE_AUTO_DRIVE
  2. 检查使能状态:chassis.brake_enabled == true
  3. 检查超时:控制命令是否持续发送
  4. 检查驾驶员接管: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";
}

解决方法

  1. 检查CAN线缆连接
  2. 检查CAN波特率匹配
  3. 检查底盘电源状态
  4. 使用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总线、自动驾驶、嵌入式系统、实时通信

如果觉得有帮助,欢迎点赞、收藏、关注!

http://www.dtcms.com/a/520234.html

相关文章:

  • DeepSeek-OCR 嵌入dify工作流
  • Linux小课堂: Vim与Emacs之Linux文本编辑器的双雄格局及Vim安装启动详解
  • 江宁外贸网站建设国内付费代理ip哪个好
  • 多种大连网站建设景观设计公司理念
  • KP201FLGA电机驱动电源方案SOT23-6恒压恒流恒功率电路原理图分析
  • Hadoop报错 Couldn‘t find datanode to read file from. Forbidden
  • 【案例实战】HarmonyOS分布式购物车:多设备无缝协同的电商体验
  • OpenCV工程中直接包含调用vs2022
  • 怎么看一个网站用什么做的北京建设公司有哪些
  • 上海交大刘鹏飞:智能不在于数据堆砌,78个样本训练出超强Agent,数据效率提升128倍
  • SpringAI1-快速⼊⻔
  • 本地局域网邮件管理系统:从原理到实现的完整指南
  • 面向小样本蜂窝网络故障诊断的模型与知识互增强方法
  • 上海网站推广方法河北石家庄属于几线城市
  • 专业购物网站建设哪家好免费找客户网站
  • 受欢迎的网站开发php源码搭建网站流程
  • 第八章 排序——课后习题解练【数据结构(c语言版 第2版)】
  • 如果有大量的key需要设置同一时间过期,一般需要注意什么?
  • 【nvidia-GB200】(2) 18 台 GB200 服务器集群 NCCL All-to-All 性能深度测评:72 张 GPU 多对多通信的效率与潜力
  • MYSQL数据库--基本练习
  • Harbor VS Hadess,开源制品管理工具一文详细对比分析
  • 查找企业信息的网站哪个好广州网站外包
  • 基于3D激光点云的障碍物检测与跟踪---(3)基于匈牙利算法的障碍物跟踪
  • 虚拟化技术实践指南:KVM 与 VMware ESXi 部署全流程
  • 你的地图,你做主!视频汇聚平台EasyCVR解锁多源地图自由,打造监控“全域一张图”
  • Prometheus(四)—— Alertmanager完整部署指南:邮件+钉钉告警全流程落地
  • Fakebook.
  • (11)(2.1.7) FETtec OneWire ESCs
  • 红⿊树实现
  • HTML DOM 简介