Modbus ASCII传感器通信异常:数据间隔不准、采集中断求助
Modbus ASCII传感器数据采集问题修正方案
问题1:数据采集间隔不精确
原因
当前代码在完成一轮数据读写、计算、保存后调用time.sleep(1),但串口通信、数据处理、文件IO等操作本身会消耗额外时间,导致实际间隔大于1秒,累积下来误差会越来越大。
解决方案
采用固定时间点调度:记录每次循环的目标执行时间,计算当前时间与目标时间的差值,只sleep需要的时间,抵消代码执行耗时。
问题2:程序运行数次后停止输出与保存
原因
ser.readline()会一直阻塞,直到接收到换行符。如果传感器未返回数据、返回数据格式异常(无换行)或串口通信中断,程序会永久卡在这一步,表现为停止输出。
解决方案
- 给串口设置超时时间,避免无限阻塞;
- 增加响应合法性校验,处理空响应或格式错误的情况;
- 优化文件操作:循环外打开文件,避免频繁打开/关闭文件的开销与潜在异常。
修正后的代码
import serial import time import os # 创建存储目录 folder_name = time.strftime("%H%M%S%d%m%y") os.makedirs(folder_name, exist_ok=True) # 初始化串口,添加超时时间(1秒) ser = serial.Serial( port='COM3', baudrate=19200, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 # 关键:设置串口读取超时 ) # 写入文件表头 file_path = os.path.join(folder_name, "data.txt") with open(file_path, "w") as file: file.write("date_time\telapsed_seconds\tO2_ppm\tO2_percent\n") start_time = time.time() next_run_time = start_time # 初始化下一次运行时间 try: # 循环内保持文件打开状态,减少IO操作 with open(file_path, "a") as file: while True: # 发送Modbus指令 ser.write(b':010400000004F7\r\n') # 读取响应,超时后返回空字符串 response = ser.readline().decode(errors='ignore').strip() if not response: print("未接收到传感器响应,跳过本次采集") # 记录空响应数据 timestamp = time.strftime("%Y-%m-%d %H:%M:%S") elapsed_seconds = time.time() - start_time file.write(f"{timestamp}\t{elapsed_seconds:.2f}\tN/A\tN/A\n") file.flush() # 立即写入磁盘,避免数据丢失 else: print(f"响应数据:{response}") # 简单校验响应格式长度,可根据实际协议完善 if len(response) >= 15: try: o2_ppm = float(response[7:15]) / 100.0 o2_percent = o2_ppm / 10000 print(f"O2_ppm: {o2_ppm:.2f}, O2_percent: {o2_percent:.6f}") # 记录有效数据 timestamp = time.strftime("%Y-%m-%d %H:%M:%S") elapsed_seconds = time.time() - start_time file.write(f"{timestamp}\t{elapsed_seconds:.2f}\t{o2_ppm:.2f}\t{o2_percent:.6f}\n") file.flush() except ValueError: print("响应数据格式错误,无法解析为数值") timestamp = time.strftime("%Y-%m-%d %H:%M:%S") elapsed_seconds = time.time() - start_time file.write(f"{timestamp}\t{elapsed_seconds:.2f}\tERROR\tERROR\n") file.flush() else: print("响应数据长度不足,格式异常") timestamp = time.strftime("%Y-%m-%d %H:%M:%S") elapsed_seconds = time.time() - start_time file.write(f"{timestamp}\t{elapsed_seconds:.2f}\tERROR\tERROR\n") file.flush() # 调度下一次执行,保证间隔精确1秒 next_run_time += 1 sleep_duration = next_run_time - time.time() if sleep_duration > 0: time.sleep(sleep_duration) except KeyboardInterrupt: print("用户终止程序") finally: ser.close() print("串口已关闭")
内容的提问来源于stack exchange,提问作者Lelouch
相关产品推荐
相关产品推荐

