CAN通讯进阶-基于Python使用dbc文件解析CAN数据(dbc文件的创建、使用方法)

配置环境

使用环境:python3.6
平台:Windows10
IDE:PyCharm

1.环境配置

1.1安装CAN通讯需要的包

pip install canlib

1.2 安装kvaser_drivers以及Kvaser CanKing

这些软件可以在官网下载https://www.kvaser.com/download/

官网是英文的,看起来可能有点吃力,这里也给出云盘连接:
链接:https://pan.baidu.com/s/1LDKyIlOV0Ky4d2qxryLZGQ
提取码:vwiv

下载下来之后的文件是这样的,三个文件依次双击安装就行了

在这里插入图片描述
安装完后,在开始栏可以查看到已经安装好了
在这里插入图片描述

2.使用DBC文件解析CAN帧

2.1DBC文件

DBC是Database Can的缩写,其代表的是CAN的数据库文件,在这个文件中把CAN通讯的信息定义的非常完整清楚,在Windows下其存在的格式便是dbc,如图所示:
在这里插入图片描述
有了DBC文件便可以对接收到的CAN帧进行解析,分析一系列CAN帧代表的真实物理含义。
正因为如此,每一个DBC文件都对应着一种CAN帧的规则,使用CAN作为通讯手段的设备(如汽车中发动机、车灯、车载空调等等)都有对应的DBC文件。

2.2本博客布局

本博客将从DBC文件的创建、使用DBC文件发送指定格式的CAN数据、使用DBC文件解析接收到的CAN数据来展开。

3.DBC文件的创建

3.1创建DBC文件代码

# author:Hurricane
# date:  2021/4/16
# File : CAN_Create_Database.py 
# E-mail:hurri_cane@qq.com



import argparse
from collections import namedtuple

from canlib import kvadblib

Message = namedtuple('Message', 'name id dlc signals')
Signal = namedtuple('Signal', 'name size scaling limits unit')
EnumSignal = namedtuple('EnumSignal', 'name size scaling limits unit enums')

_messages = [
	Message(
		name='EngineData',
		id=100,
		dlc=8,
		signals=[
			Signal(
				name='PetrolLevel',
				size=(24, 8),
				scaling=(1, 0),
				limits=(0, 255),
				unit="l",
			),
			Signal(
				name='EngPower',
				size=(48, 16),
				scaling=(0.01, 0),
				limits=(0, 150),
				unit="kW",
			),
			Signal(
				name='EngForce',
				size=(32, 16),
				scaling=(1, 0),
				limits=(0, 0),
				unit="N",
			),
			EnumSignal(
				name='IdleRunning',
				size=(23, 1),
				scaling=(1, 0),
				limits=(0, 0),
				unit="",
				enums={'Running': 0, 'Idle': 1},
			),
			Signal(
				name='EngTemp',
				size=(16, 7),
				scaling=(2, -50),
				limits=(-50, 150),
				unit="degC",
			),
			Signal(
				name='EngSpeed',
				size=(0, 16),
				scaling=(1, 0),
				limits=(0, 8000),
				unit="rpm",
			),
		]),
	Message(
		name='GearBoxInfo',
		id=1020,
		dlc=1,
		signals=[
			Signal(
				name='EcoMode',
				size=(6, 2),
				scaling=(1, 0),
				limits=(0, 1),
				unit="",
			),
			EnumSignal(
				name='ShiftRequest',
				size=(3, 1),
				scaling=(1, 0),
				limits=(0, 0),
				unit="",
				enums={'Shift_Request_On': 1, 'Shift_Request_Off': 0},
			),
			EnumSignal(
				name='Gear',
				size=(0, 3),
				scaling=(1, 0),
				limits=(0, 5),
				unit="",
				enums={
					'Idle': 0,
					'Gear_1': 1,
					'Gear_2': 2,
					'Gear_3': 3,
					'Gear_4': 4,
					'Gear_5': 5,
				},
			),
		]),
]


def create_database(name, filename):
	db = kvadblib.Dbc(name=name)

	for _msg in _messages:
		message = db.new_message(
			name=_msg.name,
			id=_msg.id,
			dlc=_msg.dlc,
		)

		for _sig in _msg.signals:
			if isinstance(_sig, EnumSignal):
				_type = kvadblib.SignalType.ENUM_UNSIGNED
				_enums = _sig.enums
			else:
				_type = kvadblib.SignalType.UNSIGNED
				_enums = {}
			message.new_signal(
				name=_sig.name,
				type=_type,
				byte_order=kvadblib.SignalByteOrder.INTEL,
				mode=kvadblib.SignalMultiplexMode.MUX_INDEPENDENT,
				size=kvadblib.ValueSize(*_sig.size),
				scaling=kvadblib.ValueScaling(*_sig.scaling),
				limits=kvadblib.ValueLimits(*_sig.limits),
				unit=_sig.unit,
				enums=_enums,
			)

	db.write_file(filename)
	db.close()


if __name__ == '__main__':
	parser = argparse.ArgumentParser(
		description="Create a database from scratch.")
	parser.add_argument('--filename', default=r'..\engine_example.dbc', help=(
		"The filename to save the database to."))
	parser.add_argument('-n', '--name', default='Engine example', help=(
		"The name of the database (not the filename, the internal name."))
	args = parser.parse_args()

	create_database(args.name, args.filename)


3.2使用方法

代码中if __name__ == '__main__':下:

parser.add_argument('--filename', default=r'..\engine_example.dbc', help=(
		"The filename to save the database to."))

指定的filename为创建的DBC文件的路径以及文件名,本代码中便是将DBC文件存在代码所在目录的上层目录中,以engine_example.dbc文件名保存

代码中_messages列表
在这里插入图片描述
便是定义CAN帧解析的规则,可以定义:

  • CAN帧的id

  • CAN帧的数据长度

  • CAN帧的解析single规则
    等等…

    创建方式如下:

在这里插入图片描述

4.DBC文件发送指定格式的CAN数据

4.1DBC文件发送指定格式的CAN数据代码

# author:Hurricane
# date:  2021/4/12
# File : CAN_Random_Send.py 
# E-mail:hurri_cane@qq.com

import argparse
import time
import random

from canlib import canlib, kvadblib


bitrates = {
    '1M': canlib.canBITRATE_1M,
    '500K': canlib.canBITRATE_500K,
    '250K': canlib.canBITRATE_250K,
    '125K': canlib.canBITRATE_125K,
    '100K': canlib.canBITRATE_100K,
    '62K': canlib.canBITRATE_62K,
    '50K': canlib.canBITRATE_50K,
    '83K': canlib.canBITRATE_83K,
    '10K': canlib.canBITRATE_10K,
}


# 随机在dbc结构中抽取一个结构
def set_random_framebox_signal(db, framebox, signals):
    sig = random.choice(signals)
    value = get_random_value(db, sig)
    framebox.signal(sig.name).phys = value

# 随机在抽取到的结构帧的数值范围中产生一个值
def get_random_value(db, sig):
    limits = sig.limits
    value = random.uniform(limits.min, limits.max)

    # round value depending on type...
    if (
            sig.type is kvadblib.SignalType.UNSIGNED or
            sig.type is kvadblib.SignalType.SIGNED
    ):
        # ...remove decimals if the signal was of type unsigned
        value = int(round(value))
    else:
        # ...otherwise, round to get only one decimal
        value = round(value, 1)

    return value


def ping_loop(channel_number, db_name, num_messages, quantity, interval, bitrate, seed=0):
    db = kvadblib.Dbc(filename=db_name)

    ch = canlib.openChannel(channel_number, canlib.canOPEN_ACCEPT_VIRTUAL)
    ch.setBusOutputControl(canlib.canDRIVER_NORMAL)
    ch.setBusParams(bitrate)
    ch.busOn()

    random.seed(seed)

    if num_messages == -1:
        # used_messages为dbc文件定义的所有帧结构
        used_messages = list(db)
    else:
        used_messages = random.sample(list(db), num_messages)

    print()
    print("Randomly selecting signals from the following messages:")
    print(used_messages)
    print("Seed used was " + repr(seed))
    print()

    while True:
        # Create an empty framebox each time, ignoring previously set signal
        # values.
        framebox = kvadblib.FrameBox(db)

        # Add all messages to the framebox, as we may use send any signal from
        # any of them.
        for msg in db:
            framebox.add_message(msg.name)

        # Make a list of all signals (which framebox has found in all messages
        # we gave it), so that set_random_framebox_signal() can pick a random
        # one.
        signals = [bsig.signal for bsig in framebox.signals()]

        # Set some random signals to random values
        for i in range(quantity):
            set_random_framebox_signal(db, framebox, signals)

        # Send all messages/frames
        for frame in framebox.frames():
            print('Sending frame', frame)
            ch.writeWait(frame, timeout=5000)

        time.sleep(interval)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description="Send random CAN message based on a database.")
    parser.add_argument('channel', type=int, default=0, nargs='?', help=(
        "The channel to send messages on."))
    parser.add_argument('--bitrate', '-b', default='500k', help=(
        "Bitrate, one of " + ', '.join(bitrates.keys())))
    parser.add_argument('--db', default="../Mobileye.dbc", help=(
        "The database file to base messages on."))
    parser.add_argument('-Q', '--quantity', type=int, default=5, help=(
        "The number of signals to send each tick."))
    parser.add_argument('-I', '--interval', type=float, default=0.2, help=(
        "The time, in seconds, between ticks."))
    parser.add_argument('-n', '--num-messages', type=int, default=-1, help=(
        "The number of message from the database to use, or -1 to use all."))
    parser.add_argument('-s', '--seed', nargs='?', default='0', help=(
        "The seed used for choosing messages. If possible, will be converted to an int. If no argument is given, a random seed will be used."))
    args = parser.parse_args()

    if args.seed is None:
        seed = None
    else:
        try:
            seed = int(args.seed)
        except ValueError:
            seed = args.seed

    ping_loop(
        channel_number=args.channel,
        db_name=args.db,
        num_messages=args.num_messages,
        quantity=args.quantity,
        interval=args.interval,
        bitrate=bitrates[args.bitrate.upper()],
        seed=args.seed,
    )

4.2使用方法

这份代码的功能为:
以DBC文件规定的数据格式,随机发送一些数据帧出去

代码中if __name__ == '__main__':下:

    parser.add_argument('--db', default="../Mobileye.dbc", help=(
        "The database file to base messages on."))

指定的filename为读取的DBC文件的路径,本代码中读取的DBC文件存在代码所在目录的上层目录中,以Mobileye.dbc文件名存在

PS:这里面的DBC文件也可以改为3.1创建DBC文件代码中生成的engine_example.dbc文件

此处附上两个DBC文件的云盘链接

链接:https://pan.baidu.com/s/1JAT2o2fPzto8555qU-lVKg
提取码:49bv

为了测试发送数据是否成功,采用上篇博客2.2.1使用Kvaser Can King接收数据中的方法。链接如下:
https://blog.csdn.net/ShakalakaPHD/article/details/115767739

运行4.1代码:
在这里插入图片描述

可以看到Kvaser Can King接收到各式各样的数据,但是其中的具含义我们并不清楚,这边涉及到第5节,使用DBC文件解析接收到的CAN帧。

5.使用DBC文件解析接收到的CAN数据

5.1使用DBC文件解析接收到的CAN数据代码

# author:Hurricane
# date:  2021/4/16
# File : CAN_Using_Database.py
# E-mail:hurri_cane@qq.com

import argparse

from canlib import canlib, kvadblib


bitrates = {
    '1M': canlib.canBITRATE_1M,
    '500K': canlib.canBITRATE_500K,
    '250K': canlib.canBITRATE_250K,
    '125K': canlib.canBITRATE_125K,
    '100K': canlib.canBITRATE_100K,
    '62K': canlib.canBITRATE_62K,
    '50K': canlib.canBITRATE_50K,
    '83K': canlib.canBITRATE_83K,
    '10K': canlib.canBITRATE_10K,
}


def printframe(db, frame):
    try:
        bmsg = db.interpret(frame)
    except kvadblib.KvdNoMessage:
        print("<<< No message found for frame with id %s >>>" % frame.id)
        return

    msg = bmsg._message

    # form = '═^' + str(width)
    # print(format(" %s " % msg.name, form))

    print('┏', msg.name)

    if msg.comment:
        print('┃', '"%s"' % msg.comment)

    for bsig in bmsg:
        print('┃', bsig.name + ':', bsig.value, bsig.unit)

    print('┗')


def monitor_channel(channel_number, db_name, bitrate, ticktime):
    db = kvadblib.Dbc(filename=db_name)

    ch = canlib.openChannel(channel_number, canlib.canOPEN_ACCEPT_VIRTUAL)
    ch.setBusOutputControl(canlib.canDRIVER_NORMAL)
    ch.setBusParams(bitrate)
    ch.busOn()

    timeout = 0.5
    tick_countup = 0
    if ticktime <= 0:
        ticktime = None
    elif ticktime < timeout:
        timeout = ticktime

    print("Listening...")
    while True:
        try:
            frame = ch.read(timeout=int(timeout * 1000))
            printframe(db, frame)
        except canlib.CanNoMsg:
            if ticktime is not None:
                tick_countup += timeout
                while tick_countup > ticktime:
                    print("tick")
                    tick_countup -= ticktime
        except KeyboardInterrupt:
            print("Stop.")
            break

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description="Listen on a CAN channel and print all signals received, as specified by a database.")
    parser.add_argument('channel', type=int, default=1, nargs='?', help=(
        "The channel to listen on."))
    parser.add_argument('--db', default="../Mobileye.dbc", help=(
        "The database file to look up messages and signals in."))
    parser.add_argument('--bitrate', '-b', default='500k', help=(
        "Bitrate, one of " + ', '.join(bitrates.keys())))
    parser.add_argument('--ticktime', '-t', type=float, default=0, help=(
        "If greater than zero, display 'tick' every this many seconds"))
    args = parser.parse_args()

    monitor_channel(args.channel, args.db, bitrates[args.bitrate.upper()], args.ticktime)

5.2使用方法

代码中if __name__ == '__main__':下:

    parser.add_argument('--db', default="../Mobileye.dbc", help=(
        "The database file to look up messages and signals in."))

指定的filename为读取的DBC文件的路径,本代码中读取的DBC文件存在代码所在目录的上层目录中,以Mobileye.dbc文件名存在

PS:这里面的DBC文件也可以改为3.1创建DBC文件代码中生成的engine_example.dbc文件

为了测试解析数据是否有效,采用此博客中4.DBC文件发送指定格式的CAN数据节下的随机发送数据的代码发送数据。

运行4.1节代码后,再运行5.1节代码来解析数据:

在这里插入图片描述
可以看到相较于Kvaser Can King接收到各式各样的数据,使用DBC文件解析后的数据都已准换为了真实的物理含义:
在这里插入图片描述

6.参考文献

Python Canlib Documentation
file:///D:/Program%20Files%20(x86)/kvaserCAN/canlib/python/pycanlib/docs/index.html

7.结束语

如果本文对你有帮助的话还请点赞、收藏一键带走哦,你的支持是我最大的动力!(づ。◕ᴗᴗ◕。)づ
在这里插入图片描述

  • 55
    点赞
  • 247
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
### 回答1: Python可以使用相应的库来编辑dbc文件。其中较常用的工具是canmatrix库,可以在Python环境下进行安装和使用。 首先,需要在Python中安装canmatrix库。可以使用pip命令来安装,命令如下: pip install canmatrix 安装完成后,可以使用import语句来引入canmatrix库,然后使用open_can_database函数读取dbc文件。例如,使用以下代码可以读取名为example.dbcdbc文件: import canmatrix db = canmatrix.Database() db = canmatrix.formats.loadp("example.dbc") 读取后,可以对dbc文件进行修改或编辑。比如,可以新增或删除节点、消息或者信号等。可以使用canmatrix库提供的各种函数和方法来进行这些修改。例如,可以使用create_frame函数创建新的消息帧: new_frame = canmatrix.Frame("NewFrameID") 然后可以使用add_frame函数来将新的消息帧添加到数据库中: db.add_frame(new_frame) 另外,还可以使用canmatrix库提供的方法来修改现有的节点、消息或信号。例如,可以使用add_signal函数为现有的消息帧添加新的信号: new_signal = canmatrix.Signal() new_signal.name = "NewSignal" new_frame.add_signal(new_signal) 最后,可以使用canmatrix提供的函数将编辑后的dbc文件保存为新的文件,并按需要进行命名。例如,可以使用以下代码保存编辑后的文件为"edited.dbc": canmatrix.formats.dump("edited.dbc", db) 以上就是使用Python编辑dbc文件的基本过程。通过使用canmatrix库,我们可以方便地读取、编辑和保存dbc文件,实现对dbc文件的灵活处理和修改。 ### 回答2: Python提供了很多工具和库来编辑和处理不同类型的文件,包括dbc文件DBC数据库容器)文件是CAN网络中用于定义通信协议的文件格式。要编辑DBC文件,可以使用Python中的第三方库`cantools`。 首先,需要在Python环境中安装`cantools`库。可以通过pip命令安装: ``` pip install cantools ``` 安装完成后,就可以使用该库来编辑DBC文件。 通过`cantools`库,可以将DBC文件加载为一个数据库对象,然后可以对该对象进行修改和操作。通过`cantools.db.load_file`函数可以加载DBC文件: ```python import cantools # 加载DBC文件 db = cantools.database.load_file('example.dbc') ``` 加载完成后,可以使用`db`对象进行一系列的操作,例如查看定义的消息、信号等信息,修改信号的数值范围等。 下面是一个示例,将加载的DBC文件中某个信号的数值范围修改为新的范围: ```python # 找到对应的信号 signal = db.get_signal_by_name('Signal_Name') # 修改信号的数值范围 signal.min = 0 signal.max = 100 # 保存到新的DBC文件 db.dump('new_dbc.dbc') ``` 以上示例中,首先通过`get_signal_by_name`函数找到需要修改的信号,然后修改信号的`min`和`max`属性,最后使用`dump`函数将修改后的数据库对象保存为新的DBC文件。 通过以上方法,可以利用PythonDBC文件进行编辑和修改。`cantools`库还提供了其他很多功能,可以根据具体需求进行进一步的操作和处理。 ### 回答3: Python是一种高级编程语言,具有丰富的库和模块,可以用来编辑和处理各种文件,包括.dbc文件。 .dbc文件是一种用于描述Controller Area Network (CAN) 通信网络的数据文件,它定义了CAN网络中传输的消息的格式、信号的属性和值等。编辑.dbc文件可以更改一个CAN网络中的消息及其属性,包括更新消息ID、信号名称、信号长度等。 要使用Python来编辑.dbc文件,可以使用第三方库pycan与Python的内置库进行操作。首先,可以使用pycan库中的dbc模块来导入.dbc文件解析其结构。可以通过读取和修改这些结构来编辑.dbc文件中的消息和信号属性。 在编辑.dbc文件之前,要确保已正确安装pycan库。然后使用以下代码: ```python import can from can.io import dbc # 导入.dbc文件解析结构 database = dbc.load_file('example.dbc') # 查看消息和信号 print(database.messages) print(database.signals) # 编辑.dbc文件:更新消息ID new_id = 100 database.messages[0].frame_id = new_id # 编辑.dbc文件:更新信号名和长度 new_signal_name = 'new_signal' new_signal_length = 8 database.signals[0].name = new_signal_name database.signals[0].length = new_signal_length # 保存修改后的.dbc文件 database.save('edited.dbc') ``` 上述代码首先导入了pycan库,并使用dbc模块中的load_file函数导入并解析.dbc文件。然后可以查看和编辑.dbc文件中的消息和信号属性。在这个例子中,我们更新了第一个消息的ID,并更新了第一个信号的名称和长度。最后,使用save函数保存修改后的.dbc文件。 通过使用Python和pycan库,可以方便地编辑.dbc文件,实现对CAN网络中消息和信号属性的更新和修改。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hurri_cane

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值