You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino与Python Serial交互异常:语音指令后Servo未即时转动求助

问题分析与解决方案

核心问题1:串口通信阻塞导致舵机响应异常

问题现象

  • 首次触发语音验证后,Python发送指令但Arduino舵机无反应
  • 再次按下按键1时,未进行语音验证舵机就自动转动

原因分析

Arduino代码存在两个关键问题:

  1. 串口读取阻塞:发送Wants in后立即调用Serial.read(),此时Python还未返回指令,该函数会阻塞程序直到收到字节,导致后续流程停滞;第二次触发时,缓冲区残留的上次指令被读取,直接触发舵机转动。
  2. 嵌套死循环:while(true)嵌套在loop()中,剥夺了Arduino处理后续串口数据的能力,导致串口数据积压。

修复方案(Arduino代码修改)

移除嵌套死循环,改用loop()原生循环;优化串口读取逻辑,先检查数据再读取,避免阻塞;新增状态标记管理流程:

#include <Key.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Servo.h>

// Variables
LiquidCrystal_I2C lcd(0x27, 16, 2);
const byte rows = 4;
const byte columns = 3;
char keys[rows][columns] = {
  { '1', '2', '3' },
  { '4', '5', '6' },
  { '7', '8', '9' },
  { '*', '0', '#' }
};
byte rowPins[rows] = { 8, 7, 6, 5 };
byte columnPins[columns] = { 4, 3, 2 };
const int trigPin = 11;
const int echoPin = 10;
Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rows, columns);
Servo servo;
long duration = 0;
long distance = 0;
bool continueDetecting = false;
bool waitingForAuth = false; // 标记是否等待授权指令

void setup() {
  Serial.begin(9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Wire.begin();
  lcd.init();
  servo.attach(9);
  servo.write(90); // 初始化舵机位置
}

bool connected = false;
void loop() {
  // 处理连接流程
  if (!connected) {
    if (Serial.available() > 0) {
      String connectToPython = Serial.readStringUntil('\n');
      connectToPython.trim();
      if (connectToPython == "Establish connection") {
        connected = true;
        Serial.println("Connection established");
      }
    }
  } 
  // 等待开始检测指令
  else if (!continueDetecting) {
    if (Serial.available() > 0) {
      String detectingState = Serial.readStringUntil('\n');
      detectingState.trim();
      if (detectingState == "Start detecting") {
        continueDetecting = true;
        Serial.println("Detecting");
      }
    }
  } 
  // 执行检测与交互逻辑
  else {
    // 超声波测距
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration / 2) * 0.0343;
    delay(500);

    // 检测到人员且未在等待授权时,显示交互提示
    if (distance <= 73 && !waitingForAuth) {
      lcd.clear();
      lcd.backlight();
      lcd.setCursor(1, 0);
      lcd.print("Wanna come in?");
      lcd.setCursor(0, 1);
      lcd.print("1 = Yes | 2 = No");
      
      unsigned long startTime = millis();
      char keyPress = NO_KEY;
      while (keyPress == NO_KEY && (millis() - startTime) < 5000) {
        keyPress = keypad.getKey();
        delay(50);
      }

      if (keyPress == '1') {
        Serial.println("Wants in");
        waitingForAuth = true;
        lcd.clear();
        lcd.print("Waiting auth...");
      } else if (keyPress == '2') {
        lcd.clear();
        lcd.noBacklight();
      }
    }

    // 处理授权指令
    if (waitingForAuth && Serial.available() > 0) {
      int receivedByte = Serial.read();
      if (receivedByte == 65) {
        spinDoorknob();
        waitingForAuth = false;
        lcd.clear();
        lcd.noBacklight();
      }
      // 清理缓冲区残留数据
      while(Serial.available() > 0) {
        Serial.read();
      }
    }
  }
}

void spinDoorknob() {
  servo.write(180);
  delay(1000);
  servo.write(0);
  delay(300);
  servo.write(90);
}

Python代码优化

增加小写匹配提高容错,添加未授权时的指令发送,避免Arduino一直等待:

def wait_for_keypad_keypress():
    while True:
        person_outside = com4.readline().decode().strip()
        if person_outside == "Wants in":
            print("Wants in")
            show_notification("test", "Someone wants to go in, Romy!")
            speech = speech_to_text()
            # 转为小写匹配,提升容错率
            if any(word in speech.lower() for word in keywords):
                byte_to_send = 65
                com4.write(byte_to_send.to_bytes(1, 'big'))
                print("done")
            else:
                # 发送无效指令,解除Arduino等待状态
                com4.write(b'0')
                print("Unauthorized")

核心问题2:win10Toast库通知消失后的错误

问题现象

通知消失后抛出错误:

WNDPROC return value cannot be converted to LRESULT
TypeError: WPARAM is simple, so must be an int object (got NoneType)

原因分析

win10Toast库在Python 3.8+环境下存在兼容性问题,导致通知回调时类型错误。

修复方案

替换为兼容性更好的plyer库:

  1. 安装依赖:pip install plyer
  2. 修改通知函数:
from plyer import notification

def show_notification(title, message):
    notification.notify(
        title=title,
        message=message,
        timeout=3
    )

内容的提问来源于stack exchange,提问作者Aremenel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 15:52:03