python-can库bus.recv无法接收CANFD报文及基于poll接口实现CAN/CANFD转发脚本需求
python-can库bus.recv无法接收CANFD报文及基于poll接口实现CAN/CANFD转发脚本需求
嗨,我来帮你搞定这两个问题——先解决python-can收不到CANFD报文的坑,再给你实现符合要求的poll接口转发脚本。
一、为什么bus.recv收不到CANFD报文?怎么修复?
你遇到的问题核心其实很简单:python-can创建CAN总线实例时,默认是不启用CANFD模式的。哪怕你的虚拟CAN设备已经配置好支持CANFD(毕竟你用candump/cansend能正常收发),但库本身没开FD模式的话,就会自动过滤掉CANFD帧,只处理经典CAN帧。
修复后的原桥接脚本(支持CANFD)
只需要在初始化can.interface.Bus时,显式加上fd=True参数,同时指定经典CAN和CANFD的比特率(要和你配置虚拟CAN设备时的参数一致):
#!/usr/bin/env python3 import can # CAN interface names CAN0 = 'can0' CAN1 = 'can1' def main(): # 关键修改:启用CANFD模式,并配置对应比特率 bus0 = can.interface.Bus( channel=CAN0, interface='socketcan', fd=True, # 必须显式开启CANFD支持 bitrate=500000, # 经典CAN比特率,和你的设备配置匹配 fd_bitrate=2000000 # CANFD数据阶段比特率,和你的设备配置匹配 ) bus1 = can.interface.Bus( channel=CAN1, interface='socketcan', fd=True, bitrate=500000, fd_bitrate=2000000 ) while True: # Receive a message from can0 msg = bus0.recv(timeout=1.0) if msg is not None: print(f"Received on {CAN0}: {msg}") # Forward the message to can1 if msg.is_fd: print(f"Forwarding CAN FD message from {CAN0} to {CAN1}") else: print(f"Forwarding classic CAN message from {CAN0} to {CAN1}") bus1.send(msg) # Receive a message from can1 msg = bus1.recv(timeout=1.0) if msg is not None: print(f"Received on {CAN1}: {msg}") # Forward the message to can0 if msg.is_fd: print(f"Forwarding CAN FD message from {CAN1} to {CAN0}") else: print(f"Forwarding classic CAN message from {CAN1} to {CAN0}") bus0.send(msg) if __name__ == "__main__": main()
修改后再测试发送CANFD报文,脚本就能正常接收并转发了。
二、基于poll接口的CAN/CANFD转发脚本实现
如果你需要严格按照“poll监听POLLIN事件 → read获取帧 → write转发”的逻辑来实现,我们可以直接用SocketCAN的原生socket来写,这样完全贴合你的伪代码需求,也能更灵活地控制底层操作。
完整实现脚本
#!/usr/bin/env python3 import socket import struct from select import poll, POLLIN # CAN接口名称 CAN0 = 'can0' CAN1 = 'can1' def create_can_socket(ifname): """创建并配置支持CANFD的SocketCAN套接字""" # 创建RAW类型的CAN套接字 sock = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) # 绑定到指定CAN接口 sock.bind((ifname,)) # 启用CANFD帧支持 sock.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FD_FRAMES, 1) return sock def main(): # 初始化两个CAN套接字 sock0 = create_can_socket(CAN0) sock1 = create_can_socket(CAN1) # 用poll对象监听两个套接字的POLLIN可读事件 poller = poll() poller.register(sock0, POLLIN) poller.register(sock1, POLLIN) # 建立套接字到目标套接字的转发映射 forward_map = {sock0: sock1, sock1: sock0} ifname_map = {sock0: CAN0, sock1: CAN1} print(f"CAN/CANFD Bridge started between {CAN0} and {CAN1} (using poll)") try: while True: # 等待事件触发,超时1秒 events = poller.poll(1000) for fd, event in events: if event & POLLIN: # 找到当前触发事件的套接字 current_sock = sock0 if fd == sock0.fileno() else sock1 target_sock = forward_map[current_sock] current_ifname = ifname_map[current_sock] target_ifname = ifname_map[target_sock] # 读取帧数据(缓冲区设大一点兼容CANFD的最大帧长) frame_data = current_sock.recv(1024) if not frame_data: continue # 判断是经典CAN帧还是CANFD帧(通过帧长度区分:经典CAN帧结构体对齐后是16字节,CANFD是72字节) is_fd = len(frame_data) > 16 # 打印日志(简单解析帧信息) if is_fd: # 解析CANFD帧结构:can_id(4B) + 长度(1B) + 标志位(1B) + 数据(64B) can_id, can_len, _, data = struct.unpack("<I B B 64s", frame_data[:70]) data = data[:can_len] print(f"[CANFD] Received on {current_ifname}: ID=0x{can_id:X}, Len={can_len}, Data={data.hex()}") else: # 解析经典CAN帧结构:can_id(4B) + DLC(1B) + 数据(8B) can_id, can_dlc, data = struct.unpack("<I B 8s", frame_data[:13]) data = data[:can_dlc] print(f"[CAN] Received on {current_ifname}: ID=0x{can_id:X}, DLC={can_dlc}, Data={data.hex()}") # 转发帧到目标接口 target_sock.sendall(frame_data) print(f"Forwarded to {target_ifname}") except KeyboardInterrupt: print("\nBridge stopped by user") finally: sock0.close() sock1.close() if __name__ == "__main__": main()
脚本说明
- 原生Socket操作:直接用SocketCAN的套接字,完全符合你要的
poll/read/write逻辑 - 双帧支持:自动识别经典CAN和CANFD帧,不需要额外配置
- 高效监听:用
poll替代轮询,能同时监听两个接口的可读事件,资源占用更低 - 日志清晰:打印帧类型、ID、长度和数据,方便调试
测试步骤
- 先确保虚拟CAN设备配置好CANFD:
sudo ip link set can0 type can bitrate 500000 fd on sudo ip link set can1 type can bitrate 500000 fd on sudo ip link set can0 up sudo ip link set can1 up - 运行脚本,然后在另一个终端发送CANFD报文:
cansend can0 213##4112233445566778899AA - 你会看到脚本打印接收和转发日志,同时用
candump can1能看到转发的帧。
备注:内容来源于stack exchange,提问作者叶问天
相关产品推荐
相关产品推荐

