如何等待PyFirmata完成Arduino写入后再执行图像采集?
Arduino PWM控制LED与相机采集同步问题
我用Arduino的PWM端口控制4个LED的亮度,同时用相机采集对应设置下的LED亮度,预期PWM设为0时LED功率最小,设为1时功率最大。但采集数据的图表显示,0值附近存在强度尖峰,原因是之前点亮的LED尚未完全关闭——核心问题是无法确保Arduino完成PWM写入操作后,再执行相机采集命令。
即使延长board.pass_time()和time.sleep()的时长,问题依然存在,推测这些延迟命令在Arduino实际完成写入前就已执行,因此无效。
以下是当前使用的Python代码片段:
# Iterate over each LED for i in range(4): board.pass_time(0.4) time.sleep(0.4) # Reset all pins to zero for j in range(4): led_pins[j].write(0) # Select the relevant pin pin = led_pins[i] # Iterate over each data value for j in range(N): print(i, j) # Set the power of the given LED pin.write(vals[j]) board.pass_time(0.4) time.sleep(0.4) # os.wait() # # Start a thread to take a capture # thread = threading.Thread(target=take_capture_thread, args=(cam,)) # thread.start() # # Wait to rejoin with thread # thread.join() # Take a capture for k in range(10): cam.issue_software_trigger() frame = cam.get_pending_frame_or_null() capture = frame.image_buffer # Measure the average power at the center of the image data[i, j] = np.average(capture[selection])
解决方案
1. 实现Arduino与主机的命令握手(最可靠)
通过串口让Arduino在完成PWM写入后,主动向主机发送确认信号,主机收到确认后再执行采集操作,从根源上保证同步。
Arduino端代码(修改后)
// 定义LED引脚 int ledPins[] = {9, 10, 11, 12}; const int PIN_COUNT = 4; void setup() { Serial.begin(9600); // 初始化所有LED引脚为输出 for (int i = 0; i < PIN_COUNT; i++) { pinMode(ledPins[i], OUTPUT); analogWrite(ledPins[i], 0); // 初始化为0 } } void loop() { // 等待主机发送的命令:1字节引脚索引 + 1字节PWM值(0-255) if (Serial.available() >= 2) { int pinIndex = Serial.read(); int pwmValue = Serial.read(); // 校验引脚索引合法性 if (pinIndex >= 0 && pinIndex < PIN_COUNT) { analogWrite(ledPins[pinIndex], pwmValue); } // 发送确认信号(任意字节,这里用1) Serial.write(1); } }
主机Python代码修改
替换原代码中直接操作led_pins的逻辑,改为串口通信并等待确认:
import serial # 初始化串口(根据你的端口调整,比如COM3或/dev/ttyUSB0) ser = serial.Serial('COM3', 9600, timeout=1) # Iterate over each LED for i in range(4): # 重置所有LED为0,并等待确认 for j in range(4): # 发送重置命令:引脚j + PWM值0 ser.write(bytes([j, 0])) # 等待Arduino确认 while ser.in_waiting == 0: pass ser.read() # 读取确认字节 # Iterate over each data value for j in range(N): print(i, j) # 将0-1的vals[j]转为0-255的PWM值 pwm_val = int(vals[j] * 255) # 发送PWM设置命令:引脚i + PWM值 ser.write(bytes([i, pwm_val])) # 等待Arduino完成写入的确认 while ser.in_waiting == 0: pass ser.read() # 执行相机采集 for k in range(10): cam.issue_software_trigger() frame = cam.get_pending_frame_or_null() if frame is not None: capture = frame.image_buffer data[i, j] = np.average(capture[selection]) ser.close()
2. 优化相机采集逻辑
即使Arduino同步完成,相机可能还残留之前的帧,可通过以下方式优化:
- 采集前多触发几次并丢弃早期帧,只保留最后1-2帧
- 对连续采集的多帧取平均值,降低噪声和残留影响
3. 避免使用board.pass_time()(若使用的是PyFirmata)
如果使用PyFirmata库,board.pass_time()是让Arduino执行延迟,但主机端不会等待Arduino完成延迟。改用上述握手方式,彻底替代依赖延迟的不可靠同步。
内容的提问来源于stack exchange,提问作者Justin Murante
相关产品推荐
相关产品推荐

