-
发生时间:2023-08-11
-
问题描述:
- 为支持项目算法测试,我们开发了 bmcan 组件,用于跟 BUSMUSTER 进行 CAN FD 通信。
- 调试过程中,发现经常无法通过 bmcan 收到特定数据的算法告警报文。
-
问题类别:软件开发
-
原因分析:
- 查看 bmcan 的调试日志,发现可以收到大量目标 CAN ID 的报文,只是没收到算法告警报文。
- 从 BUSMUSTER 自带的官方上位机界面可以看到,设备会周期性发送大概10种 CAN ID 的报文,发送周期在 1~10 ms之间。
- 初步推测原因是 bmcan 的收包速度跟不上设备的发包速度。
- 在 bmcan 的代码中增加打印日志,最终在
reader.py的read_repeatly函数中发现异常:- 理论上,此函数每调用底层 SDK 成功接收一次报文后,会 sleep 1 毫秒,然后重新接收报文。
- 实际上,此函数每次 sleep 的时间大约是 14 毫秒,远超期望的 1 毫秒!这导致收包速度明显跟不上发包速度。
- 经测试,Windows 系统 Python 3.8.3 里面,
time.sleep的最小精度只有14毫秒左右。(详见下文) - 经同事提醒,在网上找到一个使用 C/C++ 提高 Windows 系统 sleep 精度的解决方案,但毕竟是跨语言,比较麻烦。
- 后来在 cpython 的 Github Issue 页面 找到一个相关问题的讨论,该 issue 已经被 Closed,我意识到可能新版本的 Python 已经解决此问题。
- 因此,我将 Python 版本更新到 3.11.4,果然
time.sleep的最小精度变为1毫秒左右。
-
解决方案:将 Python 版本更新到 3.11.4.
-
实施结果:
time.sleep函数可以正常工作,修改后 bmcan 能够正常收到算法告警报文。 -
经验总结:
- Python 3.8.3 或更早的版本的
time.sleep的最小时间精度为 14 毫秒左右,Python 3.11.4 的time.sleep的最小时间精度为 1 毫秒左右。 - 如果发现旧版本的 Python 存在一些问题或局限,可以尝试升级 Python 到最新的稳定版本,也许可以解决问题。
- Python 3.8.3 或更早的版本的
Windows 系统 不同 Python 版本的 time.sleep 函数的行为差异
测试程序
import time
def test_sleep(seconds: float):
start = time.time()
time.sleep(seconds)
end = time.time()
print(f"计划睡眠 {seconds} s,实际睡眠 {end - start} s")
test_sleep(0.1)
test_sleep(0.01)
test_sleep(0.001)
Python 3.8.3 的运行结果
计划睡眠 0.1 s,实际睡眠 0.11043739318847656 s
计划睡眠 0.01 s,实际睡眠 0.01606273651123047 s
计划睡眠 0.001 s,实际睡眠 0.014087438583374023 s
Python 3.11.4 的运行结果
计划睡眠 0.1 s,实际睡眠 0.10086703300476074 s
计划睡眠 0.01 s,实际睡眠 0.010546207427978516 s
计划睡眠 0.001 s,实际睡眠 0.0012774467468261719 s
结论
对比 Python 3.8.3 和 Python 3.11.4 的运行结果,可以看出后者的睡眠时间精度明显高于前者。 特别是睡眠1毫秒的时候,后者的误差为 20%,前者的误差为 700% !