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

如何在自制金属探测器中集成舵机?Arduino代码调试求助

金属探测器舵机改造问题解决

问题分析

你修改的代码存在几个致命错误,直接导致设备失效:

  • 无效的条件判断:if (pin_LED, LOW) 是错误的C++语法,逗号表达式会直接取后值,相当于永远为真,舵机持续触发0度动作,再加上delay(1000),完全阻塞了金属检测的高频采样循环,核心功能根本无法运行。
  • 逻辑位置错误:舵机控制代码放在了单次测量的循环内部,每一次采样都执行一次舵机动作+延时,直接打断了金属检测的核心流程。
  • 无状态控制:没有设置触发标志位,会反复触发舵机动作,无法实现「检测到金属后动作一次、停留后复位」的需求。

修正后的代码

/*
 * 金属探测器 - 舵机版
 */
#include <Servo.h>

Servo servo1;
const int servoPin = 9;
const int SERVO_INIT_ANGLE = 0;    // 舵机初始角度
const int SERVO_TRIGGER_ANGLE = 90; // 检测到金属时的转动角度
const long SERVO_STAY_TIME = 1000;  // 停留时间(毫秒)

const byte npulse = 12; // 每次测量前给电容充电的脉冲数
const byte pin_pulse = A0; // 给电容充电的脉冲引脚
const byte pin_cap  = A1; // 测量电容电量的引脚
const byte pin_LED = 12; // 金属检测提示LED

// 舵机状态控制变量
bool isServoTriggered = false;
unsigned long servoTriggerTime = 0;

void setup() {
  servo1.attach(servoPin);
  servo1.write(SERVO_INIT_ANGLE); // 初始化舵机到初始位置

  pinMode(pin_pulse, OUTPUT);
  digitalWrite(pin_pulse, LOW);
  pinMode(pin_cap, INPUT);
  pinMode(pin_LED, OUTPUT);
  digitalWrite(pin_LED, LOW);
}

const int nmeas = 256; // 每次采样的测量次数
long int sumsum = 0; // 64次采样的累计和
long int skip = 0; // 跳过的采样次数
long int diff = 0;      // 当前采样和平均采样的差值
long int flash_period = 0; // LED闪烁周期(毫秒)
unsigned long prev_flash = 0; // 上次闪烁的时间戳

void loop() {
  int minval = 2000;
  int maxval = 0;
  unsigned long sum = 0;

  // 执行测量采样
  for (int imeas = 0; imeas < nmeas + 2; imeas++) {
    // 重置电容
    pinMode(pin_cap, OUTPUT);
    digitalWrite(pin_cap, LOW);
    delayMicroseconds(20);
    pinMode(pin_cap, INPUT);

    // 给电容充电
    for (int ipulse = 0; ipulse < npulse; ipulse++) {
      digitalWrite(pin_pulse, HIGH);
      delayMicroseconds(3);
      digitalWrite(pin_pulse, LOW);
      delayMicroseconds(3);
    }

    // 读取电容电量
    int val = analogRead(pin_cap);
    minval = min(val, minval);
    maxval = max(val, maxval);
    sum += val;

    // 确定LED状态
    unsigned long timestamp = millis();
    byte ledstat = 0;
    if (timestamp < prev_flash + 12) {
      if (diff > 0) ledstat = 1;
      if (diff < 0) ledstat = 2;
    }
    if (timestamp > prev_flash + flash_period) {
      if (diff > 0) ledstat = 1;
      if (diff < 0) ledstat = 2;
      prev_flash = timestamp;
    }
    if (flash_period > 1000) ledstat = 0;

    // 设置LED状态
    digitalWrite(pin_LED, (ledstat == 2) ? HIGH : LOW);
  }

  // 去除采样中的极值(尖峰)
  sum -= minval;
  sum -= maxval;

  // 处理采样数据,更新基准值
  if (sumsum == 0) sumsum = sum << 6; // 初始化基准值
  long int avgsum = (sumsum + 32) >> 6;
  diff = sum - avgsum;

  if (abs(diff) < (avgsum >> 10)) {   // 小偏差时更新基准值
    sumsum = sumsum + sum - avgsum;
    skip = 0;
  } else {
    skip++;
  }
  if (skip > 64) {  // 长时间偏差过大时重置基准值
    sumsum = sum << 6;
    skip = 0;
  }

  // 计算LED闪烁周期
  if (diff == 0) flash_period = 1000000;
  else flash_period = avgsum / (2 * abs(diff));

  // 舵机控制逻辑(非阻塞)
  byte currentLedStat = (digitalRead(pin_LED) == HIGH) ? 2 : 0;
  if (currentLedStat == 2 && !isServoTriggered) {
    // 检测到金属,触发舵机
    servo1.write(SERVO_TRIGGER_ANGLE);
    isServoTriggered = true;
    servoTriggerTime = millis();
  } else if (isServoTriggered && (millis() - servoTriggerTime >= SERVO_STAY_TIME)) {
    // 停留时间到,复位舵机
    servo1.write(SERVO_INIT_ANGLE);
    isServoTriggered = false;
  }
}

关键修改说明

  1. 修复条件判断:用digitalRead(pin_LED)正确读取LED状态,替代原来的无效语法。
  2. 非阻塞延时:用millis()实现舵机停留计时,避免delay()阻塞金属检测的核心采样流程。
  3. 状态控制:添加isServoTriggered标志位,确保每次检测到金属只触发一次舵机动作,不会重复执行。
  4. 逻辑位置调整:把舵机控制移到采样处理完成之后,确保只有在确定检测到金属时才执行动作,不干扰高频采样。
  5. 初始化舵机:在setup()里直接把舵机转到初始位置,避免开机时的不确定状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:15:32