跳到主要内容Arduino BLDC 超声波与 PID 控制跟随机器人实战 | 极客日志C++算法
Arduino BLDC 超声波与 PID 控制跟随机器人实战
基于 Arduino 的 BLDC 电机跟随机器人项目,通过超声波传感器采集环境数据,利用 PID 算法解算运动指令。内容涵盖三角测量感知逻辑、双环串级控制架构、多场景应用及核心代码实现。重点解析传感器抗干扰设计、PID 参数整定技巧与电源隔离方案,提供从单点跟随到自适应控制的完整工程参考。
栈溢出1 浏览 Arduino BLDC 超声波与 PID 控制跟随机器人实战
基于 Arduino 的无刷直流电机(BLDC)跟随机器人,是自动控制理论与现代驱动技术结合的典型项目。系统通过超声波传感器获取环境距离信息,利用 PID 算法实时解算运动指令,由 Arduino 驱动 BLDC 电机执行,实现对目标物体的稳定、平滑跟随。
系统特性
感知架构:三角测量与单发双收
这是实现'定向'跟随而非'盲目'避障的核心逻辑。系统通常采用一个手持式超声波发射模块和两个安装在机器人前端左右两侧的接收模块(单发双收)。这种布局构成了一个简单的三角形测量系统。当目标正对机器人时,左右两个接收模块测得的距离相等,系统可精确判断目标的偏航角度,从而实现方向控制。
控制策略:双环 PID 串级控制
为了实现平稳的跟随效果,系统采用速度环(内环)与方向环(外环)的串级 PID 控制结构。
- 方向环(外环):以左右超声波距离差为输入,通过 PD 或 PID 算法计算出所需的转向角速度(或左右轮速差),负责消除方向偏差,使机器人对准目标。
- 速度环(内环):以当前与目标的距离为输入,调节整体行进速度。
动力响应:BLDC 电机优势
相较于传统有刷电机,BLDC 电机为跟随机器人的动态响应和续航提供了坚实基础。
- 快速动态响应:高扭矩密度和快速的电磁时间常数,能迅速响应 PID 控制器发出的频繁加减速和转向指令,轨迹平滑无滞后。
- 长续航与低维护:无电刷磨损,效率高,发热量小,配合电子调速器(ESC)的高效驱动,适合长时间连续作业。

典型应用场景
- 智能行李箱与随行载具:在机场、车站或校园中,机器人底盘可作为智能行李车,自动跟随主人身后,解放双手。超声波方案成本低廉,且不受光线条件影响,适合室内外通用。
- 农业辅助与物料搬运:在农场或仓库,工人可以利用该机器人搬运工具或货物。通过简单的手持发射器,机器人能稳定地跟在工人身后,适应复杂的地形和环境变化。
- 教育与创客实践平台:作为高校《自动控制原理》、《传感器技术》和《机电一体化》课程的经典实验项目,学生可以通过搭建该系统,直观理解三角测量原理、PID 参数整定方法以及 BLDC 电机的控制特性。
- 展厅导览与广告跟随:在大型展会中,机器人可以搭载广告屏或展品,自动跟随讲解员移动,确保观众始终能清晰地看到展示内容,提升互动体验。
工程实践建议
传感器选型与安装
- 抗干扰设计:超声波传感器易受环境温度、湿度及气流影响,且多个超声波模块同时工作时可能产生串扰。建议在软件中加入时间片轮询机制或频率编码技术,并对采样数据进行滑动平均滤波或卡尔曼滤波,以剔除异常值。
- 安装位置:两个接收探头之间的基线距离应尽可能长(在结构允许范围内),以提高角度测量的分辨率。同时,探头应安装在机器人前端较高位置,避免地面反射干扰。
PID 参数整定与系统稳定性
- 积分饱和(Integral Windup):在跟随过程中,若机器人因障碍物或机械卡死无法到达目标位置,积分项会持续累积,导致解除卡死后机器人疯狂加速。必须加入积分限幅或积分分离策略。
- 微分项噪声:微分项对测量噪声敏感,容易引起电机抖动。可以适当降低微分增益,或在微分环节前加入一阶低通滤波器。
- 采样周期一致性:PID 控制的稳定性高度依赖于固定的控制周期。必须使用硬件定时器中断来触发 PID 计算,严禁使用 delay() 函数导致控制周期抖动。
电机驱动与电源管理
- 驱动器匹配:Arduino 的 IO 口无法直接驱动 BLDC 电机,必须通过电子调速器(ESC)。需确保 ESC 支持所需的通信协议(如 DShot、PWM),并正确配置其刹车和响应参数。
- 电源隔离:大电流的电机回路会产生电压波动,容易导致 Arduino 复位。建议使用独立的稳压模块(如 LM2596)为逻辑电路和电机电路分别供电,并共地处理。
代码实现示例
以下代码展示了不同场景下的核心逻辑,实际使用时请根据硬件引脚调整。
1. 单点超声波基本跟随
场景:固定跟随一个移动目标,保持设定距离。核心逻辑:超声波测距 + 单路 PID 速度控制。
#include <SimpleFOC.h>
#include <NewPing.h>
#include <PID_v1.h>
#define TRIG_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 200
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
BLDCMotor motorL(11);
BLDCMotor motorR(11);
double setpoint = 30.0;
double input, output;
double Kp = 0.5, Ki = 0.1, Kd = 0.05;
PID myPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(115200);
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-0.5, 0.5);
motorL.init();
motorR.init();
motorL.initFOC();
motorR.initFOC();
}
void loop() {
input = sonar.ping_cm();
if (input == 0) input = 100;
myPID.Compute();
float speedL = 0.2 + output;
float speedR = 0.2 + output;
speedL = constrain(speedL, -0.5, 0.5);
speedR = constrain(speedR, -0.5, 0.5);
motorL.move(speedL);
motorR.move(speedR);
motorL.loopFOC();
motorR.loopFOC();
Serial.print("Distance:");
Serial.print(input);
Serial.print(" PIDout:");
Serial.println(output);
}
2. 双超声波差速转向跟随
场景:不仅能前后跟随,还能在目标横向移动时自动转向。核心逻辑:左右双超声波 + 双路 PID(距离 PID + 角度 PID)。
#include <SimpleFOC.h>
#include <NewPing.h>
#include <PID_v1.h>
#define SONAR_L_TRIG 2
#define SONAR_L_ECHO 3
#define SONAR_R_TRIG 4
#define SONAR_R_ECHO 5
NewPing sonarL(SONAR_L_TRIG, SONAR_L_ECHO, 200);
NewPing sonarR(SONAR_R_TRIG, SONAR_R_ECHO, 200);
BLDCMotor motorL(11);
BLDCMotor motorR(11);
double setpointDist = 30.0, setpointAngle = 0.0;
double distInput, distOutput, angleInput, angleOutput;
PID pidDist(&distInput, &distOutput, &setpointDist, 0.4, 0.05, 0.02, DIRECT);
PID pidAngle(&angleInput, &angleOutput, &setpointAngle, 0.3, 0.01, 0.01, DIRECT);
void setup() {
Serial.begin(115200);
pidDist.SetMode(AUTOMATIC);
pidDist.SetOutputLimits(-0.4, 0.4);
pidAngle.SetMode(AUTOMATIC);
pidAngle.SetOutputLimits(-0.3, 0.3);
motorL.init();
motorR.init();
motorL.initFOC();
motorR.initFOC();
}
void loop() {
float distL = sonarL.ping_cm();
float distR = sonarR.ping_cm();
if (distL == 0) distL = 100;
if (distR == 0) distR = 100;
distInput = (distL + distR) / 2.0;
angleInput = distL - distR;
pidDist.Compute();
pidAngle.Compute();
float speedL = 0.2 + distOutput + angleOutput;
float speedR = 0.2 + distOutput - angleOutput;
speedL = constrain(speedL, -0.6, 0.6);
speedR = constrain(speedR, -0.6, 0.6);
motorL.move(speedL);
motorR.move(speedR);
motorL.loopFOC();
motorR.loopFOC();
}
3. 自适应 PID 跟随(带死区与饱和保护)
场景:工业级应用,加入非线性处理和异常保护。核心逻辑:死区控制 + PID 自适应 + 安全保护。
#include <SimpleFOC.h>
#include <NewPing.h>
#include <PID_v1.h>
double setpoint = 30.0;
double input, output;
PID myPID(&input, &output, &setpoint, 0.5, 0.1, 0.05, DIRECT);
float errorDeadZone = 2.0;
float errorThreshold = 20.0;
float maxSpeed = 0.4;
void setup() {
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-maxSpeed, maxSpeed);
}
void loop() {
input = sonar.ping_cm();
if (input == 0) { safetyStop(); return; }
float error = input - setpoint;
if (abs(error) < errorDeadZone) {
motorL.move(0);
motorR.move(0);
return;
}
if (abs(error) > errorThreshold) {
myPID.SetTunings(1.0, 0.0, 0.0);
} else {
myPID.SetTunings(0.5, 0.1, 0.05);
}
myPID.Compute();
float feedForward = 0.0;
if (error > 0) feedForward = 0.1;
float speedL = output + feedForward;
float speedR = output + feedForward;
speedL = constrain(speedL, -0.5, 0.5);
speedR = constrain(speedR, -0.5, 0.5);
motorL.move(speedL);
motorR.move(speedR);
motorL.loopFOC();
motorR.loopFOC();
}
void safetyStop() {
static float speedL = 0, speedR = 0;
speedL *= 0.9;
speedR *= 0.9;
motorL.move(speedL);
motorR.move(speedR);
}
关键要点解读
超声波传感器的局限与应对
回波干扰在狭小空间或面对吸音材料时可能发生。代码中应加入有效性校验,连续多次无效读数时紧急停车。此外,超声波锥形波束(约 15°)可能同时检测到障碍物和目标,双超声波方案可提供简单的横向定位,避免目标偏离中线时机器人'视而不见'。
PID 控制的三重调校
- P(比例):决定响应速度。P 值过小会导致跟随迟缓,过大则会在目标距离附近震荡。
- I(积分):消除稳态误差。如果机器人最终停在 32cm 而非 30cm,需增加 Ki。但 I 值过大会导致'积分饱和',响应滞后。
- D(微分):抑制超调。当目标突然靠近,微分项可提前减速。案例三的自适应 PID 在大误差时切为纯 P 控制,避免积分项累积造成危险。
'差速转向'是实现横向跟随的关键
angleInput = distL - distR 是核心。当目标偏左时,左声纳距离更近,angleInput 为负,pidAngle 输出负值,使左轮减速、右轮加速,从而实现转向。这种双 PID 混控是轮式机器人跟踪移动目标的经典方案。
非线性的必要处理
- 死区(Dead Zone):如案例三,±2cm 内不响应,避免机器人在目标距离附近'抽搐'。这是节省能量、减少机械磨损的关键。
- 输出限幅:PID 输出必须限制在电机安全范围内。案例一通过
SetOutputLimits() 限制,案例三通过 constrain() 实现平滑限幅,避免急启急停。
BLDC 闭环控制是精度保障
使用 analogWrite() 的开环控制,负载变化时速度会漂移,导致跟随距离波动。采用 SimpleFOC 库的闭环控制,电机速度严格跟随 motor.move() 的指令,这是 PID 算法能稳定工作的基础。编码器反馈的实时速度信息还可用于速度前馈,进一步提升响应。
其他参考方案
4. 超声波跟随小车(单传感器避障跟随)
适用于基础入门,核心在于 PWM 映射与基础 PID。
#include <NewPing.h>
#include <PID_v1.h>
#define TRIG_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 200
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
double setpoint = 30.0;
double input, output;
PID myPID(&input, &output, &setpoint, 2.0, 5.0, 1.0, DIRECT);
const int motorL_PWM = 5;
const int motorR_PWM = 6;
void setup() {
Serial.begin(9600);
myPID.SetMode(AUTOMATIC);
pinMode(motorL_PWM, OUTPUT);
pinMode(motorR_PWM, OUTPUT);
}
void loop() {
input = sonar.ping_cm();
if (input == 0) input = MAX_DISTANCE;
myPID.Compute();
int speed = constrain(output, -255, 255);
if (speed > 0) {
analogWrite(motorL_PWM, speed);
analogWrite(motorR_PWM, speed);
} else {
analogWrite(motorL_PWM, 0);
analogWrite(motorR_PWM, 0);
}
Serial.print("Distance: ");
Serial.print(input);
Serial.print(" -> Speed: ");
Serial.println(speed);
delay(50);
}
5. 双超声波传感器差速跟随
#include <NewPing.h>
#include <PID_v1.h>
#define LEFT_TRIG 12
#define LEFT_ECHO 11
#define RIGHT_TRIG 10
#define RIGHT_ECHO 9
#define MAX_DISTANCE 100
NewPing leftSonar(LEFT_TRIG, LEFT_ECHO, MAX_DISTANCE);
NewPing rightSonar(RIGHT_TRIG, RIGHT_ECHO, MAX_DISTANCE);
double leftDist, rightDist, error, output;
PID myPID(&error, &output, &setpoint, 1.5, 0.1, 0.5, DIRECT);
const int motorL_PWM = 5;
const int motorR_PWM = 6;
const int baseSpeed = 150;
void setup() {
Serial.begin(9600);
myPID.SetMode(AUTOMATIC);
pinMode(motorL_PWM, OUTPUT);
pinMode(motorR_PWM, OUTPUT);
}
void loop() {
leftDist = leftSonar.ping_cm();
rightDist = rightSonar.ping_cm();
if (leftDist == 0) leftDist = MAX_DISTANCE;
if (rightDist == 0) rightDist = MAX_DISTANCE;
error = leftDist - rightDist;
myPID.Compute();
int leftSpeed = constrain(baseSpeed - output, 0, 255);
int rightSpeed = constrain(baseSpeed + output, 0, 255);
analogWrite(motorL_PWM, leftSpeed);
analogWrite(motorR_PWM, rightSpeed);
Serial.print("Left: ");
Serial.print(leftDist);
Serial.print(" Right: ");
Serial.print(rightDist);
Serial.print(" -> L/R Speed: ");
Serial.print(leftSpeed);
Serial.print("/");
Serial.println(rightSpeed);
delay(50);
}
6. 带速度限制的智能跟随
#include <NewPing.h>
#include <PID_v1.h>
#define TRIG_PIN 12
#define ECHO_PIN 11
#define MAX_DISTANCE 150
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
double setpoint = 40.0;
double input, output;
PID myPID(&input, &output, &setpoint, 1.8, 0.5, 0.2, DIRECT);
const int motorL_PWM = 5;
const int motorR_PWM = 6;
const int maxSpeed = 200;
void setup() {
Serial.begin(9600);
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-maxSpeed, maxSpeed);
pinMode(motorL_PWM, OUTPUT);
pinMode(motorR_PWM, OUTPUT);
}
void loop() {
input = sonar.ping_cm();
if (input == 0) input = MAX_DISTANCE;
myPID.Compute();
int baseSpeed = map(constrain(input, 20, 100), 20, 100, 80, 200);
int leftSpeed = baseSpeed + output;
int rightSpeed = baseSpeed - output;
leftSpeed = constrain(leftSpeed, 0, maxSpeed);
rightSpeed = constrain(rightSpeed, 0, maxSpeed);
analogWrite(motorL_PWM, leftSpeed);
analogWrite(motorR_PWM, rightSpeed);
Serial.print("Distance: ");
Serial.print(input);
Serial.print(" -> L/R Speed: ");
Serial.print(leftSpeed);
Serial.print("/");
Serial.println(rightSpeed);
delay(30);
}
以上案例仅供参考,实际编程时需根据自己的硬件配置、使用场景和具体需求进行调整。涉及硬件操作的代码,要在使用前确认引脚和电平等参数的正确性和安全性。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online