CAN 之 DBC 文件解析(简单版)
felicx 化神

什么是 DBC 文件

DBC 是Database Can的缩写,其代表的是CAN的数据库文件。简单地说,通过 CAN 总线通信的数据类型可以用 DBC 文件来读取和理解。一般通过 Vector CANdb++ 进行编辑和查看。

DBC 文件内容

当我们打开一份 DBC 文件时,左侧树形目录对应

  • Networks:从网络的视角来观察总线上存在的节点、报文和信号
  • ECUs:每个 ECU 的 Network Node 和 Environment Variables
    • Environment Variables:在使用 CANoe 仿真节点时会用到
    • Network nodes:从单个节点的视角来观察与节点相关的总线报文及信号
      • Tx Messages:节点发送报文
      • Rx Messages:节点接收报文
      • Mapped Tx Signals:节点发送信号
      • Mapped Rx Signals:节点接收信号
  • Messages:CAN 总线上传输信息的最小单位
  • Signals:Message 里最小的元素单位

值得一提的是,Messages 与 Signals 下包含的报文信号比 Network nodes 下的收发报文信号要多,并且有可能会包含实际没用到的报文信号,因为所有创建的报文信号都会在 Messages 与 Signals 下显示,但只有映射到节点的报文信号才会在 Network nodes 下显示。

image

因此我们主要看 Network nodes 下里的网络节点,网络节点分为多个类,如下图,分为 SDE、sensor2、sensor3 三个类节点。

image

当点开节点时,会看到类节点下的 TX 与 RX 下的多个 messages。

  • ID:CAN message id,一般以16进制显示
  • Name: CAN message name
  • ID-Format:CAN 报文的类型(CAN Standard/CAN FD Standard)
  • DLC:数据长度代码,CAN 报文中数据的长度

image

每个 message 又包含了多个 signals。

  • Name:信号的名称
  • Multiplexing:多路复用
  • Startbit:信号的起始位
  • Byte Order:信号的位计数,分为 Motorols(大端字节序)和 Intel(小端字节序)
  • Value Type:数据类型
  • Factor:信号的转换系数
  • Offset:信号的转换偏移
  • Minimum & Maximum:最小和最大的信号值
  • Unit:信号中存在的物理数据的单位
  • Comment:信号说明

image

其中 factor 因数 与 offse 偏移量 定义了 physical 物理值(如vehicle speed,engine speed,water temperature)与 raw 原始值(总线上传输的值)的线性转换规则。

1
2
physical_value = raw_value * factor + offset
raw_value = ( pysical_value - offset ) / factor

DBC 文件解析

这里使用 Python。首先安装 cantools 这个库文件,可以直接解析 dbc 文件。

1
pip3 install cantools

使用时,直接 import cantools 即可。

1
2
3
import cantools
dbc_file = "******"
dbc_info = cantools.db.load_file(dbc_file) #创建一个dbc数据库对象

直接使用时,下述附代码,可直接复制使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import cantools

class DbcInfo:
"""
用于读取dbc文件,返回dbc信息
"""
def __init__(self, input_file):
"""
init
"""
self.dbc_file = input_file
self.dbc_info = cantools.db.load_file(self.dbc_file)

def get_message(self, frame_id):
"""
return message,特别强调,frame_id输入10进制即可,同时必须是int型数据,不能是字符串
"""
return self.dbc_info.get_message_by_frame_id(frame_id) #直接返回message的所有信息

def get_message_name(self, frame_id):
"""
return message name
"""
return self.dbc_info.get_message_by_frame_id(frame_id).name

def get_message_name_by_message_id(self, frame_name):
"""
return message id by message name
"""
return self.dbc_info.get_message_by_name(frame_name).frame_id

def get_signals_list(self, frame_id):
"""
return signals list
"""
return self.dbc_info.get_message_by_frame_id(frame_id).signal_tree #这里返回的是message中signal的list,包含一个message中所有的signal

def get_signal(self, frame_id, signal_name):
"""
return signal, 输入message id和signal name
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name)

def get_signal_config_maximum(self, frame_id, signal_name):
"""
return signal maximum
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name).maximum

def get_signal_config_minimum(self, frame_id, signal_name):
"""
return signal minimum
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name).minimum

def get_signal_config_scale(self, frame_id, signal_name):
"""
return signal scale,这里指的是signal定义中其取值范围的间隔,
比如从1到10,每个2取一个值,scale就是2
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name).scale

def get_signal_config_comment(self, frame_id, signal_name):
"""
return signal comment,signal说明
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name).comment

def signal_config_value_description_to_num(self, frame_id, signal_name, value_str):
"""
return signal value description
这里需要特别说明下,有些signal取值不是正常的数值,而是文字描述,
这个在定义中是个表,比如1:open, 2:close, 4:ignore,
本函数的意思是当你取到这个字符串,比如close时,能够返回这个字符串在对应中的数字2,
这在解析和保存以及后续发送中是很有用的。
"""
return self.dbc_info.get_message_by_frame_id(frame_id).get_signal_by_name(signal_name).\
choice_string_to_number(value_str)


if __name__ == '__main__':
dbc_info = DbcInfo('D:test.dbc')
#test,根据自己需求使用上述函数即可
#frame_id输入10进制即可,同时必须是int型数据,不能是字符串
print(dbc_info.get_message_name(520))

如果想进行 can 报文修改,需要注意的是,直接给的数据不一定符合要求,需要保证修改数值在 signal 的最大最小范围内,同时保证该值符合定义中的取值间隔,可采用下述函数进行 check,保证输入数值符合要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def check_value_meet_norms(self, frame_id, signal_name, value):
"""
check value meet norms and return closest value based on scale factor
检查输入是否符合规范,同时输出符合规范的最接近输入value值的value
如果你要用脚本修改can报文,但是修改的值如果不符合要求,给入会报错,
该函数保证修改值符合规范且接近用户期望值
"""
max_value = self.get_signal_config_maximum(frame_id, signal_name)
min_value = self.get_signal_config_minimum(frame_id, signal_name)
scale_value = self.get_signal_config_scale(frame_id, signal_name)
if value > max_value:
if isinstance(scale_value, int):
return int(max_value)
return max_value
elif value < min_value:
if isinstance(scale_value, int):
return int(max_value)
return min_value
else:
num = round(value / scale_value)
if isinstance(scale_value, int):
return int(num * scale_value)
return num * scale_value

参考

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
访客数 访问量