使用python-can Notifier+PCAN-USB时丢帧导致UDS(udsoncan/isotp)请求超时
使用python-can Notifier+PCAN-USB时丢帧导致UDS(udsoncan/isotp)请求超时
我之前做UDS over CAN开发时碰到过几乎一模一样的问题!核心原因其实很直白:python-can的Notifier会独占CAN Bus的接收通道——启动Notifier后,它会在后台持续调用bus.recv()把所有帧读走交给日志器,导致你的isotp栈(以及上层UDS客户端)根本拿不到ECU的回复帧。虽然外部抓包能看到ECU的响应,但这些帧已经被Notifier“吃掉”了,没传到isotp那里,自然就触发超时了。
你提到试过BufferedReader但没效果,应该是用法不对。我给你两个亲测有效的解决方案:
方案一:手动线程转发帧(最可靠)
直接绕开Notifier的独占问题,自己开一个后台线程,把从Bus收到的每帧同时传给日志器和isotp栈。这样所有帧的流向完全由你控制,不会出现抢帧冲突。
修改后的完整代码如下:
import can import isotp import udsoncan import threading from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client from udsoncan.exceptions import TimeoutException from udsoncan.configs import default_client_config # 初始化PCAN总线 bus = can.interface.Bus( bustype="pcan", channel="PCAN_USBBUS1", bitrate=500000 ) # 配置ISOTP参数 isotp_params = { 'stmin': 5, 'blocksize': 40, 'wftmax': 0, 'tx_data_length': 8, 'tx_padding': 0x55, 'rx_flowcontrol_timeout': 5000, 'rx_consecutive_frame_timeout': 1000, 'max_frame_size': 4095, 'can_fd': False } # 设置ISOTP地址 addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7E0, rxid=0x7E8) # 初始化ISOTP栈:不直接传入bus,用tx_callback处理发送,后续手动feed接收帧 stack = isotp.CanStack( address=addr, error_handler=None, params=isotp_params, tx_callback=lambda frame: bus.send(frame) # 发送委托给原始bus ) # 初始化BLF日志器 final_filename = "log_file.blf" logger = can.BLFWriter(final_filename) # 定义帧转发线程:同时给日志器和ISOTP栈喂帧 def frame_forwarder(bus, logger, stack): while True: try: # 从总线接收帧 frame = bus.recv(timeout=1.0) if frame is None: continue # 1. 写入BLF日志 logger.on_message_received(frame) # 2. 把帧传给ISOTP栈处理 stack.feed(frame) except Exception as e: print(f"转发线程出错: {str(e)}") break # 启动后台转发线程(设为daemon,主程序退出时自动结束) forward_thread = threading.Thread(target=frame_forwarder, args=(bus, logger, stack), daemon=True) forward_thread.start() # 启动UDS客户端并发送请求 conn = PythonIsoTpConnection(stack) with Client(conn, request_timeout=2, config=default_client_config) as client: try: response = client.tester_present() # 发送3E 00命令 print("Response:", response) except TimeoutException: print("UDS request timed out!")
这个方法完全可控,不管CAN流量多大,日志和UDS通信都能正常工作,不会出现丢帧。
方案二:用BufferedReader共享帧
如果你还是想用Notifier,可以通过AsyncBufferedReader把帧共享给Notifier和ISOTP栈。需要把ISOTP栈的Bus替换成一个包装类,让它从BufferedReader读取帧:
import can import isotp import udsoncan from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client from udsoncan.exceptions import TimeoutException from udsoncan.configs import default_client_config # 初始化PCAN总线 bus = can.interface.Bus( bustype="pcan", channel="PCAN_USBBUS1", bitrate=500000 ) # 定义包装类:让ISOTP栈从BufferedReader读,从原始Bus发 class BufferedBusWrapper: def __init__(self, buffered_reader, send_bus): self.buffered_reader = buffered_reader self.send_bus = send_bus def recv(self, timeout=None): return self.buffered_reader.recv(timeout=timeout) def send(self, frame, timeout=None): return self.send_bus.send(frame, timeout=timeout) # 初始化AsyncBufferedReader和Notifier reader = can.AsyncBufferedReader() final_filename = "log_file.blf" logger = can.BLFWriter(final_filename) notifier = can.Notifier(bus, [reader, logger]) # 帧同时发给reader和logger # 配置ISOTP参数和地址 isotp_params = { 'stmin': 5, 'blocksize': 40, 'wftmax': 0, 'tx_data_length': 8, 'tx_padding': 0x55, 'rx_flowcontrol_timeout': 5000, 'rx_consecutive_frame_timeout': 1000, 'max_frame_size': 4095, 'can_fd': False } addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7E0, rxid=0x7E8) # 用包装后的BufferedBus初始化ISOTP栈 wrapped_bus = BufferedBusWrapper(reader, bus) stack = isotp.CanStack(bus=wrapped_bus, address=addr, error_handler=None, params=isotp_params) # 启动UDS客户端 conn = PythonIsoTpConnection(stack) with Client(conn, request_timeout=2, config=default_client_config) as client: try: response = client.tester_present() print("Response:", response) except TimeoutException: print("UDS request timed out!") # 程序结束时清理Notifier notifier.stop()
这个方案依赖python-can的缓冲机制,低流量场景下没问题,但高负载时可能出现缓冲溢出,所以还是方案一更稳妥。
总结来说:本质问题就是Notifier和ISOTP栈不能同时直接从同一个Bus调用recv,必须让它们共享帧的来源。方案一的手动转发是最可靠的解决方式,我自己在项目里一直用这个方法,完全解决了超时问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

