如何在自制金属探测器中集成舵机?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; } }
关键修改说明
- 修复条件判断:用
digitalRead(pin_LED)正确读取LED状态,替代原来的无效语法。 - 非阻塞延时:用
millis()实现舵机停留计时,避免delay()阻塞金属检测的核心采样流程。 - 状态控制:添加
isServoTriggered标志位,确保每次检测到金属只触发一次舵机动作,不会重复执行。 - 逻辑位置调整:把舵机控制移到采样处理完成之后,确保只有在确定检测到金属时才执行动作,不干扰高频采样。
- 初始化舵机:在
setup()里直接把舵机转到初始位置,避免开机时的不确定状态。
内容的提问来源于stack exchange,提问作者Nate
相关产品推荐
相关产品推荐

