1.1 python-can
定位:通用的 CAN 总线通信库,支持多种硬件接口(SocketCAN、PCAN、Kvaser 等)。
核心功能:
连接物理 CAN 设备或虚拟接口。
发送/接收 CAN 报文(标准帧、扩展帧)。
支持 CAN FD(灵活数据速率)。
适用场景:快速搭建 CAN 通信原型,兼容性强。
定位:专注于 UDS(ISO 14229) 和 J1939 等高层协议解析的库。
核心功能:
解析 DBC 文件,自动化生成信号值。
支持 UDS 诊断服务(如 0x22 读数据、0x2E 写数据)。
处理多帧传输(如 ISO-TP 协议)。
适用场景:需要与 ECU 进行诊断交互的自动化测试。
1 2 3 4 5 |
# 安装通用 CAN 库 pip install python-can
# 安装 canard(UDS 协议支持) pip install canard |
物理设备:PCAN-USB、Kvaser、Vector 设备(需安装对应驱动)。
虚拟测试:使用 socketcan(Linux 虚拟 CAN 接口)或 CANoe 模拟总线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import can
# 连接 CAN 接口(以 socketcan 为例) bus = can.interface.Bus(channel='vcan0', bustype='socketcan')
# 发送单帧数据 msg = can.Message( arbitration_id=0x123, # CAN ID data=[0x01, 0x02, 0x03, 0x04], is_extended_id=False # 标准帧 ) bus.send(msg)
# 接收数据 for msg in bus: print(f"Received: ID={hex(msg.arbitration_id)}, Data={msg.data}")
# 关闭连接 bus.shutdown() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from canard import Canard from canard.hw import socketcan
# 加载 DBC 文件 dbc = Canard() dbc.load_dbc('vehicle.dbc')
# 创建虚拟 CAN 接口 dev = socketcan.SocketCanDev('vcan0') dev.start()
# 解析接收到的报文 def on_message(msg): decoded = dbc.decode_message(msg.arbitration_id, msg.data) print(f"信号值: {decoded}")
dev.add_listener(on_message) |
1 2 3 4 5 6 7 8 9 10 11 |
from canard.proto.uds import UdsClient
# 创建 UDS 客户端 uds = UdsClient(transport=can.Bus(bustype='socketcan', channel='vcan0'))
# 发送诊断请求:读取 ECU 序列号(服务 0x22,参数 0xF189) response = uds.request([0x22, 0xF1, 0x89]) if response: print(f"ECU 序列号: {bytes(response.data[2:]).decode()}") else: print("请求超时") |
1 2 3 4 5 6 7 8 9 10 11 12 |
def test_door_status(): bus = can.interface.Bus(channel='vcan0', bustype='socketcan')
# 发送车门控制命令(ID=0x200,数据=[0x01] 表示开门) bus.send(can.Message(arbitration_id=0x200, data=[0x01]))
# 等待并验证车门状态反馈(预期 ID=0x201,数据[0]=0x01) for msg in bus: if msg.arbitration_id == 0x201: assert msg.data[0] == 0x01, "车门未正确打开" break bus.shutdown() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import pytest
@pytest.fixture def can_bus(): bus = can.interface.Bus(channel='vcan0', bustype='socketcan') yield bus bus.shutdown()
def test_engine_rpm(can_bus): # 发送转速请求命令 can_bus.send(can.Message(arbitration_id=0x700, data=[0x03]))
# 验证响应是否在合理范围(如 800-5000 RPM) for msg in can_bus: if msg.arbitration_id == 0x701: rpm = int.from_bytes(msg.data[2:4], byteorder='big') assert 800 <= rpm <= 5000, "转速异常" break |
1 2 3 4 5 6 7 8 |
# 使用 python-can 发送 CAN FD 报文 msg = can.Message( arbitration_id=0x123, data=[i % 256 for i in range(64)], # 64 字节数据 is_fd=True, bitrate_switch=True # 启用比特率切换 ) bus.send(msg) |
1 2 3 4 5 6 7 8 9 10 |
# 连接 Vector 硬件(需安装 XL Driver) bus = can.interface.Bus( bustype='vector', channel=1, app_name='Python_HIL_Test' )
# 发送同步周期性报文 task = bus.send_periodic(msgs=[msg1, msg2], period=0.1) task.stop() # 停止发送 |
1 2 |
# 使用 pytest-html 生成测试报告 pytest.main(["--html=report.html"]) |
现象:CanError: Error sending message
解决:
检查设备驱动是否安装(如 PCAN 需安装 PCAN-Basic API)。
确认通道权限(Linux 需 sudo ip link set vcan0 up)。
现象:KeyError: 'Unknown frame ID'
解决:确保 DBC 文件中包含目标 CAN ID 的定义。
1 2 3 4 5 |
# 使用 canard 处理 ISO-TP 多帧传输 from canard.proto.iso_tp import IsoTpTransport
tp = IsoTpTransport(can_bus, tx_id=0x7E0, rx_id=0x7E8) data = tp.recv() # 接收长数据(自动重组) |