Teensy与Python串口通信采样速率过低及数据库优化求助
提升Teensy-Python串口采样率与数据库写入效率的优化方案
嘿,针对你遇到的采样率瓶颈问题,结合你的代码细节,我整理了几个核心优化方向,一步步来解决:
一、先解决Python端串口读取的低效问题
你的Python代码里有几个明显拖慢速度的点:
- 频繁的控制台打印:每次循环都打印
time、checker和串口数据,控制台IO的速度远低于串口传输和内存操作,这是采样率上不去的头号元凶!先把这些打印注释掉,只在调试时启用。 - 串口读取的小细节优化:
- 调整串口参数,设置短超时、关闭不必要的硬件流控,同时调大接收缓冲区,减少阻塞概率:
- 可以尝试用pyserial内置的
read_until(b'\n')替代自定义的ReadLine类,内置方法经过性能优化,可能更高效。
修改后的串口初始化示例:
ser = serial.Serial( port='COM5', baudrate=2000000, timeout=0.001, # 短超时避免等待 dsrdtr=False, rtscts=False, # 关闭硬件流控(Teensy USB串口不需要) buffer_size=1048576 # 1MB接收缓冲区 )
二、数据库操作的批量优化(最关键的性能提升点)
你现在每次循环都执行2条INSERT+1次COMMIT,数据库的磁盘IO和事务开销极大,这是加入数据库后采样率暴跌的核心原因。优化方案:
- 批量插入+延迟提交:攒够N条数据(比如100条)再一次性插入并提交,大幅减少事务次数。
- 使用参数化查询:避免字符串格式化的开销,同时防止SQL注入风险。
- 合并INSERT语句:利用MySQL支持的批量插入语法,把多条数据合并成一个INSERT语句,减少数据库交互次数。
修改后的数据库操作示例:
# 初始化批量存储列表,设置批量阈值 batch_size = 100 data_batch = [] sync_batch = [] while True: # 仅做必要的串口读取,去掉所有非调试打印 line = rl.readline().decode('utf-8').strip() if not line: continue current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 将数据加入批量列表 data_batch.append( (33.5, current_time, checker) ) sync_batch.append( (f"http://www.myname.com/value.php?&value={33.5}&time={current_time}", 0) ) checker += 1 # 达到批量阈值时执行批量插入 if len(data_batch) >= batch_size: # 批量插入tempinfo表 insert1 = "INSERT INTO tempinfo(value,test,counter) VALUES(%s,%s,%s)" cursor.executemany(insert1, data_batch) # 批量插入urlsync表 insert2 = "INSERT INTO urlsync(textvalue,sync) VALUES(%s,%s)" cursor.executemany(insert2, sync_batch) # 一次性提交事务 connection.commit() # 清空批量列表 data_batch = [] sync_batch = []
三、Teensy端的传输优化(从源头减少数据量与解析开销)
当前Teensy每次发送一行文本(比如i的字符串),Python需要做字符串转数字的解析,效率远不如二进制传输:
- 改用二进制格式传输:用
Serial.write()发送固定长度的字节(比如4字节的int类型),Python直接读取固定字节数后解析,速度更快。 - 批量发送数据:攒多个数据后一次性打包发送,减少串口帧的额外开销。
修改后的Teensy代码示例(二进制传输):
int i; elapsedMillis sinceTest1; void setup() { Serial.begin(2000000); i = 0; delay(5000); Serial.println("Setup Called"); Serial.flush(); } void loop() { if (i % 500000 == 0) { // 定时发送时间戳(保留文本格式方便调试) Serial.println(sinceTest1); } // 发送i的二进制数据(4字节int,小端字节序) Serial.write((const uint8_t*)&i, sizeof(i)); i++; }
对应的Python读取二进制数据的代码:
# 读取Teensy发送的4字节int数据 def read_int_from_serial(ser): data = ser.read(4) if len(data) != 4: return None return int.from_bytes(data, byteorder='little') # Teensy为小端字节序 # 在循环中替换原readline逻辑 while True: val = read_int_from_serial(ser) if val is not None: # 后续批量插入逻辑...
四、其他小建议
- 确保使用64位Python版本,并安装最新的
pyserial和pymysql库,避免旧版本的性能瓶颈。 - 如果是本地MySQL数据库,可以调整配置(比如增大
innodb_buffer_pool_size),提升写入缓存效率。
按照这个顺序优化:先去掉打印、优化串口,再改数据库批量插入,最后考虑二进制传输,采样率应该能大幅提升,甚至接近Arduino串口监视器的水平。
内容的提问来源于stack exchange,提问作者Hadi




