6浏览
查看: 6|回复: 3

[项目] 【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)

[复制链接]
基于 Arduino 控制、搭载 6.5 寸无刷轮毂电机并集成蓝牙/WiFi 自动跟随功能的机器人底盘,是将高效动力系统与无线通信感知技术深度融合的典型智能移动平台。该系统不仅具备强大的物理移动能力,还能通过无线信号实现对目标的动态追踪。

一、主要特点
1、一体化高能效动力单元
6.5 寸无刷轮毂电机作为底盘的核心执行器,决定了系统的整体机动性能。
机电集成化: 将 BLDC 电机、行星减速器、轮毂及轴承集成于轮辋内部,省去了皮带、链条等中间传动环节。这种“直驱”结构不仅紧凑美观,还大幅提升了传动效率(>85%),减少了机械磨损。
大扭矩与低速稳定性: 内置的减速机构(速比通常为 1:10 ~ 1:30)使得电机能在低转速下输出大扭矩,非常适合驱动负载在 50~200kg 级别的中小型机器人,在跟随过程中能平稳启停,抗负载扰动能力强。
长寿命与低噪音: 无刷结构消除了电刷磨损,寿命远超有刷电机,且运行噪音低,适用于对环境安静度有要求的室内服务场景。
2、双模无线感知与定位机制
蓝牙与 WiFi 技术为自动跟随提供了不同的感知维度和定位逻辑。
蓝牙跟随(BLE/UWB):
RSSI 测距: 利用信号强度(RSSI)粗略估算距离,成本低但易受环境干扰。
AoA/AoD(到达角): 配合多天线阵列,蓝牙 5.1+ 可实现厘米级高精度定位,是实现平滑跟随的理想选择。
WiFi 跟随(RTT/RSSI):
RTT(往返时间): 通过测量信号往返时间计算距离,精度较高(约 1 米内)。
视觉辅助: 在带屏设备上,WiFi 可传输摄像头画面,结合 OpenCV 进行人脸或物体识别,实现视觉跟随。
多传感器融合: 单纯依赖无线信号容易受多径效应影响。系统通常融合轮毂电机自带的编码器(里程计)和惯性测量单元(IMU)数据,通过卡尔曼滤波算法对无线定位数据进行修正,输出平滑稳定的相对位姿。
3、分层式控制架构
为了处理复杂的跟随任务,系统通常采用主从控制模式。
上位机(协同处理): 虽然主控是 Arduino,但对于复杂的 WiFi 视觉识别或 SLAM 算法,通常由 Raspberry Pi 或 ESP32 等高性能芯片负责处理图像识别和高层决策(如“目标在左前方 2 米”)。
下位机(Arduino): 作为底层执行单元,接收上位机下发的运动指令(如“左转 30 度,前进 0.5 米/秒”),结合电机编码器的闭环反馈,通过 PID 算法精确控制左右轮毂电机的转速差,实现底盘的差速转向和精准轨迹跟踪。

二、应用场景
该底盘凭借其高集成度、长续航和智能跟随能力,适用于多种轻型自动化场景:
1、智能物流与搬运: 在仓库或工厂车间,作为轻型 AGV(自动导引车),自动跟随工人搬运物料,实现“人机协同”作业,减轻劳动强度。
2、服务与接待: 在商场、展厅或酒店,作为自动跟随的行李车或导览车,为顾客提供便捷的物品寄存或路线引导服务。
3、安防与巡检: 在园区或变电站,配合巡逻人员进行伴随式监控,自动跟随预定路线或操作员,实时回传高清视频和环境数据。
4、教育与创客开发: 作为学习 ROS(机器人操作系统)、多传感器融合、无线定位算法(UWB)以及 FOC 电机控制的理想验证平台。

三、注意事项
在开发此类系统时,需重点解决无线干扰、动力匹配及安全逻辑等问题:
1、无线通信的稳定性与抗干扰
信号多径效应: 室内环境中,蓝牙/WiFi 信号易受墙壁、金属物体反射产生多径效应,导致定位漂移。需在软件中对 RSSI 或距离数据进行滑动平均滤波或中值滤波处理。
通信延迟: WiFi 传输视频流时可能存在延迟,影响跟随的实时性。建议采用 5G 频段 WiFi 减少拥堵,并设置合理的数据重传机制。
2、电源管理与电磁兼容(EMC)
电源隔离: 6.5 寸轮毂电机启动电流大,工作时产生的电磁噪声会严重干扰无线模块和 Arduino。必须使用隔离 DC-DC 模块(如 24V 转 5V/3.3V)为控制电路和通信模块供电,并在电机电源端并联大容量电解电容以吸收反电动势。
天线布局: 蓝牙/WiFi 天线应尽量远离电机和大电流走线,最好安装在底盘的顶部或立柱上,避免金属遮挡。
3、机械结构与负载匹配
重心控制: 6.5 寸轮毂电机通常用于中小型底盘。若搭载过重的货物或过高的人机交互屏幕,会导致重心过高,急转弯时容易侧翻。需合理布局电池(通常置于底盘底部)以降低重心。
轮距与轴距: 轮距越宽,横向稳定性越好;轴距越长,纵向越稳定。需根据预期的跟随速度和转弯半径设计合理的底盘尺寸。
4、安全与故障冗余
超距保护: 必须设定跟随距离的阈值。当目标距离超过设定范围(如 5 米)时,机器人应自动减速并停止,防止丢失目标后“飞车”。
紧急制动: 需设置硬件急停按钮,并在软件中监测通信超时。一旦信号中断超过设定时间,立即切断电机 PWM 输出,触发电子刹车。

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)图1

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)

蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂电机版)具体操作步骤
全程简单、低成本、不用视觉、不用雷达,新手也能一次成功。

一、方案原理(一句话懂)
你拿手机 / 蓝牙遥控器走在前面,机器人通过信号强度(RSSI) 判断你离它远不远、偏左还是偏右,自动跟着你走。不用摄像头、不用雷达、不用复杂算法。

二、所需硬件(直接照买)
6.5 寸无刷轮毂电机 ×2(36V 250W/350W 有霍尔)
36V 无刷控制器 ×1(A-WZKD3618KA 或通用款)
主控:ESP32 / Arduino + HC-05/06 蓝牙模块
36V 锂电池一组(10Ah 足够)
4040 铝型材底盘框架
蓝牙 / WiFi 遥控器 或 手机(安装蓝牙调试 APP)

三、接线步骤(极简版)
电机相线 U/V/W → 控制器 U/V/W
电机霍尔 5 芯线 → 控制器霍尔接口
控制器 PWM、DIR、GND → ESP32
ESP32 TX/RX → 蓝牙模块 HC-05
36V 电池 → 控制器电源
控制器 5V → 给蓝牙 / ESP32 供电
重点:所有 GND 必须共地。

四、软件逻辑(最简单可运行版)
ESP32 只做 4 件事:
读取蓝牙信号强度 RSSI(数值越大,距离越近)
根据 RSSI 判断:
信号太强 → 距离太近 → 机器人后退
信号适中 → 保持不动
信号太弱 → 距离太远 → 机器人前进
根据左右信号差(双蓝牙更准)判断向左 / 向右
输出 PWM + 方向信号给无刷控制器

五、具体操作步骤(从 0 到跑起来)
1. 先把底盘跑通(不跟随,先会动)
按接线图接好电机、控制器、ESP32
写简单测试代码:
前进:DIR = 正转,PWM=80~150
后退:DIR = 反转,PWM=80~150
停止:PWM=0
通电测试:电机顺畅、不抖、有力,说明底盘正常。
2. 蓝牙模块配对 & 读取信号强度
手机蓝牙连接 HC-05
ESP32 不断读取 RSSI(信号强度)
打印串口观察:
你靠近 → RSSI 变大(如 -30 ~ -50)
你走远 → RSSI 变小(如 -70 ~ -90)
3. 设定跟随距离阈值
例:
RSSI > -50 → 太近 → 后退
-50 ~ -70 → 刚好 → 停止
RSSI < -70 → 太远 → 前进
4. 实现左右跟随(最简单方案)
你手里拿蓝牙发射机
机器人前方放 2 个蓝牙模块(左 + 右)
哪边信号强,机器人就转向哪边
逻辑:
左信号 > 右信号 → 左转
右信号 > 左信号 → 右转
两边差不多 → 直行
5. 整合控制逻辑
ESP32 主循环:
读取左、右蓝牙 RSSI
计算距离 → 决定前进 / 停止 / 后退
计算左右差 → 决定左转 / 右转 / 直行
输出 PWM 给无刷控制器
延时 50~100ms,再次循环
6. 上电试运行
打开手机蓝牙,连接机器人
离开 1~3 米
机器人会:
自动朝你方向过来
保持一段距离跟着你走
你靠近它就退,你走远它就追

六、效果优化(创客必调)
PWM 不要太大:80~180 足够,轮毂电机扭矩大
加死区:RSSI 中间区间不动作,防止来回抖
加延时滤波:不要频繁启停,更顺滑
加限速:防止跟太快撞人

七、优点(非常适合创客)
成本最低
不用调试复杂视觉
不受光线影响
6.5 寸轮毂电机静音、平稳、力气大
代码几百行,半天写完

八、简单总结
蓝牙 / WiFi 跟随是创客入门自动跟随机器人最稳妥、最简单的方案。依靠信号强度判断距离与方向,配合 6.5 寸无刷轮毂电机直驱底盘,结构简洁、运行稳定,不需要复杂算法与昂贵传感器,就能实现基本的自动跟随功能,非常适合新手入门与教学展示。

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)图1

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 1 小时前

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)

本帖最后由 驴友花雕 于 2026-2-21 19:14 编辑

1、蓝牙指令遥控跟随(基础版)
场景:通过手机蓝牙串口APP(如“蓝牙串口助手”)发送字符命令(如 'F', 'B', 'L', 'R')控制底盘运动。

  1. // 电机驱动引脚 (假设使用L298N或类似驱动板)
  2. const int enA = 9;   // 左轮PWM
  3. const int in1 = 8;   // 左轮方向1
  4. const int in2 = 7;   // 左轮方向2
  5. const int enB = 10;  // 右轮PWM
  6. const int in3 = 6;   // 右轮方向1
  7. const int in4 = 5;   // 右轮方向2
  8. // 蓝牙模块连接 RX->TX, TX->RX
  9. #include <SoftwareSerial.h>
  10. SoftwareSerial BT(12, 11); // RX, TX
  11. String inputString = "";
  12. boolean stringComplete = false;
  13. int baseSpeed = 200; // 基础速度 (0-255)
  14. void setup() {
  15.   pinMode(enA, OUTPUT);
  16.   pinMode(enB, OUTPUT);
  17.   pinMode(in1, OUTPUT);
  18.   pinMode(in2, OUTPUT);
  19.   pinMode(in3, OUTPUT);
  20.   pinMode(in4, OUTPUT);
  21.   
  22.   Serial.begin(9600);
  23.   BT.begin(9600);
  24.   Serial.println("Bluetooth Follow Base Ready.");
  25.   
  26.   // 初始停止
  27.   stopMotors();
  28. }
  29. void loop() {
  30.   if (stringComplete) {
  31.     inputString.trim();
  32.     char cmd = inputString.charAt(0);
  33.    
  34.     switch (cmd) {
  35.       case 'F': // 前进
  36.         moveForward(baseSpeed);
  37.         break;
  38.       case 'B': // 后退
  39.         moveBackward(baseSpeed);
  40.         break;
  41.       case 'L': // 左转
  42.         turnLeft(baseSpeed);
  43.         break;
  44.       case 'R': // 右转
  45.         turnRight(baseSpeed);
  46.         break;
  47.       case 'S': // 停止
  48.         stopMotors();
  49.         break;
  50.       case '0' ... '9': // 设置速度
  51.         baseSpeed = inputString.toInt() * 25; // 例如 '8' -> 200
  52.         break;
  53.     }
  54.    
  55.     // 回传确认
  56.     BT.print("Cmd: "); BT.println(cmd);
  57.     Serial.print("Cmd: "); Serial.println(cmd);
  58.    
  59.     inputString = "";
  60.     stringComplete = false;
  61.   }
  62. }
  63. // 串口事件处理
  64. void serialEvent() {
  65.   while (BT.available()) {
  66.     char inChar = (char)BT.read();
  67.     inputString += inChar;
  68.     if (inChar == '\n') {
  69.       stringComplete = true;
  70.     }
  71.   }
  72. }
  73. // --- 电机驱动函数 ---
  74. void moveForward(int speed) {
  75.   digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
  76.   digitalWrite(in3, HIGH); digitalWrite(in4, LOW);
  77.   analogWrite(enA, speed);
  78.   analogWrite(enB, speed);
  79. }
  80. void moveBackward(int speed) {
  81.   digitalWrite(in1, LOW); digitalWrite(in2, HIGH);
  82.   digitalWrite(in3, LOW); digitalWrite(in4, HIGH);
  83.   analogWrite(enA, speed);
  84.   analogWrite(enB, speed);
  85. }
  86. void turnLeft(int speed) {
  87.   digitalWrite(in1, LOW); digitalWrite(in2, HIGH); // 左轮反转
  88.   digitalWrite(in3, HIGH); digitalWrite(in4, LOW); // 右轮正转
  89.   analogWrite(enA, speed);
  90.   analogWrite(enB, speed);
  91. }
  92. void turnRight(int speed) {
  93.   digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
  94.   digitalWrite(in3, LOW); digitalWrite(in4, HIGH);
  95.   analogWrite(enA, speed);
  96.   analogWrite(enB, speed);
  97. }
  98. void stopMotors() {
  99.   analogWrite(enA, 0);
  100.   analogWrite(enB, 0);
  101. }
复制代码


2、WiFi UDP 坐标跟随(ESP8266 NodeMCU版)
场景:使用 ESP8266 连接 WiFi,通过 UDP 协议接收目标坐标(X, Y),控制底盘向目标点移动。注意:此案例需使用 NodeMCU 或 ESP32 等带WiFi的开发板。

  1. #include <ESP8266WiFi.h>
  2. #include <WiFiUdp.h>
  3. // WiFi 配置
  4. const char* ssid = "Your_SSID";
  5. const char* password = "Your_Password";
  6. // UDP 配置
  7. WiFiUDP Udp;
  8. unsigned int localUdpPort = 8888;  // 本地监听端口
  9. char incomingPacket[255];  // 接收缓冲区
  10. // 电机引脚 (使用ESP8266的PWM引脚)
  11. const int enA = D1; // GPIO5
  12. const int in1 = D2; // GPIO4
  13. const int in2 = D3; // GPIO0
  14. const int enB = D4; // GPIO2
  15. const int in3 = D5; // GPIO14
  16. const int in4 = D6; // GPIO12
  17. // 目标坐标
  18. int targetX = 0, targetY = 0;
  19. void setup() {
  20.   // 初始化电机引脚...
  21.   pinMode(enA, OUTPUT); pinMode(enB, OUTPUT);
  22.   pinMode(in1, OUTPUT); pinMode(in2, OUTPUT);
  23.   pinMode(in3, OUTPUT); pinMode(in4, OUTPUT);
  24.   
  25.   Serial.begin(115200);
  26.   Serial.println();
  27.   // 连接 WiFi
  28.   Serial.printf("Connecting to %s ", ssid);
  29.   WiFi.begin(ssid, password);
  30.   while (WiFi.status() != WL_CONNECTED) {
  31.     delay(500);
  32.     Serial.print(".");
  33.   }
  34.   Serial.println(" connected");
  35.   // 启动 UDP
  36.   Udp.begin(localUdpPort);
  37.   Serial.printf("UDP server started at IP %s, port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
  38. }
  39. void loop() {
  40.   int packetSize = Udp.parsePacket();
  41.   if (packetSize) {
  42.     // 收到数据包
  43.     int len = Udp.read(incomingPacket, 255);
  44.     if (len > 0) {
  45.       incomingPacket[len] = 0;
  46.     }
  47.     Serial.printf("Received: %s\n", incomingPacket);
  48.     // 解析坐标 (格式假设为 "X:100,Y:200")
  49.     String packetStr = String(incomingPacket);
  50.     int xIndex = packetStr.indexOf("X:");
  51.     int yIndex = packetStr.indexOf("Y:");
  52.     int commaIndex = packetStr.indexOf(",");
  53.    
  54.     if (xIndex != -1 && yIndex != -1) {
  55.       String xStr = packetStr.substring(xIndex+2, commaIndex);
  56.       String yStr = packetStr.substring(yIndex+2);
  57.       targetX = xStr.toInt();
  58.       targetY = yStr.toInt();
  59.       
  60.       Serial.printf("Target Updated: X=%d, Y=%d\n", targetX, targetY);
  61.       
  62.       // 根据坐标控制运动 (简化逻辑)
  63.       if (targetY > 50) {
  64.         moveForward(200); // 目标在正前方,前进
  65.       } else if (targetY < -50) {
  66.         moveBackward(200);
  67.       } else if (targetX > 50) {
  68.         turnRight(150); // 目标在右侧,右转
  69.       } else if (targetX < -50) {
  70.         turnLeft(150);
  71.       } else {
  72.         stopMotors(); // 目标在中心区域,停止
  73.       }
  74.     }
  75.   }
  76.   delay(50);
  77. }
  78. // 电机驱动函数 (同案例一,略)
  79. void moveForward(int speed) { /* ... */ }
  80. void moveBackward(int speed) { /* ... */ }
  81. void turnLeft(int speed) { /* ... */ }
  82. void turnRight(int speed) { /* ... */ }
  83. void stopMotors() { /* ... */ }
复制代码


3、带 PID 闭环的坐标平滑跟随
场景:在案例二的基础上,引入 PID 控制器,根据目标坐标与当前位置的偏差,动态计算左右轮速,实现平滑、精准的跟随。

  1. // 基于 ESP8266/Arduino,包含必要的库和WiFi设置 (同案例二)
  2. // PID 参数
  3. double Kp = 0.8, Ki = 0.0, Kd = 0.2; // 需根据实际调试
  4. double lastError = 0, integral = 0;
  5. // 当前坐标 (假设由外部传感器如UWB提供,这里模拟)
  6. int currentX = 0, currentY = 0;
  7. void setup() {
  8.   // 同案例二 setup()
  9. }
  10. void loop() {
  11.   // 1. 接收目标坐标 (同案例二)
  12.   // ... UDP 接收代码 ...
  13.   
  14.   // 2. 获取当前位置 (这里需要接入UWB或视觉定位模块,此处用模拟值)
  15.   // currentX = getUwbX();
  16.   // currentY = getUwbY();
  17.   
  18.   // 3. 计算偏差
  19.   int errorX = targetX - currentX; // X方向偏差
  20.   
  21.   // 4. PID 计算
  22.   integral += errorX;
  23.   double derivative = errorX - lastError;
  24.   double output = Kp * errorX + Ki * integral + Kd * derivative;
  25.   lastError = errorX;
  26.   
  27.   // 5. 应用输出到差速驱动
  28.   int baseSpeed = 180;
  29.   int leftSpeed = baseSpeed + output;
  30.   int rightSpeed = baseSpeed - output;
  31.   
  32.   // 限制速度范围
  33.   leftSpeed = constrain(leftSpeed, 0, 255);
  34.   rightSpeed = constrain(rightSpeed, 0, 255);
  35.   
  36.   // 6. 判断运动方向
  37.   if (targetY > 20) { // 目标在远处,前进
  38.     setMotorSpeeds(leftSpeed, rightSpeed, FORWARD);
  39.   } else if (targetY < -20) { // 目标在身后,后退
  40.     setMotorSpeeds(leftSpeed, rightSpeed, BACKWARD);
  41.   } else { // 目标在很近,停止或原地转向
  42.     if (abs(errorX) > 10) {
  43.       // 需要转向
  44.       if (errorX > 0) {
  45.         turnRight(150);
  46.       } else {
  47.         turnLeft(150);
  48.       }
  49.     } else {
  50.       stopMotors();
  51.     }
  52.   }
  53.   
  54.   delay(30);
  55. }
  56. // 设置左右轮速度及方向
  57. void setMotorSpeeds(int left, int right, int dir) {
  58.   if (dir == FORWARD) {
  59.     digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
  60.     digitalWrite(in3, HIGH); digitalWrite(in4, LOW);
  61.   } else {
  62.     digitalWrite(in1, LOW); digitalWrite(in2, HIGH);
  63.     digitalWrite(in3, LOW); digitalWrite(in4, HIGH);
  64.   }
  65.   analogWrite(enA, left);
  66.   analogWrite(enB, right);
  67. }
复制代码


要点解读
1、6.5寸轮毂电机的驱动特性大电流需求:6.5寸电机通常电流较大(峰值可达10A-20A)。严禁使用 Arduino 数字引脚直接驱动,必须使用大功率电机驱动板(如 BTS7960, VNH2SP30, 或 大电流电调)。PWM频率:Arduino 默认 PWM 频率(490Hz/980Hz)通常可用,但某些电调可能需要特定频率(如 50Hz 舵机信号或更高频率),需查阅电调手册。
2、无线通信协议的选择蓝牙 (Bluetooth):适合短距离(10米内)、点对点控制,连接简单,功耗低。WiFi (UDP/TCP):适合中远距离、局域网内控制,支持多设备通信,延迟相对较低。UDP 协议比 TCP 更适合实时控制,因为它不重传丢包,延迟更稳定。
3、“死区” (Dead Zone) 处理必要性:在坐标跟随中,当目标点非常接近当前位置时,偏差很小。如果不设死区,电机可能会在“微动”和“停止”间高频振荡,导致发热和磨损。实现:在代码中判断 if (abs(errorX) < 10 && abs(errorY) < 10)时,直接停止电机。
4、PID 闭环控制的引入开环问题:案例一、二使用开环控制(直接给速度),无法补偿地面打滑、负载不均等干扰。PID优势:案例三展示了差速 PID。通过计算 X 方向偏差,动态调整左右轮速差。P项提供快速响应,D项抑制振荡。这使得机器人能平滑地收敛到目标路径,而不是左右摇摆。
5、安全与急停机制通信超时保护:无线信号可能中断。必须加入看门狗定时器,如果超过 500ms 未收到有效数据,立即停止电机,防止机器人失控。物理急停:建议在底盘上增加物理碰撞开关或红外避障传感器作为最后一道防线,防止程序逻辑错误导致撞墙。

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)图1





回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 1 小时前

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)

4、蓝牙手柄+视觉识别双模跟随机器人(室内个性化服务)
1. 场景定位
适用于室内家庭、商场等场景,支持手机蓝牙手柄遥控+视觉自主跟随双模式切换:无目标时用蓝牙手动控制,识别到人体(或主人)后自动切换为跟随模式,实现智能跟随、避障,满足个人代步、跟随购物等需求。
2.  核心跟随逻辑
双模式切换逻辑:
蓝牙模式:接收手柄指令,控制底盘移动(前进、后退、转向);
视觉跟随模式:OpenMV识别人体目标,输出目标距离与方位角,Arduino计算跟随速度/转向角度,驱动轮毂电机实现跟随;
模式切换条件:视觉检测到目标(人体置信度>80%)→切换到跟随模式;目标丢失3秒以上→切回蓝牙模式。
跟随控制闭环:
目标距离控制:距离远→加速,距离近→减速,目标丢失→停车;
方位校正:目标偏左→左轮加速、右轮减速(右转校正),目标偏右→右轮加速、左轮减速(左转校正);
避障辅助:超声波检测到障碍,距离<30cm时,暂停跟随并转向避障,避障后恢复跟随。

  1. // 蓝牙+视觉双模跟随机器人(6.5寸轮毂电机版)
  2. // 功能:蓝牙遥控/视觉跟随切换,跟随时避障,精准控制轮毂电机
  3. // ========== 硬件引脚配置 ==========
  4. #define LEFT_WHEEL_PWM 9    // 左轮毂电机PWM口
  5. #define RIGHT_WHEEL_PWM 10  // 右轮毂电机PWM口
  6. #define BLUE_TX 1          // 蓝牙模块发送端
  7. #define BLUE_RX 0          // 蓝牙模块接收端
  8. #define OPENMV_TX 11       // OpenMV发送端
  9. #define OPENMV_RX 12       // OpenMV接收端
  10. #define LEFT_ULTRA 13      // 左超声波触发
  11. #define RIGHT_ULTRA 14     // 右超声波触发
  12. // ========== 核心参数配置 ==========
  13. #define TARGET_DISTANCE 80   // 目标跟随距离(cm)
  14. #define SAFE_DISTANCE 30     // 避障安全距离(cm)
  15. #define MAX_SPEED 200        // 最大转速(PWM 0-255)
  16. #define TURN_SPEED_RATIO 0.3 // 转向转速差比(0-1,值越大转向越快)
  17. #define TARGET_LOST_TIME 3000 // 目标丢失等待时间(ms)
  18. // ========== 变量声明 ==========
  19. unsigned long lastTargetTime = 0;
  20. bool isFollowMode = false;
  21. float targetDistance = 0;   // 目标距离(OpenMV获取)
  22. float targetAngle = 0;      // 目标方位角(正为右,负为左)
  23. String blueCommand = "";    // 蓝牙指令
  24. // ========== 函数声明 ==========
  25. void setupMotorControl();
  26. void setupCommunication();
  27. void parseBluetoothCommand();
  28. void updateVisualTarget();
  29. void controlFollow();
  30. void obstacleAvoidance();
  31. void setMotorSpeed(int leftPWM, int rightPWM);
  32. void setup() {
  33.   Serial.begin(115200);
  34.   setupMotorControl();
  35.   setupCommunication();
  36.   Serial.println("双模跟随机器人初始化完成");
  37.   Serial.println("模式:初始蓝牙模式,检测到人体自动切换跟随");
  38. }
  39. void loop() {
  40.   parseBluetoothCommand();   // 解析蓝牙指令
  41.   updateVisualTarget();      // 更新视觉目标数据
  42.   
  43.   // 模式切换判断
  44.   if (targetDistance > 0 && millis() - lastTargetTime < TARGET_LOST_TIME) {
  45.     isFollowMode = true;
  46.     Serial.println("切换至:视觉跟随模式");
  47.   } else {
  48.     if (isFollowMode) {
  49.       isFollowMode = false;
  50.       Serial.println("切换至:蓝牙模式");
  51.     }
  52.     setMotorSpeed(0, 0); // 跟随丢失时停车
  53.   }
  54.   
  55.   if (isFollowMode) {
  56.     controlFollow();         // 视觉跟随控制
  57.     obstacleAvoidance();     // 避障辅助
  58.   } else {
  59.     executeBluetoothControl(); // 蓝牙遥控执行
  60.   }
  61. }
  62. // ========== 初始化轮毂电机控制 ==========
  63. void setupMotorControl() {
  64.   pinMode(LEFT_WHEEL_PWM, OUTPUT);
  65.   pinMode(RIGHT_WHEEL_PWM, OUTPUT);
  66.   analogWrite(LEFT_WHEEL_PWM, 0);
  67.   analogWrite(RIGHT_WHEEL_PWM, 0);
  68. }
  69. // ========== 初始化通信(蓝牙+视觉) ==========
  70. void setupCommunication() {
  71.   Serial1.begin(9600, SERIAL_8N1, BLUE_TX, BLUE_RX);  // 蓝牙串口
  72.   Serial2.begin(9600, SERIAL_8N1, OPENMV_TX, OPENMV_RX); // OpenMV串口
  73. }
  74. // ========== 解析蓝牙手柄指令 ==========
  75. void parseBluetoothCommand() {
  76.   if (Serial1.available() > 0) {
  77.     char c = Serial1.read();
  78.     if (c == '\n') {
  79.       // 指令格式:F120L80(F=前进速度,L=左转速度)
  80.       int forwardSpeed = blueCommand.substring(1, 4).toInt();
  81.       int turnSpeed = blueCommand.substring(5, 8).toInt();
  82.       executeBluetoothControl(forwardSpeed, turnSpeed);
  83.       blueCommand = "";
  84.     } else {
  85.       blueCommand += c;
  86.     }
  87.   }
  88. }
  89. // ========== 执行蓝牙遥控 ==========
  90. void executeBluetoothControl(int forward = 0, int turn = 0) {
  91.   if (forward > 0) {
  92.     int leftPWM = forward - turn * TURN_SPEED_RATIO;
  93.     int rightPWM = forward + turn * TURN_SPEED_RATIO;
  94.     leftPWM = constrain(leftPWM, 0, MAX_SPEED);
  95.     rightPWM = constrain(rightPWM, 0, MAX_SPEED);
  96.     setMotorSpeed(leftPWM, rightPWM);
  97.   } else {
  98.     setMotorSpeed(0, 0); // 不操作时停车
  99.   }
  100. }
  101. // ========== 更新视觉目标数据 ==========
  102. void updateVisualTarget() {
  103.   if (Serial2.available() > 0) {
  104.     // OpenMV发送格式:"D100A-15"(D=距离,A=角度)
  105.     String data = Serial2.readStringUntil('\n');
  106.     data.trim();
  107.     if (data.startsWith("D") && data.indexOf("A") != -1) {
  108.       targetDistance = data.substring(1, data.indexOf("A")).toFloat();
  109.       targetAngle = data.substring(data.indexOf("A")+1).toFloat();
  110.       lastTargetTime = millis();
  111.     }
  112.   }
  113. }
  114. // ========== 视觉跟随核心控制 ==========
  115. void controlFollow() {
  116.   // 1. 距离控制:目标距离远加速,近减速
  117.   int speedAdjust = map(targetDistance, 50, 150, 80, 180);
  118.   speedAdjust = constrain(speedAdjust, 50, MAX_SPEED);
  119.   
  120.   // 2. 方位校正:目标偏右→左转(右轮加速,左轮减速)
  121.   int turnAdjust = map(targetAngle, -30, 30, -50, 50);
  122.   int leftPWM = speedAdjust + turnAdjust;
  123.   int rightPWM = speedAdjust - turnAdjust;
  124.   
  125.   // 限制转速范围
  126.   leftPWM = constrain(leftPWM, 0, MAX_SPEED);
  127.   rightPWM = constrain(rightPWM, 0, MAX_SPEED);
  128.   
  129.   setMotorSpeed(leftPWM, rightPWM);
  130.   
  131.   // 串口输出状态
  132.   Serial.print("跟随:距离=");Serial.print(targetDistance);
  133.   Serial.print("cm,角度=");Serial.print(targetAngle);
  134.   Serial.print("°,转速:L=");Serial.print(leftPWM);Serial.print(" R=");Serial.print(rightPWM);
  135. }
  136. // ========== 避障辅助逻辑 ==========
  137. void obstacleAvoidance() {
  138.   // 左/右超声波测距(简化实现,实际需补全)
  139.   int leftDist = getUltrasonicDistance(LEFT_ULTRA);
  140.   int rightDist = getUltrasonicDistance(RIGHT_ULTRA);
  141.   
  142.   if (leftDist < SAFE_DISTANCE) {
  143.     // 左侧有障碍,右转避障
  144.     setMotorSpeed(50, 150);
  145.     Serial.println("避障:左侧障碍,右转");
  146.     delay(500);
  147.   } else if (rightDist < SAFE_DISTANCE) {
  148.     // 右侧有障碍,左转避障
  149.     setMotorSpeed(150, 50);
  150.     Serial.println("避障:右侧障碍,左转");
  151.     delay(500);
  152.   }
  153. }
  154. // ========== 超声波测距辅助函数 ==========
  155. int getUltrasonicDistance(int pin) {
  156.   pinMode(pin, OUTPUT);
  157.   digitalWrite(pin, LOW);
  158.   delayMicroseconds(2);
  159.   digitalWrite(pin, HIGH);
  160.   delayMicroseconds(10);
  161.   digitalWrite(pin, LOW);
  162.   pinMode(pin, INPUT);
  163.   int duration = pulseIn(pin, HIGH);
  164.   return duration * 0.0343 / 2; // 返回cm
  165. }
  166. // ========== 设置轮毂电机转速 ==========
  167. void setMotorSpeed(int leftPWM, int rightPWM) {
  168.   analogWrite(LEFT_WHEEL_PWM, leftPWM);
  169.   analogWrite(RIGHT_WHEEL_PWM, rightPWM);
  170. }
复制代码


5、WiFi远程监控+自动跟随机器人(室外物流跟车)
1. 场景定位
适用于室外物流园区、仓库外部等场景,实现远程监控+自动跟随:通过WiFi连接PC/手机端,远程查看机器人状态、下达跟随/暂停指令,机器人通过激光雷达识别车辆(或目标载体),自动跟随并保持安全车距,满足物流跟车、园区巡检等需求。
2. 核心跟随逻辑
远程控制逻辑:
远程终端通过WiFi TCP发送指令(格式:"START"启动跟随,"PAUSE"暂停,"RESET"重置参数);
机器人接收指令后执行,并实时回传状态(距离、速度、位置)。
激光跟车逻辑:
目标识别:扫描车辆特征(如车辆高度≥1.5m,宽度≥1m),过滤行人、固定障碍;
距离控制:与车辆距离>2m→加速,距离<1m→减速,目标丢失→停车;
方位校正:车辆偏左→右转(左轮减速、右轮加速),车辆偏右→左转,保持跟随在车辆正后方;
室外适配:根据GPS定位辅助,避免跟车时偏离预设路径,适应园区弯道。

  1. // WiFi远程监控+激光自动跟随机器人(6.5寸轮毂电机版)
  2. // 功能:WiFi远程控制,激光识别车辆跟随,实时状态回传,室外GPS定位
  3. #include <WiFi.h>
  4. #include <ESPAsyncTCP.h>
  5. #include <ESPAsyncWebServer.h>
  6. #include <TinyGPS++.h>
  7. // ========== 硬件引脚配置 ==========
  8. #define LEFT_WHEEL_PWM 22
  9. #define RIGHT_WHEEL_PWM 23
  10. #define LIDAR_TX 17
  11. #define LIDAR_RX 16
  12. #define GPS_TX 18
  13. #define GPS_RX 19
  14. // ========== WiFi与服务器配置 ==========
  15. const char* ssid = "园区WiFi";
  16. const char* password = "12345678";
  17. const int serverPort = 8080;
  18. AsyncWebServer server(serverPort);
  19. AsyncWebSocket ws("/ws"); // WebSocket实时通信
  20. // ========== 核心参数配置 ==========
  21. #define TARGET_VEHICLE_WIDTH 120 // 车辆宽度阈值(cm)
  22. #define TARGET_VEHICLE_HEIGHT 150 // 车辆高度阈值(cm)
  23. #define FOLLOW_DISTANCE 150 // 跟车安全距离(cm)
  24. #define MAX_FOLLOW_SPEED 180 // 跟车最大速度
  25. #define MIN_FOLLOW_SPEED 60 // 跟车最小速度
  26. // ========== 变量声明 ==========
  27. float targetDistance = 0;
  28. float targetAngle = 0;
  29. bool isFollowing = false;
  30. float currentLat = 0, currentLng = 0;
  31. bool vehicleDetected = false;
  32. // ========== 函数声明 ==========
  33. void setupNetwork();
  34. void handleWebSocket();
  35. void parseLidarData();
  36. void controlFollowVehicle();
  37. void setMotorSpeed(int leftPWM, int rightPWM);
  38. void updateGPSData();
  39. void setup() {
  40.   Serial.begin(115200);
  41.   pinMode(LEFT_WHEEL_PWM, OUTPUT);
  42.   pinMode(RIGHT_WHEEL_PWM, OUTPUT);
  43.   setupNetwork();
  44.   Serial1.begin(115200, SERIAL_8N1, LIDAR_TX, LIDAR_RX); // 激光雷达
  45.   Serial2.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);       // GPS
  46.   ws.onEvent(handleWebSocket);
  47.   server.addHandler(&ws);
  48.   server.begin();
  49.   Serial.println("WiFi连接成功,端口:8080");
  50.   Serial.println("等待远程指令,检测到车辆自动启动跟随");
  51. }
  52. void loop() {
  53.   parseLidarData();         // 解析激光雷达数据
  54.   controlFollowVehicle();   // 车辆跟随控制
  55.   handleWebSocket();        // 处理WebSocket指令
  56.   updateGPSData();         // 更新GPS位置
  57.   
  58.   // 定期回传状态到客户端
  59.   if (millis() % 1000 == 0) {
  60.     sendStatusToClient();
  61.   }
  62. }
  63. // ========== 初始化WiFi网络 ==========
  64. void setupNetwork() {
  65.   WiFi.begin(ssid, password);
  66.   while (WiFi.status() != WL_CONNECTED) {
  67.     delay(500);
  68.     Serial.print(".");
  69.   }
  70.   Serial.print("\nIP地址:");
  71.   Serial.println(WiFi.localIP());
  72. }
  73. // ========== WebSocket指令处理 ==========
  74. void handleWebSocket() {
  75.   if (ws.events() == WS_EVT_DATA) {
  76.     String command = ws.arg("data").toString();
  77.     if (command == "START") {
  78.       isFollowing = true;
  79.       Serial.println("接收指令:启动跟随");
  80.       ws.send("OK:启动跟随");
  81.     } else if (command == "PAUSE") {
  82.       isFollowing = false;
  83.       setMotorSpeed(0, 0);
  84.       Serial.println("接收指令:暂停跟随");
  85.       ws.send("OK:暂停跟随");
  86.     } else if (command == "RESET") {
  87.       targetDistance = 0;
  88.       targetAngle = 0;
  89.       Serial.println("接收指令:重置参数");
  90.       ws.send("OK:参数重置");
  91.     }
  92.   }
  93. }
  94. // ========== 解析激光雷达数据 ==========
  95. void parseLidarData() {
  96.   if (Serial1.available() > 0) {
  97.     // 简化数据格式:假设雷达发送"V150W120A-5"(V=车辆标志,W=宽度,A=角度)
  98.     String data = Serial1.readStringUntil('\n');
  99.     data.trim();
  100.     if (data.startsWith("V")) {
  101.       int width = data.substring(1, data.indexOf("W")).toInt();
  102.       targetAngle = data.substring(data.indexOf("W")+1, data.indexOf("A")).toFloat();
  103.       targetDistance = data.substring(data.indexOf("A")+1).toFloat();
  104.       vehicleDetected = true;
  105.       isFollowing = true; // 检测到车辆自动启动跟随
  106.     } else {
  107.       vehicleDetected = false;
  108.     }
  109.   }
  110. }
  111. // ========== 车辆跟随核心控制 ==========
  112. void controlFollowVehicle() {
  113.   if (!isFollowing || !vehicleDetected) {
  114.     setMotorSpeed(0, 0);
  115.     return;
  116.   }
  117.   
  118.   // 1. 距离控制:距离远加速,近减速
  119.   int speed = map(targetDistance, 100, 250, MIN_FOLLOW_SPEED, MAX_FOLLOW_SPEED);
  120.   speed = constrain(speed, MIN_FOLLOW_SPEED, MAX_FOLLOW_SPEED);
  121.   
  122.   // 2. 方位校正:目标偏右→左转(右轮加速,左轮减速)
  123.   int turn = map(targetAngle, -20, 20, -40, 40);
  124.   int leftPWM = speed + turn;
  125.   int rightPWM = speed - turn;
  126.   
  127.   // 限制转速范围
  128.   leftPWM = constrain(leftPWM, 0, MAX_FOLLOW_SPEED);
  129.   rightPWM = constrain(rightPWM, 0, MAX_FOLLOW_SPEED);
  130.   
  131.   setMotorSpeed(leftPWM, rightPWM);
  132.   
  133.   Serial.print("跟车:距离=");Serial.print(targetDistance);
  134.   Serial.print("cm,角度=");Serial.print(targetAngle);
  135.   Serial.print("°,速度:L=");Serial.print(leftPWM);Serial.print(" R=");Serial.print(rightPWM);
  136. }
  137. // ========== 设置轮毂电机转速 ==========
  138. void setMotorSpeed(int leftPWM, int rightPWM) {
  139.   analogWrite(LEFT_WHEEL_PWM, leftPWM);
  140.   analogWrite(RIGHT_WHEEL_PWM, rightPWM);
  141. }
  142. // ========== 更新GPS数据 ==========
  143. void updateGPSData() {
  144.   static TinyGPSPlus gps;
  145.   while (Serial2.available() > 0) {
  146.     gps.encode(Serial2.read());
  147.   }
  148.   if (gps.location.isUpdated()) {
  149.     currentLat = gps.location.lat();
  150.     currentLng = gps.location.lng();
  151.   }
  152. }
  153. // ========== 回传状态到客户端 ==========
  154. void sendStatusToClient() {
  155.   String status = "STATUS:距离=" + String(targetDistance) +
  156.                   ",角度=" + String(targetAngle) +
  157.                   ",跟随状态=" + (isFollowing ? "开启" : "关闭") +
  158.                   ",位置=" + String(currentLat,6) + "," + String(currentLng,6);
  159.   ws.send(status);
  160. }
复制代码


6、超声波红外+WiFi协同跟随机器人(低成本仓储跟车)
1. 场景定位
适用于仓储内部、低成本改造的物流场景,不依赖视觉/激光雷达,采用超声波+红外传感器识别目标,结合WiFi远程监控,实现自动跟随:跟随仓库内的AGV小车或搬运员,保持固定距离,成本低廉、部署简单,满足中小仓库的低成本跟车需求。
2. 核心跟随逻辑
低成本目标识别逻辑:
目标判断:前方超声波检测到移动目标(距离在50-200cm,且持续移动3秒以上),结合红外对管检测到反光条(如AGV车身)→判定为目标;
距离控制:目标距离>150cm→加速,<50cm→减速,丢失目标→停车;
方位校正:左红外检测到目标→右转(右轮加速、左轮减速),右红外检测到目标→左转,保持跟随在目标正后方;
协同避障:超声波检测到障碍(距离<30cm)→暂停跟随,左/右超声波检测可通行路径→转向避障,避障后恢复跟随。
WiFi远程控制:支持远程暂停、启动,查看电池电量、跟随距离,适合仓库管理员统一管理多台机器人。


  1. // 超声波红外+WiFi协同跟随机器人(6.5寸轮毂电机版)
  2. // 功能:低成本目标识别,超声波+红外协同跟随,WiFi远程控制,仓储跟车
  3. #include <ESP8266WiFi.h>
  4. // ========== 硬件引脚配置 ==========
  5. #define LEFT_WHEEL_PWM 9
  6. #define RIGHT_WHEEL_PWM 10
  7. #define FRONT_ULTRA 2   // 前方超声波触发
  8. #define LEFT_ULTRA 3    // 左超声波触发
  9. #define RIGHT_ULTRA 4   // 右超声波触发
  10. #define LEFT_INFRARED 5 // 左红外对管
  11. #define RIGHT_INFRARED 6 // 右红外对管
  12. #define ESP8266_TX 7
  13. #define ESP8266_RX 8
  14. // ========== WiFi配置 ==========
  15. const char* ssid = "仓储WiFi";
  16. const char* password = "warehouse123";
  17. const char* serverIP = "192.168.1.100";
  18. const int serverPort = 8080;
  19. WiFiClient client;
  20. // ========== 核心参数配置 ==========
  21. #define TARGET_MIN_DIST 50  // 目标最小距离(cm)
  22. #define TARGET_MAX_DIST 200 // 目标最大距离(cm)
  23. #define FOLLOW_SPEED_BASE 80 // 跟随基础速度
  24. #define SAFE_DIST 30        // 避障安全距离(cm)
  25. // ========== 变量声明 ==========
  26. int frontDist = 0, leftDist = 0, rightDist = 0;
  27. bool leftInfrared = false, rightInfrared = false;
  28. bool isFollowing = false;
  29. unsigned long targetDetectTime = 0;
  30. bool targetExist = false;
  31. // ========== 函数声明 ==========
  32. void setupNetwork();
  33. void sendWiFiCommand();
  34. void detectTarget();
  35. void controlFollow();
  36. void obstacleAvoidance();
  37. void setMotorSpeed(int leftPWM, int rightPWM);
  38. int getUltrasonicDistance(int pin);
  39. void setup() {
  40.   Serial.begin(9600);
  41.   pinMode(LEFT_WHEEL_PWM, OUTPUT);
  42.   pinMode(RIGHT_WHEEL_PWM, OUTPUT);
  43.   pinMode(FRONT_ULTRA, OUTPUT);
  44.   pinMode(LEFT_ULTRA, OUTPUT);
  45.   pinMode(RIGHT_ULTRA, OUTPUT);
  46.   pinMode(LEFT_INFRARED, INPUT);
  47.   pinMode(RIGHT_INFRARED, INPUT);
  48.   Serial1.begin(9600, SERIAL_8N1, ESP8266_TX, ESP8266_RX); // ESP8266
  49.   setupNetwork();
  50.   Serial.println("低成本跟车机器人初始化完成");
  51.   Serial.println("检测到目标自动启动跟随,WiFi远程控制暂停/启动");
  52. }
  53. void loop() {
  54.   detectTarget();          // 识别目标
  55.   if (isFollowing && targetExist) {
  56.     controlFollow();       // 跟随控制
  57.     obstacleAvoidance();   // 避障
  58.   } else {
  59.     setMotorSpeed(0, 0);   // 非跟随状态停车
  60.   }
  61.   sendWiFiCommand();       // 处理远程指令
  62.   
  63.   // 每1秒回传状态
  64.   if (millis() % 1000 == 0) {
  65.     sendStatusToServer();
  66.   }
  67. }
  68. // ========== 初始化WiFi ==========
  69. void setupNetwork() {
  70.   WiFi.begin(ssid, password);
  71.   while (WiFi.status() != WL_CONNECTED) {
  72.     delay(500);
  73.     Serial.print(".");
  74.   }
  75.   Serial.print("\nWiFi连接成功,IP:");
  76.   Serial.println(WiFi.localIP());
  77. }
  78. // ========== 处理远程指令 ==========
  79. void sendWiFiCommand() {
  80.   if (client.connected()) {
  81.     String cmd = client.readStringUntil('\n');
  82.     if (cmd == "START") {
  83.       isFollowing = true;
  84.       Serial.println("接收指令:启动跟随");
  85.       client.println("OK:启动");
  86.     } else if (cmd == "PAUSE") {
  87.       isFollowing = false;
  88.       setMotorSpeed(0, 0);
  89.       Serial.println("接收指令:暂停跟随");
  90.       client.println("OK:暂停");
  91.     }
  92.   } else {
  93.     // 连接服务器
  94.     if (client.connect(serverIP, serverPort)) {
  95.       Serial.println("已连接远程服务器");
  96.     }
  97.   }
  98. }
  99. // ========== 目标识别逻辑 ==========
  100. void detectTarget() {
  101.   frontDist = getUltrasonicDistance(FRONT_ULTRA);
  102.   leftInfrared = digitalRead(LEFT_INFRARED) == HIGH;
  103.   rightInfrared = digitalRead(RIGHT_INFRARED) == HIGH;
  104.   
  105.   // 目标识别条件:距离在目标范围内,且持续检测到3秒以上
  106.   if (frontDist >= TARGET_MIN_DIST && frontDist <= TARGET_MAX_DIST) {
  107.     if (millis() - targetDetectTime > 3000) {
  108.       targetExist = true;
  109.       isFollowing = true;
  110.     } else {
  111.       targetDetectTime = millis();
  112.     }
  113.   } else {
  114.     targetExist = false;
  115.     targetDetectTime = 0;
  116.   }
  117. }
  118. // ========== 跟随核心控制 ==========
  119. void controlFollow() {
  120.   int speed = FOLLOW_SPEED_BASE;
  121.   
  122.   // 距离控制
  123.   if (frontDist > TARGET_MAX_DIST) {
  124.     speed = FOLLOW_SPEED_BASE + 50; // 距离远,加速
  125.   } else if (frontDist < TARGET_MIN_DIST) {
  126.     speed = FOLLOW_SPEED_BASE - 30; // 距离近,减速
  127.   }
  128.   
  129.   // 方位校正:左红外检测到目标,说明目标偏左,右转校正
  130.   int turn = 0;
  131.   if (leftInfrared) {
  132.     turn = -30; // 右转
  133.   } else if (rightInfrared) {
  134.     turn = 30; // 左转
  135.   }
  136.   
  137.   int leftPWM = speed + turn;
  138.   int rightPWM = speed - turn;
  139.   
  140.   // 限制转速
  141.   leftPWM = constrain(leftPWM, 30, 150);
  142.   rightPWM = constrain(rightPWM, 30, 150);
  143.   
  144.   setMotorSpeed(leftPWM, rightPWM);
  145.   
  146.   Serial.print("跟车:距离=");Serial.print(frontDist);
  147.   Serial.print(",左红外=");Serial.print(leftInfrared);
  148.   Serial.print(",右红外=");Serial.print(rightInfrared);
  149.   Serial.print(",转速:L=");Serial.print(leftPWM);Serial.print(" R=");Serial.print(rightPWM);
  150. }
  151. // ========== 避障逻辑 ==========
  152. void obstacleAvoidance() {
  153.   leftDist = getUltrasonicDistance(LEFT_ULTRA);
  154.   rightDist = getUltrasonicDistance(RIGHT_ULTRA);
  155.   
  156.   if (frontDist < SAFE_DIST) {
  157.     // 前方有障碍,检测左右路径
  158.     if (leftDist > SAFE_DIST && rightDist <= SAFE_DIST) {
  159.       // 左侧可通行,右转
  160.       setMotorSpeed(100, 50);
  161.       Serial.println("避障:前方障碍,右转绕行");
  162.       delay(800);
  163.     } else if (rightDist > SAFE_DIST && leftDist <= SAFE_DIST) {
  164.       // 右侧可通行,左转
  165.       setMotorSpeed(50, 100);
  166.       Serial.println("避障:前方障碍,左转绕行");
  167.       delay(800);
  168.     } else {
  169.       // 左右均不可通行,停车
  170.       setMotorSpeed(0, 0);
  171.       Serial.println("避障:左右均不可通行,停车等待");
  172.     }
  173.   }
  174. }
  175. // ========== 超声波测距函数 ==========
  176. int getUltrasonicDistance(int pin) {
  177.   pinMode(pin, OUTPUT);
  178.   digitalWrite(pin, LOW);
  179.   delayMicroseconds(2);
  180.   digitalWrite(pin, HIGH);
  181.   delayMicroseconds(10);
  182.   digitalWrite(pin, LOW);
  183.   pinMode(pin, INPUT);
  184.   int duration = pulseIn(pin, HIGH);
  185.   return duration * 0.0343 / 2;
  186. }
  187. // ========== 设置轮毂电机转速 ==========
  188. void setMotorSpeed(int leftPWM, int rightPWM) {
  189.   analogWrite(LEFT_WHEEL_PWM, leftPWM);
  190.   analogWrite(RIGHT_WHEEL_PWM, rightPWM);
  191. }
  192. // ========== 回传状态到服务器 ==========
  193. void sendStatusToServer() {
  194.   String status = "DIST:" + String(frontDist) +
  195.                   ",IR_L:" + (leftInfrared ? "1" : "0") +
  196.                   ",IR_R:" + (rightInfrared ? "1" : "0") +
  197.                   ",FOLLOW:" + (isFollowing ? "1" : "0") +
  198.                   ",BAT:100"; // 假设电池电量100%,实际可添加电压检测
  199.   if (client.connected()) {
  200.     client.println(status);
  201.   }
  202. }
复制代码


要点解读
1. 多模通信协同:蓝牙/WiFi与跟随功能的深度适配,平衡灵活性与远程性
蓝牙与WiFi是实现跟随控制的核心通信手段,需根据场景需求精准适配,避免通信与跟随功能的冲突,核心要点如下:
通信模式与场景匹配:蓝牙通信延迟低、成本低,适合室内短距离个性化跟随,与视觉/超声波等本地传感器协同,实现“手柄+自主跟随”的灵活切换,无需依赖网络,应对家庭、商场等小范围场景;WiFi通信覆盖广、支持多终端远程监控,适合室外物流、仓储集群等需要远程管理的场景,可远程下达启动/暂停指令,实时查看机器人状态,满足集中化管理需求。二者的选择需与跟随场景的“范围、管理需求”强绑定,避免用错通信模式导致功能失效。
通信与感知的优先级协调:多模通信易与传感器形成数据冲突,需明确优先级规则,比如案例4的蓝牙+视觉模式中,视觉跟随优先级高于蓝牙遥控,检测到目标时自动切断蓝牙控制,避免人工误操作干扰自主跟随;案例5的WiFi远程与激光跟随中,远程暂停指令优先级最高,可强制暂停跟随,防止跟车失控。通过优先级排序,确保“通信指令”与“自主感知决策”不冲突,避免因指令延迟或传感器失效导致跟随失控。
通信协议的轻量化设计:跟随机器人的通信需兼顾实时性与低功耗,需采用轻量化协议,比如蓝牙用自定义字符串指令,减少数据量,降低传输延迟;WiFi用WebSocket或TCP短连接,避免长连接占用资源,同时对数据进行压缩,减少传输带宽。协议需明确格式,确保主控能快速解析指令,避免因协议复杂导致解析延迟,影响跟随的实时性。

2. BLDC轮毂电机闭环控制:跟随精度的动力保障,应对动态负载变化
6.5寸轮毂BLDC电机是跟随的动力核心,其控制精度直接决定跟随的稳定性,闭环控制是应对跟随过程中负载波动的关键,要点如下:
闭环控制模式适配跟随场景:跟随过程中,机器人会面临路面起伏、负载变化等动态负载,开环控制无法维持转速稳定,需针对性选择闭环模式。案例4的室内跟随用速度闭环(编码器反馈),实时调整PWM输出,保持转速稳定,确保跟随速度平稳;案例5的室外跟车用位置闭环(PID控制),精准控制轮毂电机转动角度,实现精准转向校正,避免跟车时偏离轨迹;案例6的低成本跟随可用简易闭环,通过编码器反馈维持基础速度,平衡成本与性能。闭环模式的选择需以“跟随场景对精度的需求”为核心,避免过度追求闭环导致成本浪费,或开环导致跟随抖动。
PID参数标定与动态适配:PID是闭环控制的核心,参数需结合实际机械结构标定,比如轮毂电机的减速器减速比、底盘重量、跟随速度需求,标定需分两步:先确定比例系数,确保转速响应速度;再调整积分系数,消除稳态误差;最后微调微分系数,抑制超调。跟随过程中需动态适配,比如室外跟车遇到陡坡,PID需自动提升输出扭矩,避免因负载增加导致转速下降,丢失目标。标定需通过实际场景测试,记录不同负载下的转速数据,优化PID参数,确保跟随时的转速稳定性。
轮毂电机与底盘的机械协同:6.5寸轮毂电机直接集成在车轮内,机械安装精度影响控制效果,需确保轮毂电机与底盘的安装同轴度,避免车轮偏心导致转动卡顿,影响跟随精度;同时根据跟随场景选择合适的轮毂电机扭矩,室内跟随选扭矩适中的电机,避免转向过猛;室外跟车选大扭矩电机,适应复杂路面;仓储跟车选低速高扭矩电机,避免与AGV碰撞。机械协同是动力控制的基础,安装精度不足会导致闭环控制失效,即使PID参数完美,也无法实现稳定跟随。

3. 目标感知与识别:跟随的前提,适配不同场景的感知方案
自主跟随的核心是精准识别目标,不同场景的目标特征差异大,感知方案需与场景强适配,要点如下:
感知方案与场景特征匹配:感知方案需根据目标特征和场景环境定制,室内个性化服务用视觉识别(人体特征),利用OpenMV的人体检测算法,识别准确率高,能区分人体与其他障碍;室外物流跟车用激光雷达(车辆轮廓),测距范围广、精度高,能识别远距离的车辆目标,不受光照影响;低成本仓储用超声波+红外(AGV反光条),成本极低,利用AGV的反光特征和超声波测距,适合对成本敏感的场景。感知方案的选择需优先考虑“目标的可识别性”和“环境干扰”,避免在强光下用视觉、远距离用红外等错误组合。
目标识别的抗干扰设计:跟随过程中会遇到各类干扰,需设计抗干扰逻辑,比如视觉识别时,通过设置置信度阈值,过滤低置信度的目标,避免误将墙壁识别为人体;激光雷达识别时,设置车辆的尺寸阈值,过滤行人、货架等小目标;超声波+红外识别时,增加“持续检测时间”条件,避免瞬间干扰导致误识别。同时采用多传感器协同抗干扰,比如视觉+超声波,当视觉识别到目标时,用超声波确认距离,双重验证提升识别可靠性,避免因单一传感器误判导致跟随失控。
目标跟踪的实时性保障:跟随需要实时响应目标的移动,感知数据的更新周期直接影响跟随的灵敏度,需优化传感器的扫描频率和数据传输效率,比如激光雷达设置高扫描频率,OpenMV设置高帧率,超声波缩短测距周期,确保能快速捕捉目标的移动变化。同时主控需及时处理传感器数据,避免因数据拥堵导致延迟,比如用多线程处理传感器数据,将感知解析与电机控制分离,提升数据处理效率,确保目标移动时能快速调整速度和方向,实现实时跟踪。

4. 跟随控制算法:速度与转向的动态适配,实现平稳跟随
跟随的核心是动态控制底盘的速度与转向,让机器人与目标保持安全距离和稳定方位,算法需兼顾动态响应与平稳性,要点如下:
速度控制的比例适配逻辑:跟随速度需与目标距离动态匹配,采用比例控制算法,根据距离偏差调整速度,比例系数需结合实际场景测试确定,系数过大,速度变化剧烈,容易发生碰撞;系数过小,速度响应迟缓,无法跟上目标。比如案例1的视觉跟随,将距离与速度的映射比例设置为,距离远时加速,距离近时减速,距离适中时维持基础速度,确保跟随时既不会追尾,也不会与目标拉开过大距离。比例系数需根据目标移动速度、机器人动力性能调整,确保速度变化平稳。
转向控制的差速校正机制:保持跟随方位的核心是差速转向,根据目标的方位偏差调整左右轮转速,偏差为正(目标偏右)时,右轮减速、左轮加速,实现左转校正;偏差为负时,左轮减速、右轮加速,实现右转校正。差速的幅度需与方位偏差成正比,偏差越大,转速差越大,转向越迅速;偏差小时,转速差小,避免频繁小幅度转向导致跟随抖动。比如案例2的激光跟车,将方位角与转速差的映射关系设置为,角度大时转速差大,角度小时转速差小,确保跟随时始终在目标正后方,避免跟随偏移。
跟随状态的切换与容错:跟随过程中会出现目标丢失、障碍干扰等异常情况,需设计状态切换与容错逻辑,目标丢失时,先减速停车,避免盲目跟随导致碰撞,等待目标重新出现后再启动跟随,同时设置等待时间阈值,避免长时间停车;遇到障碍时,优先避障,避障完成后检测目标位置,自动恢复跟随。状态切换需平稳过渡,比如从跟随切换到避障时,速度逐渐降低,避免急刹车;从避障切换到跟随时,速度逐渐提升,避免猛加速。容错逻辑需覆盖所有异常情况,确保机器人在各种场景下的安全,避免失控。

5. 可靠性与安全保护:跟随的基础底线,应对复杂场景的不确定性
跟随机器人涉及运动控制,可靠性与安全是工程落地的关键,需从硬件、软件、环境适配三方面构建保护体系,要点如下:
硬件级安全保护设计:硬件是安全的第一道防线,需加入多重保护,电源端需设计过流保护、过压保护,防止电机堵转、电源短路导致硬件烧毁;驱动板需支持过流保护、过热保护,当电机过载时自动切断输出;传感器需合理安装,避免运动部件碰撞,同时对传感器进行防护,比如室内用防尘罩保护视觉模块,室外用防水壳保护激光雷达;急停按钮需独立设计,直接控制驱动板电源,不依赖主控,确保紧急情况下能一键停车,避免因主控死机导致失控。硬件保护需覆盖所有可能的风险点,形成完整的保护链。
软件级容错与冗余设计:软件需具备容错能力,避免单一部件失效导致功能瘫痪,传感器数据异常时,启用备用传感器或预设参数,比如视觉失效时,切换到超声波跟随,采用预设的速度与转向参数,维持基本跟随;通信中断时,自动切换到自主跟随模式,停止接收远程指令,依靠本地传感器继续工作,避免因通信中断导致机器人失联;电机控制异常时,启动软保护,限制最大转速,避免转速失控。冗余设计可提升系统的容错能力,确保部分部件失效时,机器人仍能安全运行。
场景化可靠性测试与适配:跟随机器人的可靠性需通过实际场景测试验证,室内跟随需测试不同光照、不同人流密度下的识别与跟随效果,优化视觉的抗干扰能力;室外跟车需测试不同路面、不同天气下的跟随稳定性,优化PID参数和避障逻辑;仓储跟车需测试不同AGV车型、不同货架布局下的识别准确率,调整目标检测参数。测试需覆盖所有典型场景,记录异常情况,针对性优化,比如室外雨天,激光雷达易受雨滴干扰,需增加滤波算法,过滤虚假目标;室内强光下,视觉模块易出现曝光过度,需调整镜头角度和曝光参数。通过场景化测试,让机器人适配实际应用场景,确保稳定可靠。

注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

【花雕】蓝牙 / WiFi 自动跟随机器人底盘(6.5 寸轮毂版)图1

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2026 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail