基于人脸距离的树莓派-Arduino直流电机串口控制异常排查
解决树莓派与Arduino串口通信指令偶尔失效的问题
问题根源
- 指令无界定符,接收逻辑模糊:Arduino用
Serial.readString()读取数据时,会持续读取到串口缓冲区为空,若树莓派频繁发送相同指令,会导致缓冲区出现拼接数据(如onon),无法匹配预设指令。 - 重复发送冗余指令:树莓派每帧检测到距离符合条件就发送指令,大量重复指令挤占串口带宽,引发丢包或粘包。
- Arduino初始化延迟:树莓派打开串口后,Arduino会自动重启,此时立即发送的指令会直接丢失。
解决方案
1. 给指令添加结束符,修正Arduino接收逻辑
给树莓派发送的指令加上换行符\n,让Arduino准确识别完整指令;同时修改Arduino的读取方式,仅读取到换行符为止,并清理冗余字符。
树莓派代码修改(指令发送部分):
将原发送代码:
usb.write(b'of') # 和 usb.write(b'on')
改为:
usb.write(b'of\n') # 添加换行符作为指令结束标记 usb.flush() # 确保数据全部发送 # 和 usb.write(b'on\n') usb.flush()
Arduino代码修改(读取逻辑):
void loop() { if (Serial.available()) { String command = Serial.readStringUntil('\n'); // 读取到换行符为止 command.trim(); // 移除换行符、空格等冗余字符 if (command == "on"){ forward(); }else if(command == "of"){ mstop(); } } }
2. 避免重复发送相同指令
在树莓派代码中记录上一次发送的指令,仅当指令发生变化时才发送,减少串口通信量。
树莓派代码修改:
- 新增导入
time模块:
import time
- 打开串口后添加延时,等待Arduino重启完成:
try: usb = serial.Serial(USB_PORT, 9600, timeout=2) time.sleep(2) # 等待Arduino重启初始化 except: print("ERROR - Could not open USB serial port. Please check your port name and permissions.") print("Exiting program.") exit()
- 主循环前定义变量记录上一次指令:
last_command = None
- 修改距离判断的发送逻辑:
if face_width_in_frame != 0: Distance = Distance_finder(Focal_length_found, Known_width, face_width_in_frame) # 原有的绘图代码保持不变 current_command = None if Distance < 50 : print("back") elif 50 < Distance < 80: current_command = "of" if current_command != last_command: print("stop") usb.write(b'of\n') usb.flush() last_command = current_command elif Distance > 80: current_command = "on" if current_command != last_command: print("forward") usb.write(b'on\n') usb.flush() last_command = current_command
修改后的完整关键代码
树莓派代码(关键片段)
import numpy as np import cv2 import serial import time USB_PORT = "/dev/ttyUSB0" try: usb = serial.Serial(USB_PORT, 9600, timeout=2) time.sleep(2) except: print("ERROR - Could not open USB serial port. Please check your port name and permissions.") print("Exiting program.") exit() # 原有的常量、函数定义保持不变 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Cannot open camera") exit() last_command = None while True: ret, frame = cap.read() if not ret: print("Can't receive frame (stream end?). Exiting ...") break face_width_in_frame = face_data(frame) if face_width_in_frame != 0: Distance = Distance_finder(Focal_length_found, Known_width, face_width_in_frame) # 原有的绘图代码保持不变 current_command = None if Distance < 50 : print("back") elif 50 < Distance < 80: current_command = "of" if current_command != last_command: print("stop") usb.write(b'of\n') usb.flush() last_command = current_command elif Distance > 80: current_command = "on" if current_command != last_command: print("forward") usb.write(b'on\n') usb.flush() last_command = current_command cv2.imshow('frame', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows()
Arduino代码(完整)
#include <AFMotor.h> AF_DCMotor motor(1); void setup() { Serial.begin(9600); } void loop() { if (Serial.available()) { String command = Serial.readStringUntil('\n'); command.trim(); if (command == "on"){ forward(); }else if(command == "of"){ mstop(); } } } void forward(){ motor.setSpeed(200); motor.run(FORWARD); } void mstop(){ motor.run(RELEASE); }
内容的提问来源于stack exchange,提问作者iulian
相关产品推荐
相关产品推荐

