文章目录
- 1. 如何解析CAN消息
-
- 1.1 简介
- 1.2 python-can库使用
- 2. python-can库介绍
-
- 2.1 完整解析流程
- 2.2 简单示例
- 3. 总结与坑
- 4. 代码示例
-
- 1. 解析一个DBC
- 2. 生成一个DBC
- 3. 解析.asc数据 保存为.csv格式
1. 如何解析CAN消息
关于CAN的基础知识,可阅读如下链接:
- CAN协议详解
- CAN消息解析
此部分参考自:https://zhuanlan.zhihu.com/p/635594421
1.1 简介
用python分析asc,blf等格式
数据,python-can
这个包是必备的,cantools
这个包虽然理论上非必须,但用它处理起来更方便。另外cantools这个包本身依赖python-can,所以直接装个cantools就能直接上手干了。
pip install python-can
pip install cantools
另外,要分析asc, blf格式的数据,还得需要相应的数据库文件
,也就是.dbc
或.arxml
文件。DBC(Database Container)是一种CAN数据定义文件,用于描述车辆网络系统中的消息、信号和节点之间的关系和通信规则
。在Python中,我们可以使用第三方库cantools来读取和解析DBC文件,以便在开发过程中使用。
下面是以blf格式数据为例展示解析方法,asc格式数据类似。
1.2 python-can库使用
按官网的说法,python-can这个工具非常强大,不仅能离线的分析CAN数据,还能通过在线的方式与Vector工具箱、SocketCAN、PCAN等主流工具直接联动,把在线数据灌入python里进行在线运算与分析,只要写一下配置文件就行。
如果是离线分析CAN数据的话,这个工具也是简单到离谱:python-can直接提供一个.BLFReader函数(或.ASCReader(asc_path)),输入blf/asc文件名,直接输出结构化的CAN数据,简单粗暴。
import can
blf_data = can.BLFReader("log1.blf")
#asc_data = can.ASCReader("xxx.asc")
for msg in blf_data:
print(msg)
以上代码中,blf_data = can.BLFReader(...)
实际上是建立一个can.BLFReader类
,本身是不怎么耗时的。
下面的for循环实际上是调用的BLFReader类的__iter__方法,得到的msg是一个can.message类
。由于can.message类重写了__str__方法,因此我们在调用print(msg)时,就会自动把message里的内容格式化输出。格式化输出的格式和Vector工具箱中的Trace几乎完全一样,因此即使用惯了Vector工具箱的人看起来也毫无压力。
更细化的,我们可以翻到can.message类__str__方法的实现代码,具体看一下我们print(msg)时,print出的究竟是什么东西。
我们在print(msg)
时,输出格式里从左到右分别是:
- 这条msg的时间戳
- 这条msg的can_id
- 这条msg的属性(比如是否是canfd帧、是否是错误帧、是rx还是tx帧等等)
- 这条msg的长度(也就是dlc)
- 以16进制格式输出的raw data
- 这条msg的can channel
由于我们没有用dbc或arxml文件去解析msg里的数据,因此也只能打印出raw data,打印不出来具体的信号值
。
另外,由于msg本身就是一个can.message类,因此我们也可以手动取到它的各种属性,比如:
比较常用的有:
- msg.channel —— channel,取决于录制blf时的channel mapping
- msg.timestamp —— 录制blf时记录的本条数据时间戳
- msg.arbitration_id —— can id,或者叫frame id
- msg.data —— bytearray格式的raw data
raw data如果直接打印的话,默认是按bytearray格式
打印的。比如,我们可以筛选出channel=2,id=0x51的数据帧
,打印出它们的raw data:
import can
blf_data = can.BLFReader("log1.blf")
for msg in blf_data:
if msg.channel == 2 and msg.arbitration_id == 0x51:
print(msg.data)
更进一步的,如果我们知道相应的数据帧中的每一位的定义,也可以自己筛出我们需要的信号的物理值
。
比如这里的channel=2,id=0x51的数据帧,我们通过其他工具(比如Vector Autosar Explorer
)翻到其内部定义,可以看到它第2个byte中的后4个bit,以及第3个byte中的第4-6个bit都是某个有物理意义的值
:
然后我们就可以按需把这个物理值取出来:
import can
blf_data = can.BLFReader("log1.blf")
for msg in blf_data: