Canbus模块介绍
我们先看下什么是Canbus
: 控制器局域网 (Controller Area Network
,简称CAN
或者CAN bus
) 是一种车用总线标准。被设计用于在不需要主机(Host
)的情况下,允许网络上的节点相互通信。采用广播机制,并利用标识符来定义内容和消息的优先顺序,使得canbus
的扩展性良好,同时不基于特殊类型(Host
)的节点,增加了升级网络的便利性。
这里的Canbus
模块其实可以称为Chassis
模块,主要的作用是反馈车当前的状态(航向,角度,速度等信息),并且发送控制命令到车线控底盘,可以说Canbus
模块是车和自动驾驶软件之间的桥梁。由于这个模块和"drivers/canbus"
的联系紧密,因此也一起在这里介绍。Canbus
模块是车和自动驾驶软件之间的桥梁,通过canbus
驱动(drivers/canbus
)来实现将车身信息发送给Apollo
上层软件,同时接收控制命令,发送给汽车线控底盘实现对汽车的控制。
那么canbus
模块的输入是什么?输出是什么呢?
可以看到canbus
模块:
- 输入 - 1.
ControlCommand
(控制命令) - 输出 - 1.
Chassis
(汽车底盘信息), 2.ChassisDetail
(汽车底盘信息详细信息)
Canbus
一方面接收Control
模块发布的指令,然后将指令解析为CAN
协议报文通过can
总线传递给车上的各个控制单元;另一方面从can
总线上获取数据并且将信息解析为底盘信息,然后把消息发布出去(反馈底盘信息)。
Canbus
模块的目录结构如下:
接着我们来分析下Canbus
模块的执行流程。
Canbus(主模块)
Canbus
模块的主流程在文件"canbus_component.cc"
中,Canbus
模块为定时触发,每10ms执行一次,发布chassis
信息,而ControlCommand
则是每次读取到之后触发回调"OnControlCommand"
,发送"control_command"
到线控底盘。
1 | bool CanbusComponent::Proc() { |
由于不同型号的车辆的canbus
命令不一样,在"/vehicle"
中适配了不同型号车辆底盘协议的canbus
消息格式,所有的车都继承自Vehicle_controller
基类,通过对Vehicle_controller
的抽象来发送和读取canbus
信息。
车辆工厂模式(VehicleFactory)
在vehicle
中可以适配不同的车型,而每种车型都对应一个vehicle_controller
,创建每种车辆的控制器(VehicleController
)和消息管理(MessageManager
)流程如下:
VehicleFactory
类通过创建不同的类型AbstractVehicleFactory
,每个车型自己的Factory
在创建出对应的VehicleController
和MessageManager
。
用林肯来举例子就是: VehicleFactory
创建LincolnVehicleFactory
,之后通过CreateMessageManager
和CreateVehicleController
创建对应的控制器(LincolnController
)和消息管理器(LincolnMessageManager
)。
上述代码流程用到了设计模式的工厂模式,通过车辆工厂创造不同的车辆类型。
车辆控制器(LincolnController)
下面以林肯来介绍LincolnController
,以及如何接收chassis
信息,其它的车型可以以此类推。
可以看上面的整体流程图lincoln_controller.cc
部分,显然,controller
分为三部分init->start->stop
,init
过程是获取MessageManager
的protocolData
,即发送的消息协议数据类型,然后通过CanSender
发送这些消息。如以int
为协议来代表的是整数,以char
为协议代表的字符。那这个protocolData
就是我们以这个类型为协议来替代车辆行驶的具体操作。例如在modules/canbus/vehicle/brake_60.h
这个Brake60
类的操作就是刹车。start
之后启动一个看门狗,检查canbus
消息格式是否正确,最后stop
模块则是结束看门狗进程。
Canbus(驱动程序)
上层的canbus
就介绍完成了,而canbus
的发送(CanSender)
和接收(CanReceiver)
,还有消息管理(MessageManager)
都是在"drivers/canbus"
中实现的。
消息管理器(MessageManager)
MessageManager
,顾名思义就是用于管理所有的msg,主要作用是解析和保存canbus
数据。而具体的接收和发送则是在"CanReceiver"
和"CanSender"
中。使用前需要将所有用户自定义的ProtocolData
初始化并将其分为send
和receive
类加入各自队列中,包含一个Parse
接口,会逐一调用所有receive
类ProtocolData
对象的Parse
接口,实现将接受到的CanFrame
数据转化为自定义数据类型(例如0x6b的帧中数据为车辆加速度数据,将数据解析到自定义的ChassisDetail
中的lateral_acceleration
、longitudinal_acceleration
、vertical_acceleration
变量中),底层有一个unordered_map
用于根据id
搜索加入的ProtocolData
对象,用于将send
类对象提取出来供CanSender
使用。
拿接收消息举例子,也就是说CanReceiver
收到消息后,会调用MessageManager
中的Parse
去解析消息,消息的解析协议在"modules/canbus/vehicle/lincoln/protocol"
中,每个消息把自己对应的信息塞到"chassis_detail"
中完成了消息的接收。
消息接收(CanReceiver)
CanReceiver
中的"Start"
调用"RecvThreadFunc"
实现消息的接收,这里会启动一个异步进程去完成接收。
1 | template <typename SensorType> |
RecvThreadFunc
通过"can_client_"
接收消息,然后通过"MessageManager"
去解析消息,在MessageManager
中有讲到。
1 | template <typename SensorType> |
消息发送(CanSender)
消息发送对应的是在CanSender
中的"Start"
调用"PowerSendThreadFunc"
,我们可以看具体实现:
1 | template <typename SensorType> |
PowerSendThreadFunc
再通过"can_client"
发送消息:
1 | std::vector<CanFrame> can_frames; |
canbus客户端(CanClient)
CanClient
是canbus
客户端,同时也是canbus
的驱动程序,针对不同的canbus
卡,对发送和接收进行封装,并且提供给消息发送和接收控制器使用。
拿"EsdCanClient"
来举例子,"Send"
函数发送can
消息,调用的是第三方的硬件驱动canWrite
,"Receive"
函数接收can
消息,调用的是第三方的硬件驱动canRead
。其他的can卡可以参考上述的流程,至此整个canbus
驱动就分析完成了。