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

Arduino与树莓派串口通信故障:单独指令有效组合后无响应

问题分析与解决方案

核心问题定位

从代码和现象来看,导致电机无动作、中断失效的主要原因有以下几点:

1. Loop函数的阻塞与干扰代码

  • Arduino的loop()末尾存在无条件执行的MoveReverse调用,这会导致串口指令触发的电机动作刚执行就被反转操作打断,看起来像是电机没动。
  • 代码中残留了无效的注释行Put whatever you want here!,会导致编译错误(如果未修正的话)。

2. 串口接收的冗余字符干扰

Python的input()函数会将用户输入的内容(包括回车换行符)发送给Arduino,Serial.readString()会读取到包含换行/回车的字符串,虽然toInt()能忽略这些字符,但可能导致后续串口缓冲区残留数据,影响指令判断。

3. 中断变量读取的原子性问题

counter_A和counter_B是volatile类型的16位整数,在主程序读取时,若被中断打断(比如读取高8位时触发中断修改变量),会导致读取到错误的数值,进而让电机函数的循环条件失效。

4. 电机循环逻辑的潜在问题

部分电机函数(如MoveReverse、SpinRight)的while循环使用&&(逻辑与)条件,这意味着只有当两个计数器都未达到steps时才会运行,若其中一个计数器先达标,另一个电机也会立刻停止,可能导致动作不完整。


具体解决方案

1. 修复Loop函数

删除无效代码和干扰性的无条件反转调用,修改后的loop()如下:

void loop()
{
  delay(100);
  const int compareOne = 1;
  const int compareTwo = 2;
  const int compareThree = 3;
  
  if (Serial.available() > 0){
    String stringFromSerial = Serial.readStringUntil('\n'); // 读取到换行符为止
    stringFromSerial.trim(); // 移除空格、换行、回车等冗余字符
    
    int cmd = stringFromSerial.toInt();
    if (cmd == compareOne){
      Serial.println("Forward");
      MoveForward(CMtoSteps(50), 255);
    }
    else if (cmd == compareTwo){
       Serial.println("Spin Right");
       SpinRight(CMtoSteps(10), 255);
    } 
    else if (cmd == compareThree){
      Serial.println("Spin Left");
      SpinLeft(CMtoSteps(10), 255);
    }
    else {
      Serial.println("Not equal");
    }
  }
}
  • 使用else if替代多个独立if,避免重复判断
  • 用readStringUntil('\n')精准读取单条指令
  • 提前将字符串转为整数,减少重复转换操作

2. 优化Python发送逻辑

修改Python代码,确保只发送纯指令字符,不带换行:

import serial, time
arduino = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
cmd = ''
while cmd != '0':
        cmd = input('Enter a cmd ').strip() # 移除输入的换行和空格
        arduino.write(cmd.encode('ascii'))
        time.sleep(0.1) # 给Arduino预留处理时间

3. 修复中断变量读取的原子性

在所有读取counter_A和counter_B的地方,使用临界区保护,以MoveForward为例:

void MoveForward(int steps, int mspeed) 
{
   // 关闭中断后重置计数器,避免被中断干扰
   noInterrupts();
   counter_A = 0;
   counter_B = 0;
   interrupts();
   
   // Set Motor A forward
   digitalWrite(in1, HIGH);
   digitalWrite(in2, LOW);
 
   // Set Motor B forward
   digitalWrite(in3, HIGH);
   digitalWrite(in4, LOW);
   
   // Go forward until step value is reached
   while (true) {
      // 读取计数器时关闭中断
      noInterrupts();
      int currentA = counter_A;
      int currentB = counter_B;
      interrupts();
      
      bool aDone = currentA >= steps;
      bool bDone = currentB >= steps;
      
      if (aDone && bDone) break; // 两个电机都达标才退出循环
      
      analogWrite(enA, aDone ? 0 : mspeed);
      analogWrite(enB, bDone ? 0 : mspeed);
   }
    
  // Stop when done
  analogWrite(enA, 0);
  analogWrite(enB, 0);
  
  noInterrupts();
  counter_A = 0;
  counter_B = 0;
  interrupts();
}
  • 所有对counter_A和counter_B的读写操作都用noInterrupts()和interrupts()包裹,保证原子性
  • 优化循环退出条件,确保两个电机都完成步数后才停止

4. 统一电机函数的循环逻辑

将MoveReverse、SpinRight、SpinLeft的while循环条件改为和MoveForward一致的逻辑,避免因&&导致的提前停止问题。


验证步骤

  1. 上传修改后的Arduino代码,确保无编译错误
  2. 运行修改后的Python脚本,输入1、2、3测试各动作
  3. 若仍有问题,可在电机函数中打印steps、currentA、currentB的值,验证计数器是否正常递增,排查传感器或接线问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:55:17