【花雕学编程】Arduino BLDC 之超声波矩阵动态跟随机器人

在基于Arduino与BLDC(无刷直流电机)构建的超声波矩阵动态跟随机器人系统中,多传感器阵列感知与底层高动态运动控制实现了深度融合。从专业视角来看,该系统通过空间多点测距数据融合与BLDC电机的精准控制,实现了在非结构化环境中的平滑、安全跟随。以下是关于该技术的详细解析:
一、 主要特点
360°无死角感知矩阵与目标锁定
系统在底盘四周(通常为4-8个方向)均匀布置超声波传感器,形成环形检测矩阵。通过分时触发或组触发策略消除声波串扰,并结合空间插值算法构建连续的“距离环”。系统不仅能识别最近点,还能通过分析距离矩阵的几何特征(如连续扇区、V形缺口)来精准锁定目标(如行人的腿部),并利用滤波算法预测运动轨迹,防止因短暂遮挡导致跟丢。
BLDC高动态响应与差速平滑控制
相较于传统有刷电机,BLDC电机配合FOC(磁场定向控制)算法具备毫秒级的扭矩响应速度和极低速下的平稳运行能力。系统采用双环控制(外环角度纠偏,内环速度维持),将感知到的距离和角度偏差转化为左右轮的速度指令。在目标快速移动或距离过近时,系统能自动调整最大允许速度和转向灵敏度,实现“近慢远快”的安全跟随策略,确保轨迹平滑无锯齿。
多维度的安全避障与冗余防护
超声波矩阵在跟随过程中同时承担避障功能。当检测到侧向或前方有突发障碍时,系统会动态调整跟随路径,实现绕行跟随而非死板走直线。此外,系统设计了多级安全距离阈值与紧急制动机制,并在物理层面(如最前端加装微动开关)提供最后一道硬件保护,防止目标过于接近进入超声波盲区时发生碰撞。
二、 应用场景
智能随行载物与个人服务助理
在机场、车站、超市或校园等场景,机器人可作为智能行李车或购物车,自动锁定用户腿部并保持在1-2米的安全距离跟随。系统能在拥挤的人流中准确锁定目标并绕开周围障碍物,有效解放用户双手。
工业物料牵引与柔性物流
在工厂车间或仓储环境中,机器人可跟随工人移动,承载工具或零部件,实现“人走到哪,物料送到哪”。相比磁条导航,该方案无需铺设固定轨道,且超声波不受油污、粉尘影响,能够适应恶劣的工业环境。
安防巡逻与特殊环境辅助
作为移动基座,机器人可跟随安保人员或清洁工人进行伴随式巡逻与工作,扩展监控视野或携带应急设备。在农业采摘场景中,机器人还能跟随采摘工人自动运输果实筐,适应户外的复杂地形与植物叶片干扰。
三、 需要注意的事项
传感器物理局限与多路串扰
超声波存在近场盲区(通常2-10cm)和最大量程限制,且在光滑表面易产生镜面反射,在柔软表面易被吸收。此外,多个传感器同时发射会导致严重的信号串扰。对策是必须采用严格的时分复用机制(依次触发探头),引入温度补偿修正声速,并结合中值滤波等算法剔除野值。
主控算力瓶颈与实时性保障
读取多路超声波、执行滤波算法、解算运动学并控制多个BLDC电机,对微控制器的资源消耗极大。基础的Arduino Uno等8位单片机极易导致控制周期过长(>100ms),使机器人运动卡顿。对于复杂的矩阵跟随,强烈建议使用ESP32、STM32等具备更高主频或双核架构的MCU,并采用非阻塞式编程与硬件中断来保障实时性。
电磁兼容(EMC)与机械减震
BLDC电机是强电磁干扰源,启动电流大且易产生反电动势。超声波模块的信号线必须远离电机驱动线,最好使用屏蔽线,并在电源端加装隔离DC-DC模块和去耦电容。同时,超声波探头对振动敏感,应使用金属支架并加装减震海绵,防止电机震动导致测距噪声。
目标丢失的状态机处理
在人群密集处容易跟错目标,或当目标被完全遮挡时机器人容易迷失。系统需设计合理的状态机:当目标丢失时,机器人不应盲目急停,而应记录最后位置并原地缓慢旋转扫描,利用矩阵的宽视场角重新捕获目标;若超时仍未找到,则自动进入待机状态并报警。

1、双超声波差速转向跟随(入门级方位感知)
适用场景:左右两个超声波传感器分别检测目标两侧距离,通过距离差计算偏航角,PID控制驱动差速底盘实现平滑转向跟随。
#include <SimpleFOC.h>
#include <NewPing.h>
#include <PID_v1.h>
// ==================== BLDC差速电机 ====================
BLDCMotor motorL(7), motorR(7);
// 需补充Encoder和Driver初始化...
// ==================== 双超声波传感器 ====================
#define LEFT_TRIG 2
#define LEFT_ECHO 3
#define RIGHT_TRIG 4
#define RIGHT_ECHO 5
#define MAX_DISTANCE 200
NewPing leftSonar(LEFT_TRIG, LEFT_ECHO, MAX_DISTANCE);
NewPing rightSonar(RIGHT_TRIG, RIGHT_ECHO, MAX_DISTANCE);
// ==================== PID控制器 ====================
double setpointAngle = 0; // 期望角度偏差(目标居中)
double angleError = 0; // 实际角度偏差(左-右距离差)
double turnOutput = 0;
double Kp_angle = 0.6, Ki_angle = 0.1, Kd_angle = 0.03;
PID anglePID(&angleError, &turnOutput, &setpointAngle,
Kp_angle, Ki_angle, Kd_angle, DIRECT);
// ==================== 跟随参数 ====================
const float TARGET_DISTANCE = 50.0; // 期望跟随距离(cm)
const float MAX_SPEED = 3.0; // 最大线速度(rad/s)
const float BASE_SPEED = 1.5;
void setup() {
Serial.begin(115200);
// BLDC电机初始化(速度控制模式)
motorL.controller = MotionControlType::velocity;
motorR.controller = MotionControlType::velocity;
motorL.init(); motorL.initFOC();
motorR.init(); motorR.initFOC();
// PID初始化
anglePID.SetMode(AUTOMATIC);
anglePID.SetOutputLimits(-0.8, 0.8);
}
void loop() {
motorL.loopFOC(); motorR.loopFOC();
// ==================== 1. 读取双超声波数据 ====================
float leftDist = leftSonar.ping_cm();
float rightDist = rightSonar.ping_cm();
// 数据有效性过滤
if (leftDist <= 0 || leftDist > 150) leftDist = 150;
if (rightDist <= 0 || rightDist > 150) rightDist = 150;
// ==================== 2. 【核心】角度偏差计算 ====================
// 左右距离差 → 目标偏角(左-右:正值表示目标偏左)
angleError = leftDist - rightDist;
anglePID.Compute();
// ==================== 3. 距离控制 ====================
float avgDist = (leftDist + rightDist) / 2.0;
float distError = TARGET_DISTANCE - avgDist;
// 距离控制:距离远则加速,距离近则减速
float speedFactor = constrain(distError / 30.0, -0.6, 0.6);
float targetSpeed = BASE_SPEED + speedFactor * BASE_SPEED;
targetSpeed = constrain(targetSpeed, 0.2, MAX_SPEED);
// 如果目标太远(>120cm),快速逼近
if (avgDist > 120) {
targetSpeed = MAX_SPEED;
}
// ==================== 4. 差速驱动 ====================
// 转向修正:turnOutput为正→左转(左轮减速,右轮加速)
float vL = targetSpeed - turnOutput;
float vR = targetSpeed + turnOutput;
// 近距离减速(防止碰撞)
if (avgDist < 25) {
vL *= 0.4;
vR *= 0.4;
}
motorL.move(constrain(vL, -MAX_SPEED, MAX_SPEED));
motorR.move(constrain(vR, -MAX_SPEED, MAX_SPEED));
// 调试输出
Serial.print("L:"); Serial.print(leftDist);
Serial.print(" R:"); Serial.print(rightDist);
Serial.print(" Err:"); Serial.print(angleError);
Serial.print(" Turn:"); Serial.print(turnOutput);
Serial.print(" Speed:"); Serial.println(targetSpeed);
delay(50);
}
核心要点:通过左右超声波的距离差计算目标偏航角,PID控制器将角度偏差平滑转换为转向修正量,实现差速转向跟随。这是超声波矩阵跟随系统最基础的“方位感知”方案。
2、多超声波矩阵跟随 + 目标锁定(3~5路阵列)
适用场景:3~5个超声波传感器组成弧形/环形阵列,通过矩阵测距识别目标方位和距离,实现更鲁棒的跟随逻辑,同时具备基础避障能力。
#include <SimpleFOC.h>
#include <NewPing.h>
// ==================== BLDC差速电机 ====================
BLDCMotor motorL(7), motorR(7);
// 需补充Encoder和Driver初始化...
// ==================== 超声波矩阵(5路环形) ====================
#define SONAR_NUM 5
#define MAX_DISTANCE 200
// 引脚定义:[左后, 左前, 正中, 右前, 右后]
const int TRIG_PINS[SONAR_NUM] = {2, 4, 6, 8, 10};
const int ECHO_PINS[SONAR_NUM] = {3, 5, 7, 9, 11};
NewPing sonar[SONAR_NUM] = {
NewPing(2, 3, MAX_DISTANCE),
NewPing(4, 5, MAX_DISTANCE),
NewPing(6, 7, MAX_DISTANCE),
NewPing(8, 9, MAX_DISTANCE),
NewPing(10, 11, MAX_DISTANCE)
};
// ==================== 跟随参数 ====================
const float TARGET_DIST = 50.0; // 目标跟随距离(cm)
const float MIN_DIST = 20.0; // 最小安全距离(cm)
const float MAX_LINEAR_SPEED = 2.0; // 最大线速度(rad/s)
// 传感器权重(弧形布局:中心权重最大)
const float WEIGHTS[SONAR_NUM] = {0.3, 0.8, 1.2, 0.8, 0.3};
void setup() {
Serial.begin(115200);
motorL.controller = MotionControlType::velocity;
motorR.controller = MotionControlType::velocity;
motorL.init(); motorL.initFOC();
motorR.init(); motorR.initFOC();
}
void loop() {
motorL.loopFOC(); motorR.loopFOC();
// ==================== 1. 扫描所有超声波(时分复用防串扰) ====================
float distances[SONAR_NUM];
float minDist = 999;
int minIdx = 0;
for (int i = 0; i < SONAR_NUM; i++) {
distances[i] = sonar[i].ping_cm();
if (distances[i] <= 0 || distances[i] > 150) distances[i] = 150;
if (distances[i] < minDist) {
minDist = distances[i];
minIdx = i;
}
delay(30); // 时分复用:避免传感器间串扰
}
// ==================== 2. 【核心】目标方位计算(加权质心法) ====================
// 使用加权平均计算目标相对方位(-1~1,负=偏左)
float weightedPos = 0;
float totalWeight = 0;
for (int i = 0; i < SONAR_NUM; i++) {
// 距离越近,权重越大
float distWeight = 1.0 / (distances[i] + 1.0);
float pos = (float)(i - 2) / 2.0; // 将索引映射到[-1, 1]
weightedPos += pos * distWeight * WEIGHTS[i];
totalWeight += distWeight * WEIGHTS[i];
}
float targetPosition = (totalWeight > 0) ? weightedPos / totalWeight : 0;
targetPosition = constrain(targetPosition, -1.0, 1.0);
// ==================== 3. 【核心】避障优先检测 ====================
if (minDist < MIN_DIST) {
// 前方障碍过近 → 急停避障
Serial.println("⚠️ 避障触发!");
motorL.move(0);
motorR.move(0);
delay(300);
// 向开阔方向后退
if (targetPosition < -0.3) {
motorL.move(-0.3); motorR.move(0.3); // 左转后退
} else if (targetPosition > 0.3) {
motorL.move(0.3); motorR.move(-0.3); // 右转后退
} else {
motorL.move(-0.5); motorR.move(-0.5); // 直行后退
}
delay(400);
motorL.move(0); motorR.move(0);
return;
}
// ==================== 4. 目标跟随控制 ====================
// 距离控制
float avgDist = minDist; // 使用最近距离代表目标距离
float distError = TARGET_DIST - avgDist;
float linearSpeed = constrain(distError * 0.03, 0.1, MAX_LINEAR_SPEED);
// 方位控制
float angularSpeed = constrain(targetPosition * 2.0, -1.0, 1.0);
// 近距离减速
if (avgDist < 35) {
linearSpeed *= 0.5;
}
// ==================== 5. 差速驱动 ====================
float wheelBase = 0.25;
motorL.move(linearSpeed - angularSpeed * wheelBase / 2);
motorR.move(linearSpeed + angularSpeed * wheelBase / 2);
// 调试
Serial.print("Min:"); Serial.print(minDist);
Serial.print(" Pos:"); Serial.print(targetPosition);
Serial.print(" Lin:"); Serial.print(linearSpeed);
Serial.print(" Ang:"); Serial.println(angularSpeed);
delay(50);
}
核心要点:采用时分复用触发策略避免多超声波串扰,通过加权质心法综合多传感器数据计算目标方位,避障逻辑具备硬优先级—前方障碍过近时无条件挂起跟随。
3、预测性矩阵跟随 + 卡尔曼滤波轨迹预测
适用场景:目标可能被短暂遮挡或快速移动,引入卡尔曼滤波预测目标运动轨迹,在传感器数据丢失时仍能维持短时跟随,提高系统鲁棒性。
#include <SimpleFOC.h>
#include <NewPing.h>
// ==================== BLDC差速电机 ====================
BLDCMotor motorL(7), motorR(7);
// 需补充Encoder和Driver初始化...
// ==================== 超声波矩阵(5路) ====================
#define SONAR_NUM 5
NewPing sonar[SONAR_NUM] = {
NewPing(2, 3, 200), NewPing(4, 5, 200), NewPing(6, 7, 200),
NewPing(8, 9, 200), NewPing(10, 11, 200)
};
// ==================== 卡尔曼滤波器(一维位置跟踪)====================
class KalmanFilter {
private:
float x; // 状态估计
float P; // 估计误差协方差
float Q; // 过程噪声
float R; // 测量噪声
public:
KalmanFilter(float init_x, float q, float r)
: x(init_x), P(1.0), Q(q), R(r) {}
float update(float z) {
// 预测
P = P + Q;
// 更新
float K = P / (P + R);
x = x + K * (z - x);
P = (1 - K) * P;
return x;
}
};
KalmanFilter kfPos(50.0, 0.05, 1.0); // 位置滤波器
KalmanFilter kfVel(0.0, 0.02, 0.5); // 速度滤波器
// ==================== 跟随参数 ====================
const float TARGET_DIST = 50.0;
const float MAX_SPEED = 2.5;
float lastDist = 0;
unsigned long lastTime = 0;
void setup() {
Serial.begin(115200);
motorL.controller = MotionControlType::velocity;
motorR.controller = MotionControlType::velocity;
motorL.init(); motorL.initFOC();
motorR.init(); motorR.initFOC();
}
void loop() {
motorL.loopFOC(); motorR.loopFOC();
// ==================== 1. 读取传感器 ====================
float distances[SONAR_NUM];
for (int i = 0; i < SONAR_NUM; i++) {
distances[i] = sonar[i].ping_cm();
if (distances[i] <= 0 || distances[i] > 150) distances[i] = 150;
delay(25);
}
// 取最小距离作为目标距离
float minDist = 999;
for (int i = 0; i < SONAR_NUM; i++) {
if (distances[i] < minDist) minDist = distances[i];
}
// ==================== 2. 【核心】卡尔曼滤波预测 ====================
unsigned long now = millis();
float dt = (now - lastTime) / 1000.0;
if (dt > 0.5) dt = 0.05; // 限制最大步长
lastTime = now;
// 位置滤波
float filteredDist = kfPos.update(minDist);
// 速度估计(通过位置差分)
if (dt > 0.01) {
float rawVel = (filteredDist - lastDist) / dt;
float filteredVel = kfVel.update(rawVel);
lastDist = filteredDist;
}
// ==================== 3. 预测性控制 ====================
// 根据预测速度前馈补偿
float predictedDist = filteredDist + kfVel.x * 0.5; // 预测0.5秒后的位置
float distError = TARGET_DIST - predictedDist;
float linearSpeed = constrain(distError * 0.03, 0.1, MAX_SPEED);
// 如果预测距离过近(目标正在快速接近),提前减速
if (predictedDist < 35) {
linearSpeed *= 0.3;
}
// ==================== 4. 方位控制(简化)====================
float pos = 0;
float totalW = 0;
for (int i = 0; i < SONAR_NUM; i++) {
float w = 1.0 / (distances[i] + 1.0);
pos += (i - 2) / 2.0 * w;
totalW += w;
}
float targetPos = (totalW > 0) ? pos / totalW : 0;
float angularSpeed = constrain(targetPos * 2.5, -1.0, 1.0);
// ==================== 5. 驱动执行 ====================
float wheelBase = 0.25;
motorL.move(linearSpeed - angularSpeed * wheelBase / 2);
motorR.move(linearSpeed + angularSpeed * wheelBase / 2);
// 调试
Serial.print("Raw:"); Serial.print(minDist);
Serial.print(" Filter:"); Serial.print(filteredDist);
Serial.print(" Vel:"); Serial.print(kfVel.x);
Serial.print(" Pred:"); Serial.print(predictedDist);
Serial.print(" Lin:"); Serial.println(linearSpeed);
delay(40);
}
核心要点:引入卡尔曼滤波对目标距离和运动速度进行状态估计,在传感器数据波动时提供平滑输出,在目标短暂被遮挡时维持短时预测跟随。这种“预测性跟随”策略在目标快速变速时能有效减少响应滞后。
要点解读
-
超声波矩阵的核心优势在于解决“方位感知”问题
单一超声波传感器只能测距,无法判断目标方位。通过矩阵化布局(3~5路甚至更多),利用不同传感器测距值的差异解算目标相对于机器人中轴线的偏航角,实现“看到”目标位置而非仅仅是距离。 -
时分复用是解决多超声波串扰的工程铁律
当多个传感器同时发射时,A传感器可能接收到B传感器的回波,导致测距完全错误。必须采用严格的状态机依次触发,等待回波接收完成后再触发下一个,牺牲部分采样频率换取数据可靠性。 -
传感器布局设计直接影响跟随精度
最优布局是弧形/扇形阵列(前密后疏),前方传感器间距较密以提高近距离定位精度,后方传感器用于辅助判断目标是否丢失。案例二的加权质心法通过差异化权重分配进一步补偿安装角度偏差。 -
卡尔曼滤波是提升系统鲁棒性的关键
超声波数据存在噪声和偶发跳变,直接使用原始数据会导致转向抖动。引入卡尔曼滤波或中值滤波进行数据平滑,可在目标短暂被遮挡时维持预测性跟随,防止系统“跟丢”。 -
BLDC FOC是实现“平滑跟随”的执行保障
跟随过程需要频繁加减速和微调方向,BLDC电机配合FOC控制实现毫秒级扭矩响应和低速平稳运行。案例中的差速驱动通过左右轮独立调速实现平滑转向,避免生硬的“一顿一顿”。

4、固定路径目标跟随——物流小车跟随货架
场景描述:工厂/仓库中,机器人跟随固定的货架(直线移动),超声波矩阵检测货架在前方的位置,调整速度与方向,保持安全跟随距离(0.5m)。适用于搬运小型货物,替代人工搬运。
#include <HCSR04.h> // 超声波传感器库(简化触发/回波处理)
#include <LiquidCrystal.h> // LCD显示库
// 超声波模块定义(Trigger→Echo)
HCSR04 ultrasonic1(2, 3);
HCSR04 ultrasonic2(4, 5);
HCSR04 ultrasonic3(6, 7);
HCSR04 ultrasonic4(8, 9);
// 驱动与显示初始化
LiquidCrystal lcd(18, 19, 20, 21, 22, 23);
#define LEFT_IN1 10
#define LEFT_IN2 11
#define LEFT_PWM 5
#define RIGHT_IN3 12
#define RIGHT_IN4 13
#define RIGHT_PWM 6
// PID参数(距离控制:目标距离0.5m,偏差=当前距离-0.5)
float Kp = 2.0, Ki = 0.1, Kd = 0.05;
float integral = 0, previous_error = 0;
// 目标距离与安全阈值
const float TARGET_DIST = 0.5; // 目标跟随距离
const float MAX_DIST = 3.0; // 最大检测距离
const float SAFE_DIST = 0.3; // 停止距离
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
pinMode(LEFT_IN1, OUTPUT);
pinMode(LEFT_IN2, OUTPUT);
pinMode(RIGHT_IN3, OUTPUT);
pinMode(RIGHT_IN4, OUTPUT);
pinMode(LEFT_PWM, OUTPUT);
pinMode(RIGHT_PWM, OUTPUT);
lcd.print("System Ready!");
delay(2000);
}
void loop() {
// 1. 计算超声波矩阵中心距离(4个传感器平均值)
float dist1 = ultrasonic1.getDistance();
float dist2 = ultrasonic2.getDistance();
float dist3 = ultrasonic3.getDistance();
float dist4 = ultrasonic4.getDistance();
float validDist[4] = {dist1, dist2, dist3, dist4};
int validCount = 0;
float sum = 0;
for (int i = 0; i < 4; i++) {
if (validDist[i] > 0.02 && validDist[i] < MAX_DIST) { // 过滤无效值
sum += validDist[i];
validCount++;
}
}
float centerDist = validCount > 0 ? sum / validCount : MAX_DIST; // 避免除零
// 2. PID计算转向量
float error = centerDist - TARGET_DIST;
integral += error;
float derivative = error - previous_error;
float output = Kp * error + Ki * integral + Kd * derivative;
previous_error = error;
// 3. 电机控制:差速转向,保持目标距离
float baseSpeed = 150; // 基础PWM值(0-255)
int leftPWM = constrain(baseSpeed + output, 0, 255);
int rightPWM = constrain(baseSpeed - output, 0, 255);
// 4. 状态逻辑:目标太近→停止;太远→加速
if (centerDist < SAFE_DIST) {
// 停止电机
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 0);
analogWrite(RIGHT_PWM, 0);
} else if (centerDist > MAX_DIST) {
// 全速前进找目标
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 255);
analogWrite(RIGHT_PWM, 255);
} else {
// 正常跟随:差速调整距离
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, leftPWM);
analogWrite(RIGHT_PWM, rightPWM);
}
// 5. 状态显示
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Dist: ");
lcd.print(centerDist);
lcd.print(" m");
lcd.setCursor(0, 1);
lcd.print("Error: ");
lcd.print(error);
Serial.print("Center Dist: ");
Serial.print(centerDist);
Serial.print(" | Error: ");
Serial.print(error);
Serial.print(" | Left PWM: ");
Serial.print(leftPWM);
Serial.print(" | Right PWM: ");
Serial.println(rightPWM);
delay(100); // 采样周期100ms
}
案例说明:
核心逻辑:用4个超声波的平均距离代表目标整体位置,PID算法控制差速,使机器人保持0.5m的跟随距离;
扩展优化:可加入编码器实现速度闭环,提升跟随稳定性;添加蜂鸣器提醒避障。
5、动态目标跟随——宠物互动跟随机器人
场景描述:家庭环境中,机器人跟随宠物(猫/狗)移动,通过超声波矩阵判断宠物的左右方位,动态调整转向,避免碰撞家具,保持0.3m的亲密距离。适用于宠物互动、家庭陪伴。
#include <HCSR04.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // I2C LCD,减少接线
// 超声波模块(左前/右前/左后/右后?不,改为**左右覆盖**:左传感器测左前方,右传感器测右前方)
// 优化:2个超声波覆盖左右,检测目标方位(左侧距离小→目标在左,需左转)
HCSR04 ultrasonicLeft(2, 3); // 左传感器:覆盖左45°
HCSR04 ultrasonicRight(4, 5); // 右传感器:覆盖右45°
// 驱动与显示
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C地址默认0x27
#define LEFT_IN1 10
#define LEFT_IN2 11
#define LEFT_PWM 6
#define RIGHT_IN3 12
#define RIGHT_IN4 13
#define RIGHT_PWM 7
// 目标跟随参数
const float TARGET_DIST = 0.3; // 跟随距离
const float SPEED_PWM = 180; // 基础速度
const float TURN_AMPLITUDE = 30;// 转向幅度(PWM调整量)
// 状态机:检测目标方向
enum RobotState { STOPPED, FOLLOWING, SEARCHING };
RobotState currentState = SEARCHING;
void setup() {
Serial.begin(9600);
Wire.begin();
lcd.init();
lcd.backlight();
pinMode(LEFT_IN1, OUTPUT);
pinMode(LEFT_IN2, OUTPUT);
pinMode(RIGHT_IN3, OUTPUT);
pinMode(RIGHT_IN4, OUTPUT);
pinMode(LEFT_PWM, OUTPUT);
pinMode(RIGHT_PWM, OUTPUT);
lcd.print("Pet Robot Ready");
delay(2000);
}
void loop() {
// 1. 检测目标:左/右传感器最小距离代表目标方位
float distLeft = ultrasonicLeft.getDistance();
float distRight = ultrasonicRight.getDistance();
float minDist = min(distLeft, distRight);
float direction = distLeft - distRight; // 正→目标在左,负→目标在右
// 2. 状态切换
if (minDist > 2.0) {
currentState = SEARCHING; // 目标太远,搜索
} else if (minDist < 0.2) {
currentState = STOPPED; // 目标太近,停止
} else {
currentState = FOLLOWING; // 正常跟随
}
// 3. 状态逻辑
switch (currentState) {
case SEARCHING:
// 全速前进,寻找目标
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 255);
analogWrite(RIGHT_PWM, 255);
lcd.clear();
lcd.print("Searching...");
Serial.println("State: Searching");
break;
case STOPPED:
// 停止,避免碰撞
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 0);
analogWrite(RIGHT_PWM, 0);
lcd.clear();
lcd.print("Stop! Too Close");
Serial.println("State: Stopped");
delay(500); // 停止0.5秒,避免频繁启停
break;
case FOLLOWING:
// 根据方向差速转向,保持目标在前方
int leftPWM, rightPWM;
if (direction > 5) { // 目标在左,左转(左慢右快)
leftPWM = SPEED_PWM - TURN_AMPLITUDE;
rightPWM = SPEED_PWM + TURN_AMPLITUDE;
} else if (direction < -5) { // 目标在右,右转(左快右慢)
leftPWM = SPEED_PWM + TURN_AMPLITUDE;
rightPWM = SPEED_PWM - TURN_AMPLITUDE;
} else { // 目标在中间,匀速前进
leftPWM = SPEED_PWM;
rightPWM = SPEED_PWM;
}
// 电机控制(前进方向)
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, constrain(leftPWM, 0, 255));
analogWrite(RIGHT_PWM, constrain(rightPWM, 0, 255));
// 显示状态
lcd.clear();
lcd.print("Following! Dist:");
lcd.print(minDist);
lcd.print(" m");
Serial.print("Direction: ");
Serial.print(direction);
Serial.print(" | Left PWM: ");
Serial.print(leftPWM);
Serial.print(" | Right PWM: ");
Serial.println(rightPWM);
break;
}
delay(80); // 更快响应动态目标
}
案例说明:
核心逻辑:用2个超声波覆盖左右方位,方向差决定转向,适用于快速移动的动态目标(如宠物);
优化:可加入红外传感器辅助检测目标(避免超声波盲区),或增加陀螺仪提升转向精度;加入避障逻辑(前方障碍物→停止)。
6、多目标选择跟随——超市导购机器人
场景描述:超市中,机器人需跟随“指定的顾客”(通过佩戴RFID标签识别),当顾客在货架间移动时,机器人通过超声波矩阵跟踪顾客位置,自动避开障碍物,同时支持切换跟随目标(如更换顾客)。
代码实现(简化RFID逻辑,重点展示多目标选择与跟随)
#include <HCSR04.h>
#include <RFID.h> // RFID识别库(需导入MFRC522库)
#include <LiquidCrystal.h>
// 超声波:4个覆盖前方180°
HCSR04 ultrasonic1(2, 3);
HCSR04 ultrasonic2(4, 5);
HCSR04 ultrasonic3(6, 7);
HCSR04 ultrasonic4(8, 9);
// RFID配置(SPI接口,D10-D13)
MFRC522 mfrc522(10, 53); // SS→D10, SCK→D53(Mega硬件SPI)
// 驱动与显示
LiquidCrystal lcd(18, 19, 20, 21, 22, 23);
#define LEFT_IN1 12
#define LEFT_IN2 13
#define LEFT_PWM 4
#define RIGHT_IN3 24
#define RIGHT_IN4 25
#define RIGHT_PWM 5
// 多目标与跟随参数
struct Target {
byte uid[4]; // RFID UID
char name[10];// 目标名称(如“顾客A”)
};
Target targets[2] = {
{{0x12, 0x34, 0x56, 0x78}, "CustomerA"},
{{0xAB, 0xCD, 0xEF, 0x12}, "CustomerB"}
};
byte currentTargetUid[4]; // 当前跟随目标的UID
bool targetLocked = false;
// 跟随控制参数
const float TARGET_DIST = 0.6;
const float MAX_DIST = 3.0;
float Kp = 1.5, Ki = 0.08, Kd = 0.03;
float integral = 0, prev_error = 0;
void setup() {
Serial.begin(9600);
SPI.begin();
mfrc522.PCD_Init();
lcd.begin(16, 2);
pinMode(LEFT_IN1, OUTPUT);
pinMode(LEFT_IN2, OUTPUT);
pinMode(RIGHT_IN3, OUTPUT);
pinMode(RIGHT_IN4, OUTPUT);
pinMode(LEFT_PWM, OUTPUT);
pinMode(RIGHT_PWM, OUTPUT);
lcd.print("Scan RFID to Start");
Serial.println("RFID Reader Ready");
// 初始化目标UID(默认跟随第一个目标)
memcpy(currentTargetUid, targets[0].uid, 4);
targetLocked = true;
}
void loop() {
// 1. RFID:检测目标卡,切换跟随目标
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) return;
byte cardUid[4];
memcpy(cardUid, mfrc522.uid.uidByte, 4);
// 验证卡是否为目标
for (int i = 0; i < 2; i++) {
if (memcmp(cardUid, targets[i].uid, 4) == 0) {
memcpy(currentTargetUid, targets[i].uid, 4);
targetLocked = true;
lcd.clear();
lcd.print("Locked: ");
lcd.print(targets[i].name);
Serial.print("Locked target: ");
Serial.println(targets[i].name);
break;
}
}
// 2. 超声波:计算目标距离与方向(仅跟踪锁定的目标)
if (!targetLocked) {
// 未锁定目标,搜索模式
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 200);
analogWrite(RIGHT_PWM, 200);
lcd.setCursor(0, 1);
lcd.print("Searching...");
delay(100);
return;
}
// 计算目标中心距离与方向
float dist1 = ultrasonic1.getDistance();
float dist2 = ultrasonic2.getDistance();
float dist3 = ultrasonic3.getDistance();
float dist4 = ultrasonic4.getDistance();
// 过滤无效值,计算中心距离
float valid[4] = {dist1, dist2, dist3, dist4};
float sum = 0, cnt = 0;
for (int i = 0; i < 4; i++) {
if (valid[i] > 0.02 && valid[i] < MAX_DIST) {
sum += valid[i];
cnt++;
}
}
float centerDist = cnt > 0 ? sum / cnt : MAX_DIST;
// 方向:左前/右前距离差→目标方位
float direction = (dist1 + dist3) - (dist2 + dist4); // 左传感器和 - 右传感器和
// 3. PID控制:调整距离与方向
float error = centerDist - TARGET_DIST;
integral += error;
float derivative = error - prev_error;
float distanceOutput = Kp * error + Ki * integral + Kd * derivative;
prev_error = error;
// 方向输出(正→左转,负→右转)
float directionOutput = direction * 0.02; // 缩放因子,适配PWM范围
// 4. 电机控制:距离+方向双重调整
int leftPWM = constrain(150 + distanceOutput + directionOutput, 0, 255);
int rightPWM = constrain(150 + distanceOutput - directionOutput, 0, 255);
// 停止逻辑:目标消失或太近
if (centerDist > MAX_DIST || centerDist < 0.2) {
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, 0);
analogWrite(RIGHT_PWM, 0);
lcd.setCursor(0, 1);
lcd.print("Target Lost!");
} else {
// 正常跟随:前进+差速调整
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
analogWrite(LEFT_PWM, leftPWM);
analogWrite(RIGHT_PWM, rightPWM);
// 显示状态
lcd.clear();
lcd.print("Follow: ");
lcd.print(targets[0].name);
lcd.setCursor(0, 1);
lcd.print("Dist: ");
lcd.print(centerDist);
lcd.print(" Dir: ");
lcd.print(direction > 0 ? "L" : "R");
}
delay(100);
}
案例说明:
核心逻辑:通过RFID识别目标身份,实现“多目标选择”,结合超声波矩阵跟踪目标位置,支持切换跟随对象;
扩展优化:可增加激光雷达提升障碍物识别精度,或加入语音模块(“正在跟随顾客A”)提升交互性;集成路径规划算法,避开超市货架障碍。
要点解读
- 超声波矩阵:从“点检测”到“面感知”的关键
作用:单传感器只能测“点”距离,矩阵(多传感器阵列)能覆盖宽角度范围(如4个传感器覆盖180°),通过传感器数据差(如左右传感器距离差)判断目标方位,实现“面感知”。
设计技巧:
传感器布局:按等角度分布(如4个传感器间距45°),覆盖核心检测区域;
数据融合:用平均值法(多传感器取平均)过滤无效值(超量程、干扰),用差值法判断方向(左传感器距离小→目标在左);
采样频率:超声波检测周期≥100ms(HC-SR04单次检测需约10ms),多传感器需循环触发,避免相互干扰。 - BLDC驱动:动力与转向的核心执行器
作用:BLDC电机(无刷直流电机)相比有刷电机,具有效率高、寿命长、速度可调的优势,是机器人动力的核心;驱动模块(如L298N)将Arduino的PWM信号转换为电机的电压/电流,控制电机转速与方向。
控制逻辑:
方向控制:通过H桥电路(L298N)的IN1/IN2组合实现电机正反转(IN1=HIGH、IN2=LOW→正转;IN1=LOW、IN2=HIGH→反转);
速度控制:用PWM信号(0-255)调整驱动模块的EN引脚电压,PWM越大,电机转速越快;
差速转向:机器人转向的核心是左右轮速度差(左轮慢、右轮快→右转;反之左转),需确保左右电机参数一致,避免偏航。 - 跟随算法:PID是动态稳定的核心
问题痛点:动态目标(如宠物)距离会实时变化,直接用“距离差=速度差”的开环控制,会导致机器人超调(距离忽远忽近)或震荡(频繁转向)。
PID解决方案:
P(比例):快速响应距离偏差(偏差大→输出大,快速调整距离);
I(积分):消除稳态误差(长期小偏差积累,让机器人稳定在目标距离);
D(微分):抑制超调(感知偏差变化率,提前减速,避免震荡)。
调参技巧:
先调P:从小到大,直到机器人出现震荡;
再调D:从0增大,直到震荡消失;
最后调I:从0增大,直到稳态误差消除(目标距离稳定在±0.1m内)。 - 动态响应:从“检测”到“执行”的实时性
关键要求:动态跟随的核心是实时响应——目标移动速度快,机器人需在短时间内完成“检测→计算→执行”,否则会丢失目标或碰撞。
优化策略:
硬件层面:选择高性能控制器(Arduino Mega比Uno快,处理多传感器更高效);用硬件PWM(比软件PWM稳定)控制电机;
软件层面:
精简算法:避免复杂的浮点运算(用整数运算替代,如将距离乘以100转为整数);
提高采样频率:将检测周期从100ms缩短到50ms(但需确保超声波传感器不会超负荷);
状态机:用状态机管理逻辑(如“搜索→跟随→停止”),避免逻辑混乱,提升响应速度。 - 安全与鲁棒性:避免失效的核心保障
问题场景:机器人在实际应用中可能遇到各种异常——目标消失、障碍物遮挡、传感器干扰、电源电压下降,若没有安全机制,会导致机器人失控(碰撞、损坏)。
安全机制设计:
目标丢失处理:当所有传感器检测距离超过MAX_DIST(如3m),进入搜索模式(全速前进寻找目标);若持续丢失目标,停止电机,避免盲目移动;
碰撞保护:当任何传感器检测距离小于SAFE_DIST(如0.2m),立即停止电机,防止碰撞;
传感器冗余:增加红外传感器、激光雷达等辅助传感器,补充超声波盲区(如近距离检测),提升抗干扰能力;
硬件保护:给电机驱动加保险丝(防止过流烧毁),给Arduino加过压保护电路(防止电源电压过高),用光电编码器实现电机闭环控制(防止电机堵转)。
请注意:以上案例仅作为思路拓展的参考示例,不保证完全正确、适配所有场景或可直接编译运行。由于硬件平台、实际使用场景、Arduino 版本的差异,均可能影响代码的适配性与使用方法的选择。在实际编程开发时,请务必根据自身硬件配置、使用场景及具体功能需求进行针对性调整,并通过多次实测验证效果;同时需确保硬件接线正确,充分了解所用传感器、执行器等设备的技术规范与核心特性。对于涉及硬件操作的代码,使用前务必核对引脚定义、电平参数等关键信息的准确性与安全性,避免因参数错误导致硬件损坏或运行异常。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)