4、仓储货物跟随机器人(扫码引导+二维码目标跟随)
场景定位:适用于仓库、电商分拣中心,机器人跟随搬运车或工人,精准追踪预设二维码标识的目标(如货架标签、货物托盘),实现货物的自动跟随与定点停靠,核心需求是二维码识别+轮毂电机差速转向+跟随距离控制,6.5寸轮毂电机可直接驱动轮体,简化传动结构,提升底盘可靠性。
硬件配置:Arduino Mega2560(扩展引脚满足多传感器)、TC-1280二维码识别模块(串口通信,识别距离10-30cm)、6.5寸无刷轮毂电机2个(集成霍尔传感器,支持速度闭环控制)、超声波距离传感器(HC-SR04,辅助判断目标距离)、舵机(控制二维码模块旋转,扩大识别范围)。
核心逻辑:
目标锁定:通过舵机带动二维码模块扫描,识别预设目标二维码后锁定跟踪;
距离与角度计算:超声波测目标距离,二维码模块自带角度偏移检测,计算目标相对于机器人的横向偏移;
轮毂电机控制:采用PID距离闭环+差速转向,距离远则加速,距离近则减速;横向偏移则通过左右轮毂电机差速修正航向,保持始终正对目标;
停靠逻辑:距离目标<10cm时停止轮毂电机,发送停靠确认信号(如蜂鸣器提示)。
- #include <Servo.h> // 舵机控制库
-
- #include <NewPing.h> // 超声波距离库
-
-
-
- // 硬件引脚定义
-
- #define LEFT_WHEEL_PWM 9 // 左轮毂电机PWM(接驱动模块)
-
- #define LEFT_WHEEL_DIR 8 // 左电机方向控制
-
- #define RIGHT_WHEEL_PWM 6 // 右轮毂电机PWM
-
- #define RIGHT_WHEEL_DIR 5 // 右电机方向控制
-
- #define QR_MODULE_TX 10 // 二维码模块TX
-
- #define QR_MODULE_RX 11 // 二维码模块RX
-
- #define ULTRA_TRIG 12 // 超声波触发
-
- #define ULTRA_ECHO 13 // 超声波接收
-
- #define SERVO_PIN 14 // 舵机控制(二维码模块旋转)
-
- #define BUZZER_PIN 15 // 蜂鸣器(停靠提示)
-
-
-
- // 全局变量
-
- Servo qrServo; // 舵机对象
-
- NewPing ultrasonic(ULTRA_TRIG, ULTRA_ECHO, 300); // 超声波最大300cm
-
- int followDistance = 50; // 目标跟随距离(cm)
-
- int qrCodeValid = 0; // 二维码识别有效标志(0=未识别,1=已识别)
-
- float leftSpeed = 0; // 左轮毂电机PWM值
-
- float rightSpeed = 0; // 右轮毂电机PWM值
-
- int offsetAngle = 0; // 目标横向偏移角度(正为右偏,负为左偏)
-
-
-
- void setup() {
-
- Serial.begin(9600);
-
- // 初始化轮毂电机引脚
-
- pinMode(LEFT_WHEEL_PWM, OUTPUT);
-
- pinMode(LEFT_WHEEL_DIR, OUTPUT);
-
- pinMode(RIGHT_WHEEL_PWM, OUTPUT);
-
- pinMode(RIGHT_WHEEL_DIR, OUTPUT);
-
- // 初始化舵机(二维码模块旋转)
-
- qrServo.attach(SERVO_PIN);
-
- qrServo.write(0); // 初始位置0度
-
- // 初始化超声波
-
- pinMode(ULTRA_TRIG, OUTPUT);
-
- pinMode(ULTRA_ECHO, INPUT);
-
- // 初始化蜂鸣器
-
- pinMode(BUZZER_PIN, OUTPUT);
-
- // 启动二维码模块串口(硬件串口1,避免冲突)
-
- Serial1.begin(9600);
-
- Serial.println("仓储跟随机器人初始化完成");
-
- }
-
-
-
- void loop() {
-
- static unsigned long updateTime = 0;
-
- if (millis() - updateTime < 50) return; // 20Hz更新频率,避免过载
-
- updateTime = millis();
-
-
-
- // 1. 旋转舵机扫描二维码
-
- scanQRCode();
-
- if (!qrCodeValid) {
-
- // 未识别到目标,低速旋转扫描
-
- setMotorSpeed(30, 30);
-
- return;
-
- }
-
-
-
- // 2. 超声波测距+计算目标偏移
-
- calculateTargetParams();
-
-
-
- // 3. 跟随控制(距离+角度双闭环)
-
- if (followDistance > 10) {
-
- followControl();
-
- } else {
-
- // 停靠目标
-
- stopMotors();
-
- digitalWrite(BUZZER_PIN, HIGH);
-
- delay(1000);
-
- digitalWrite(BUZZER_PIN, LOW);
-
- qrCodeValid = 0; // 重置,准备下次扫描
-
- }
-
-
-
- // 4. 调试输出
-
- Serial.print("目标状态:"); Serial.print(qrCodeValid ? "已锁定" : "扫描中");
-
- Serial.print(" 距离:"); Serial.print(followDistance);
-
- Serial.print("cm 偏移:"); Serial.print(offsetAngle);
-
- Serial.print("° 左速:"); Serial.print(leftSpeed);
-
- Serial.print(" 右速:"); Serial.println(rightSpeed);
-
- }
-
-
-
- // 扫描二维码(舵机带动模块旋转,扩大扫描范围)
-
- void scanQRCode() {
-
- static int servoPos = 0;
-
- servoPos += 15;
-
- if (servoPos > 180) servoPos = 0;
-
- qrServo.write(servoPos);
-
- delay(50);
-
-
-
- // 读取二维码模块数据(假设模块返回"TARGET"表示识别成功)
-
- if (Serial1.available() > 0) {
-
- String data = Serial1.readStringUntil('\n');
-
- if (data.indexOf("TARGET") != -1) {
-
- qrCodeValid = 1;
-
- Serial.println("识别到目标二维码,开始锁定跟随");
-
- }
-
- }
-
- }
-
-
-
- // 计算目标距离与偏移角度
-
- void calculateTargetParams() {
-
- // 超声波测目标距离
-
- int distance = ultrasonic.ping_cm();
-
- if (distance > 0 && distance < 300) {
-
- followDistance = distance;
-
- }
-
-
-
- // 计算目标横向偏移(简化逻辑:假设二维码模块安装高度固定,偏移量通过舵机角度估算)
-
- // 实际可根据模块自带的角度数据,此处简化为舵机角度映射
-
- int servoAngle = qrServo.read();
-
- offsetAngle = map(servoAngle, 0, 180, -30, 30); // 舵机0-180度对应偏移-30~30度
-
- }
-
-
-
- // 跟随控制(距离闭环+角度差速修正)
-
- void followControl() {
-
- // 距离PID控制(简化为比例控制,根据距离调整基础速度)
-
- int distanceError = followDistance - 50; // 目标距离50cm,误差为实际-目标
-
- int baseSpeed = map(distanceError, 0, 100, 0, 200); // 误差越大,基础速度越高
-
- baseSpeed = constrain(baseSpeed, 30, 220);
-
-
-
- // 角度差速修正(偏移为正,右轮减速,左轮加速,左转修正)
-
- int angleCorrection = map(offsetAngle, -30, 30, 0, 50);
-
- if (offsetAngle > 0) {
-
- leftSpeed = baseSpeed + angleCorrection;
-
- rightSpeed = baseSpeed - angleCorrection;
-
- } else {
-
- leftSpeed = baseSpeed - angleCorrection;
-
- rightSpeed = baseSpeed + angleCorrection;
-
- }
-
- leftSpeed = constrain(leftSpeed, 30, 220);
-
- rightSpeed = constrain(rightSpeed, 30, 220);
-
-
-
- // 控制轮毂电机
-
- setMotorSpeed(leftSpeed, rightSpeed);
-
- }
-
-
-
- // 设置轮毂电机速度(PWM调速,正转为高电平,反转为低电平)
-
- void setMotorSpeed(float leftPWM, float rightPWM) {
-
- // 左轮毂电机
-
- digitalWrite(LEFT_WHEEL_DIR, HIGH); // 正转
-
- analogWrite(LEFT_WHEEL_PWM, leftPWM);
-
- // 右轮毂电机
-
- digitalWrite(RIGHT_WHEEL_DIR, HIGH);
-
- analogWrite(RIGHT_WHEEL_PWM, rightPWM);
-
- }
-
-
-
- // 停止电机
-
- void stopMotors() {
-
- analogWrite(LEFT_WHEEL_PWM, 0);
-
- analogWrite(RIGHT_WHEEL_PWM, 0);
-
- digitalWrite(LEFT_WHEEL_DIR, LOW);
-
- digitalWrite(RIGHT_WHEEL_DIR, LOW);
-
- }
复制代码
5、户外跟随服务机器人(人体跟随+避障绕行)
场景定位:适用于公园、园区、景区等户外场景,机器人跟随行人,自动避开障碍物(如树木、台阶、垃圾桶),实现“行人走哪,机器人跟哪”的灵活跟随,核心需求是人体红外感知+激光/超声波避障+轮毂电机灵活转向,6.5寸轮毂电机接地面积大,户外适应性强,可直接应对草地、石子路等复杂路面。
硬件配置:Arduino Mega2560、人体红外热释传感器(2个,左右对称安装,识别行人方位)、激光雷达(RPLIDAR A1,360°扫描,检测障碍物,可选低成本超声波阵列替代)、6.5寸无刷轮毂电机2个(带霍尔编码器,支持速度反馈)、锂电池(12V,支持户外续航)、蜂鸣器(避障预警)。
核心逻辑:
人体识别:左右人体红外传感器检测行人方位,判断行人在机器人左侧、右侧还是正前方;
避障检测:激光雷达扫描周围环境,识别障碍物距离与方位,判断是否需要绕行;
跟随与避障决策:无障碍物时,跟随行人(正前方则直行,侧方则差速转向);有障碍物时,优先避障,避开后重新锁定行人继续跟随;
轮毂电机闭环控制:编码器反馈速度,通过PID控制轮毂电机速度,确保跟随稳定,不抖动。
- #include <Wire.h>
-
- #include <SPI.h>
-
-
-
- // 硬件引脚定义(假设用超声波替代激光雷达,降低成本)
-
- #define LEFT_WHEEL_PWM 9
-
- #define LEFT_WHEEL_DIR 8
-
- #define RIGHT_WHEEL_PWM 6
-
- #define RIGHT_WHEEL_DIR 5
-
- #define LEFT_IR_PIN 10 // 左侧人体红外
-
- #define RIGHT_IR_PIN 11 // 右侧人体红外
-
- #define FRONT_ULTRA_TRIG 12 // 前方超声波
-
- #define FRONT_ULTRA_ECHO 13 // 前方超声波
-
- #define LEFT_ULTRA_TRIG 14 // 左侧超声波
-
- #define LEFT_ULTRA_ECHO 15 // 左侧超声波
-
- #define RIGHT_ULTRA_TRIG 16 // 右侧超声波
-
- #define RIGHT_ULTRA_ECHO 17 // 右侧超声波
-
- #define BUZZER_PIN 18 // 避障蜂鸣器
-
-
-
- // 全局变量
-
- NewPing frontUltra(FRONT_ULTRA_TRIG, FRONT_ULTRA_ECHO, 300);
-
- NewPing leftUltra(LEFT_ULTRA_TRIG, LEFT_ULTRA_ECHO, 300);
-
- NewPing rightUltra(RIGHT_ULTRA_TRIG, RIGHT_ULTRA_ECHO, 300);
-
-
-
- int followState = 0; // 跟随状态(0=未检测到人,1=正前方,2=左侧,3=右侧,4=避障中)
-
- int frontDistance = 0; // 前方障碍物距离
-
- int leftDistance = 0; // 左侧障碍物距离
-
- int rightDistance = 0; // 右侧障碍物距离
-
- int leftSpeed = 80; // 左轮毂电机基础PWM
-
- int rightSpeed = 80; // 右轮毂电机基础PWM
-
- const int SAFE_DISTANCE = 50; // 安全避障距离(cm)
-
- const int FOLLOW_DISTANCE = 80; // 跟随行人距离(cm,需配合距离传感器,此处简化)
-
-
-
- void setup() {
-
- Serial.begin(9600);
-
- // 轮毂电机引脚
-
- pinMode(LEFT_WHEEL_PWM, OUTPUT);
-
- pinMode(LEFT_WHEEL_DIR, OUTPUT);
-
- pinMode(RIGHT_WHEEL_PWM, OUTPUT);
-
- pinMode(RIGHT_WHEEL_DIR, OUTPUT);
-
- // 人体红外引脚
-
- pinMode(LEFT_IR_PIN, INPUT);
-
- pinMode(RIGHT_IR_PIN, INPUT);
-
- // 避障蜂鸣器
-
- pinMode(BUZZER_PIN, OUTPUT);
-
- Serial.println("户外跟随机器人初始化完成");
-
- }
-
-
-
- void loop() {
-
- static unsigned long updateTime = 0;
-
- if (millis() - updateTime < 30) return; // 约30Hz更新,确保实时性
-
- updateTime = millis();
-
-
-
- // 1. 检测人体红外,判断行人方位
-
- detectHuman();
-
-
-
- // 2. 检测避障距离
-
- detectObstacles();
-
-
-
- // 3. 跟随与避障决策控制
-
- if (followState == 4) {
-
- // 避障模式
-
- obstacleAvoidance();
-
- } else if (followState == 1 || followState == 2 || followState == 3) {
-
- // 跟随模式
-
- humanFollow();
-
- } else {
-
- // 未检测到人,低速巡逻
-
- setMotorSpeed(30, 30);
-
- }
-
-
-
- // 4. 调试输出
-
- Serial.print("跟随状态:");
-
- switch(followState) {
-
- case 0: Serial.println("未检测到人"); break;
-
- case 1: Serial.println("正前方有人"); break;
-
- case 2: Serial.println("左侧有人"); break;
-
- case 3: Serial.println("右侧有人"); break;
-
- case 4: Serial.println("避障中"); break;
-
- }
-
- Serial.print("前方距离:"); Serial.print(frontDistance);
-
- Serial.print(" 左侧距离:"); Serial.print(leftDistance);
-
- Serial.print(" 右侧距离:"); Serial.println(rightDistance);
-
- }
-
-
-
- // 检测人体方位
-
- void detectHuman() {
-
- int leftIR = digitalRead(LEFT_IR_PIN);
-
- int rightIR = digitalRead(RIGHT_IR_PIN);
-
-
-
- if (leftIR == HIGH && rightIR == HIGH) {
-
- followState = 1; // 正前方(左右都检测到,判断行人在正前方)
-
- } else if (leftIR == HIGH && rightIR == LOW) {
-
- followState = 2; // 左侧
-
- } else if (leftIR == LOW && rightIR == HIGH) {
-
- followState = 3; // 右侧
-
- } else {
-
- followState = 0; // 未检测到
-
- }
-
- }
-
-
-
- // 检测障碍物距离
-
- void detectObstacles() {
-
- frontDistance = frontUltra.ping_cm();
-
- leftDistance = leftUltra.ping_cm();
-
- rightDistance = rightUltra.ping_cm();
-
- // 无效距离处理(超过最大量程返回0)
-
- if (frontDistance <= 0 || frontDistance > 300) frontDistance = 300;
-
- if (leftDistance <= 0 || leftDistance > 300) leftDistance = 300;
-
- if (rightDistance <= 0 || rightDistance > 300) rightDistance = 300;
-
- }
-
-
-
- // 避障控制(优先绕行)
-
- void obstacleAvoidance() {
-
- // 前方障碍物近,判断左右是否可绕行
-
- if (frontDistance < SAFE_DISTANCE) {
-
- digitalWrite(BUZZER_PIN, HIGH); // 预警
-
- if (leftDistance > rightDistance) {
-
- // 左侧距离远,向左侧绕行
-
- setMotorSpeed(60, 100); // 左慢右快,右转(绕左侧障碍物)
-
- } else if (rightDistance > leftDistance) {
-
- // 右侧距离远,向右侧绕行
-
- setMotorSpeed(100, 60); // 左快右慢,左转(绕右侧障碍物)
-
- } else {
-
- // 左右都近,后退再转向
-
- setMotorSpeed(-50, -50); // 后退
-
- delay(500);
-
- if (leftDistance > SAFE_DISTANCE) {
-
- setMotorSpeed(100, 60); // 左转
-
- } else {
-
- setMotorSpeed(60, 100); // 右转
-
- }
-
- }
-
- } else {
-
- // 避开障碍物,回到跟随模式
-
- followState = 1;
-
- digitalWrite(BUZZER_PIN, LOW);
-
- }
-
- }
-
-
-
- // 跟随行人控制
-
- void humanFollow() {
-
- // 基础速度
-
- int baseSpeed = 80;
-
- switch (followState) {
-
- case 1: // 正前方,直行
-
- leftSpeed = baseSpeed;
-
- rightSpeed = baseSpeed;
-
- break;
-
- case 2: // 左侧,左转跟随(左快右慢)
-
- leftSpeed = baseSpeed + 20;
-
- rightSpeed = baseSpeed - 20;
-
- break;
-
- case 3: // 右侧,右转跟随(左慢右快)
-
- leftSpeed = baseSpeed - 20;
-
- rightSpeed = baseSpeed + 20;
-
- break;
-
- }
-
- // 检查前方是否需要避障
-
- if (frontDistance < SAFE_DISTANCE) {
-
- followState = 4; // 切换到避障模式
-
- } else {
-
- // 无障碍物,执行跟随
-
- setMotorSpeed(leftSpeed, rightSpeed);
-
- }
-
- }
-
-
-
- // 控制轮毂电机(支持正反转,后退功能)
-
- void setMotorSpeed(int leftPWM, int rightPWM) {
-
- // 左轮毂电机(正转:DIR=HIGH,PWM>0;反转:DIR=LOW,PWM>0)
-
- if (leftPWM > 0) {
-
- digitalWrite(LEFT_WHEEL_DIR, HIGH);
-
- analogWrite(LEFT_WHEEL_PWM, leftPWM);
-
- } else {
-
- digitalWrite(LEFT_WHEEL_DIR, LOW);
-
- analogWrite(LEFT_WHEEL_PWM, -leftPWM);
-
- }
-
- // 右轮毂电机
-
- if (rightPWM > 0) {
-
- digitalWrite(RIGHT_WHEEL_DIR, HIGH);
-
- analogWrite(RIGHT_WHEEL_PWM, rightPWM);
-
- } else {
-
- digitalWrite(RIGHT_WHEEL_DIR, LOW);
-
- analogWrite(RIGHT_WHEEL_PWM, -rightPWM);
-
- }
-
- }
复制代码
6、工业巡检跟随机器人(预设轨迹+RFID定点跟随)
场景定位:适用于工厂、车间的工业巡检,机器人沿着预设RFID轨迹点跟随巡检人员,在关键巡检点(如设备、仪表)自动停留,采集数据(温度、压力),核心需求是RFID轨迹识别+轮毂电机轨迹跟踪+定点停靠,6.5寸无刷轮毂电机扭矩大,适合工厂水泥地面、金属地面,耐磨损、抗干扰。
硬件配置:Arduino Mega2560、RFID读卡器(EM4095,读取地面预埋RFID卡轨迹)、温度传感器(DS18B20,采集设备温度)、6.5寸无刷轮毂电机2个(带霍尔编码器)、OLED显示屏(显示巡检数据与跟随状态)、按键(手动切换巡检点)。
核心逻辑:
轨迹识别:地面预埋RFID卡,机器人经过时读取RFID ID,识别当前所在巡检点与目标巡检点;
轨迹跟踪:根据当前巡检点与目标巡检点的轨迹(预设为直线段),通过轮毂电机差速控制,跟踪轨迹;
定点跟随:到达目标巡检点后,停止电机,启动传感器采集数据,显示在OLED上,停留30秒后自动跟随巡检人员至下一个点;
手动干预:通过按键切换巡检点,应对临时需求,提升灵活性。
- #include <OneWire.h> // DS18B20温度传感器库
-
- #include <DallasTemperature.h>
-
- #include <Wire.h>
-
- #include <Adafruit_SSD1306.h> // OLED显示屏库
-
- #include <RFID.h> // RFID读卡器库
-
-
-
- // 硬件引脚定义
-
- #define LEFT_WHEEL_PWM 9
-
- #define LEFT_WHEEL_DIR 8
-
- #define RIGHT_WHEEL_PWM 6
-
- #define RIGHT_WHEEL_DIR 5
-
- #define RFID_RST 10
-
- #define RFID_CS 11
-
- #define ONE_WIRE_PIN 12 // DS18B20数据引脚
-
- #define OLED_RESET 13 // OLED复位(可不接,库默认处理)
-
- #define KEY_PIN 14 // 按键(切换巡检点)
-
-
-
- // 全局变量
-
- RFID rfid(RFID_RST, RFID_CS); // RFID对象
-
- OneWire oneWire(ONE_WIRE_PIN);
-
- DallasTemperature sensors(&oneWire);
-
- Adafruit_SSD1306 display(OLED_RESET); // OLED对象
-
-
-
- // 预设巡检点RFID ID与坐标(简化为轨迹偏移量,实际可结合GPS)
-
- struct CheckPoint {
-
- String rfidId;
-
- int trackOffset; // 轨迹偏移量(机器人相对于轨迹的左右偏移,正为右)
-
- bool isTarget; // 是否为当前目标巡检点
-
- } checkPoints[3] = {
-
- {"123456", 0, false}, // 起点
-
- {"234567", 10, false}, // 巡检点1
-
- {"345678", 0, false} // 巡检点2(终点)
-
- };
-
- int currentCheckIndex = 0; // 当前所在巡检点索引
-
- int targetCheckIndex = 1; // 目标巡检点索引
-
- float trackOffset = 0; // 当前轨迹偏移量
-
- float leftSpeed = 80;
-
- float rightSpeed = 80;
-
- bool isSampling = false; // 是否在采样(定点采集数据)
-
- unsigned long samplingStart = 0;
-
- const int SAMPLING_TIME = 30000; // 采样时间30秒
-
-
-
- void setup() {
-
- Serial.begin(9600);
-
- // 初始化轮毂电机
-
- pinMode(LEFT_WHEEL_PWM, OUTPUT);
-
- pinMode(LEFT_WHEEL_DIR, OUTPUT);
-
- pinMode(RIGHT_WHEEL_PWM, OUTPUT);
-
- pinMode(RIGHT_WHEEL_DIR, OUTPUT);
-
- // 初始化RFID
-
- rfid.init();
-
- // 初始化温度传感器
-
- sensors.begin();
-
- // 初始化OLED
-
- display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // 0x3C为常见OLED地址
-
- display.clearDisplay();
-
- display.setTextSize(1);
-
- display.setTextColor(WHITE);
-
- display.println("工业巡检机器人");
-
- display.display();
-
- // 初始化按键
-
- pinMode(KEY_PIN, INPUT_PULLUP);
-
- Serial.println("巡检跟随机器人初始化完成");
-
- }
-
-
-
- void loop() {
-
- static unsigned long updateTime = 0;
-
- if (millis() - updateTime < 40) return; // 25Hz更新
-
- updateTime = millis();
-
-
-
- // 1. 检测按键,手动切换巡检点
-
- checkKey();
-
-
-
- // 2. 读取RFID,识别当前巡检点
-
- recognizeCheckPoint();
-
-
-
- // 3. 轨迹跟踪与跟随控制
-
- if (!isSampling) {
-
- if (currentCheckIndex != targetCheckIndex) {
-
- trackFollowing(); // 跟踪轨迹至目标点
-
- } else {
-
- // 到达目标点,启动采样
-
- startSampling();
-
- }
-
- } else {
-
- // 采样中,检测是否完成
-
- checkSamplingComplete();
-
- }
-
-
-
- // 4. 更新OLED显示
-
- updateDisplay();
-
-
-
- // 5. 按键检测与状态调试
-
- if (digitalRead(KEY_PIN) == LOW) {
-
- delay(200); // 消抖
-
- targetCheckIndex = (targetCheckIndex + 1) % 3;
-
- Serial.print("手动切换目标巡检点:"); Serial.println(targetCheckIndex);
-
- }
-
- }
-
-
-
- // 识别当前巡检点(读取RFID)
-
- void recognizeCheckPoint() {
-
- if (rfid.readCard()) {
-
- String readId = rfid.getCardId();
-
- for (int i = 0; i < 3; i++) {
-
- if (readId == checkPoints[i].rfidId) {
-
- currentCheckIndex = i;
-
- checkPoints[i].isTarget = (i == targetCheckIndex);
-
- Serial.print("识别到巡检点:"); Serial.print(i);
-
- Serial.print(",RFID ID:"); Serial.println(readId);
-
- if (i == targetCheckIndex) {
-
- Serial.println("已到达目标巡检点,准备采样");
-
- }
-
- rfid.stopRead();
-
- break;
-
- }
-
- }
-
- }
-
- }
-
-
-
- // 轨迹跟踪控制(基于偏移量差速修正)
-
- void trackFollowing() {
-
- int targetOffset = checkPoints[targetCheckIndex].trackOffset;
-
- float offsetError = targetOffset - trackOffset;
-
- // 偏移误差修正,误差为正,右轮加速,右转修正
-
- float correction = map(offsetError, -20, 20, -30, 30);
-
- leftSpeed = 80 - correction;
-
- rightSpeed = 80 + correction;
-
- leftSpeed = constrain(leftSpeed, 30, 180);
-
- rightSpeed = constrain(rightSpeed, 30, 180);
-
-
-
- // 控制轮毂电机
-
- digitalWrite(LEFT_WHEEL_DIR, HIGH);
-
- digitalWrite(RIGHT_WHEEL_DIR, HIGH);
-
- analogWrite(LEFT_WHEEL_PWM, leftSpeed);
-
- analogWrite(RIGHT_WHEEL_PWM, rightSpeed);
-
-
-
- // 模拟轨迹偏移变化(实际可通过编码器反馈计算偏移,此处简化)
-
- trackOffset += offsetError * 0.1;
-
- if (trackOffset > 20) trackOffset = 20;
-
- if (trackOffset < -20) trackOffset = -20;
-
- }
-
-
-
- // 启动采样(定点数据采集)
-
- void startSampling() {
-
- isSampling = true;
-
- samplingStart = millis();
-
- stopMotors();
-
- Serial.println("开始采样,采集设备温度");
-
- }
-
-
-
- // 检查采样是否完成
-
- void checkSamplingComplete() {
-
- if (millis() - samplingStart >= SAMPLING_TIME) {
-
- isSampling = false;
-
- // 自动切换至下一个巡检点
-
- targetCheckIndex = (targetCheckIndex + 1) % 3;
-
- Serial.print("采样完成,切换至巡检点:"); Serial.println(targetCheckIndex);
-
- // 启动电机,跟随至下一个点
-
- setMotorSpeed(80, 80);
-
- }
-
- }
-
-
-
- // 更新OLED显示
-
- void updateDisplay() {
-
- display.clearDisplay();
-
- display.setCursor(0, 0);
-
- display.print("当前巡检点:");
-
- display.print(currentCheckIndex);
-
- display.println(" 目标:" + String(targetCheckIndex));
-
- display.setCursor(0, 10);
-
- if (isSampling) {
-
- float remaining = (SAMPLING_TIME - (millis() - samplingStart)) / 1000;
-
- display.print("采样中,剩余:");
-
- display.print(remaining);
-
- display.println("s");
-
- // 读取温度
-
- sensors.requestTemperatures();
-
- float temp = sensors.getTempCByIndex(0);
-
- display.setCursor(0, 20);
-
- display.print("设备温度:");
-
- display.print(temp);
-
- display.println(" ℃");
-
- } else {
-
- display.print("跟踪轨迹中");
-
- display.println("偏移:" + String(trackOffset));
-
- }
-
- display.display();
-
- }
-
-
-
- // 检测按键
-
- void checkKey() {
-
- // 已在loop开头处理按键,此处为占位,实际可扩展按键功能
-
- }
-
-
-
- // 停止电机
-
- void stopMotors() {
-
- analogWrite(LEFT_WHEEL_PWM, 0);
-
- analogWrite(RIGHT_WHEEL_PWM, 0);
-
- digitalWrite(LEFT_WHEEL_DIR, LOW);
-
- digitalWrite(RIGHT_WHEEL_DIR, LOW);
-
- }
-
-
-
- // 设置电机速度
-
- void setMotorSpeed(int leftPWM, int rightPWM) {
-
- digitalWrite(LEFT_WHEEL_DIR, HIGH);
-
- digitalWrite(RIGHT_WHEEL_DIR, HIGH);
-
- analogWrite(LEFT_WHEEL_PWM, leftPWM);
-
- analogWrite(RIGHT_WHEEL_PWM, rightPWM);
-
- }
复制代码
要点解读
1. 6.5寸无刷轮毂电机的选型与驱动适配:底盘执行的“动力核心”
6.5寸无刷轮毂电机将电机、减速器、轮毂集成,简化底盘结构,但选型和驱动适配直接决定跟随系统的可靠性,需重点关注:
扭矩与转速匹配:户外服务机器人需大扭矩应对复杂路面,仓储机器人需适中转速保证跟随效率,需根据场景选择合适扭矩(一般户外场景选≥5N·m,仓储选3-5N·m)和转速(100-300rpm,配合PWM调速);
驱动模块兼容性:轮毂电机通常为三相无刷,需搭配专用无刷驱动模块(如VESC、C620),而非普通L298N,案例中为简化使用两路有刷驱动模拟,实际需确认驱动模块支持无刷电机的霍尔信号接入,实现速度闭环;
控制方式选择:基础跟随用开环PWM控制即可,进阶需求(如高精度轨迹跟踪、速度稳定)需接入霍尔编码器,通过PID实现速度闭环,减少路面颠簸导致的速度波动,确保跟随平稳。
2. 跟随传感器的感知逻辑:从“识别目标”到“判断状态”的关键
自动跟随的核心是传感器精准感知目标,不同场景的传感器方案需匹配目标特性,避免感知偏差:
目标特性适配传感器:仓储场景目标为静态二维码,用二维码模块精准识别;户外场景目标为动态行人,用人体红外或视觉传感器(如OpenMV)识别动态目标;工业场景目标为固定轨迹,用RFID识别静态轨迹点,传感器需与目标形态、运动状态匹配,避免漏检或误检;
多传感器融合提升可靠性:单一传感器易失效(如户外阳光干扰人体红外、二维码被遮挡),案例中采用“主传感器+辅助传感器”融合:仓储用二维码+超声波测距,户外用人体红外+超声波避障,工业用RFID+温度传感器,多传感器数据交叉验证,降低单一传感器故障风险;
感知数据的预处理与状态判断:传感器原始数据需预处理(如滤波、无效值剔除),再转换为机器人可理解的“状态”(如目标方位、距离、偏移量),案例中通过限幅滤波处理超声波数据,通过ID匹配识别RFID巡检点,将原始信号转化为控制算法需要的决策依据,避免无效数据导致误动作。
3. 差速转向与跟随控制算法:实现“平稳跟随”的核心策略
自动跟随的本质是控制轮毂电机差速转向,让机器人始终对准目标并保持合适距离,算法需兼顾响应速度与稳定性:
差速转向的核心逻辑:轮式机器人差速转向通过左右轮毂电机速度差实现,目标偏左则左轮加速、右轮减速(左转),目标偏右则相反,基础算法为“基础速度+偏差修正”,偏差越大修正幅度越大,案例中采用比例控制实现简单高效的转向,也可进阶为PID控制,提升转向精度和稳定性;
距离闭环与角度闭环的协同:跟随需同时控制距离(保持跟随间距)和角度(对准目标),案例1采用距离比例控制+角度差速修正,距离远则加速,角度偏则转向,两者协同避免“距离近但角度偏”或“角度正但距离远”的问题,实现“对准且保持距离”的双重目标;
避障与跟随的优先级切换:户外、工业场景存在障碍物,需明确“避障优先于跟随”,案例2中当障碍物距离小于安全阈值时,立即切换至避障模式,避开后再重新锁定目标,避免碰撞,切换逻辑需设计状态机,确保状态切换平滑,无顿挫,不丢失目标。
4. 多任务调度与实时性:确保系统响应“不卡顿、不丢包”
自动跟随系统需同时处理传感器采集、算法计算、电机控制、显示输出等多任务,Arduino资源有限,需合理调度:
控制周期的合理设定:电机控制需高频(案例中30-50ms更新一次),保证跟随响应及时;传感器采集可匹配其刷新率(如超声波50ms、RFID100ms),避免频繁采集导致CPU过载;通过millis计算时间间隔,替代delay实现非阻塞式调度,确保多任务并行处理,案例中采用定时更新策略,平衡实时性与资源占用;
任务优先级划分:核心任务(传感器读取、电机控制)优先级最高,显示、调试等次要任务可适当降低更新频率,避免次要任务占用过多CPU导致核心控制延迟,如OLED显示更新频率可设为1Hz,远低于电机控制频率;
资源冲突规避:Arduino硬件串口资源有限,多传感器(如GPS、二维码模块)需合理分配串口,避免串口冲突;硬件引脚按功能分组,避免引脚混乱,同时预留备用引脚,方便后期扩展,如案例3中用软件串口处理RFID数据,避免与OLED冲突。
5. 户外适应性与可靠性设计:应对复杂环境的“底线保障”
自动跟随机器人常工作在户外、工业等复杂环境,可靠性是落地的前提,需从硬件、软件多维度设计:
硬件防护与适配:户外场景需考虑防水防尘(轮毂电机、传感器加防护壳)、电池续航(选用大容量锂电池,搭配稳压模块保证电压稳定)、抗干扰(工业场景需远离强电,传感器线缆采用屏蔽线);轮毂电机的接地面积大,提升复杂路面的通过性,减少打滑;
软件的容错与自恢复:传感器失效时,软件需设计容错机制,如二维码识别失败时,机器人低速旋转扫描,而非停止不动;避障时左右距离都近时,自动后退再转向,避免卡住;通信失败时,采用默认安全策略(如停止电机、预警),防止失控;
能耗优化与续航保障:轮毂电机无刷高效,本身能耗低于有刷电机,软件上可在目标丢失时降低电机速度(低速巡逻),到达目标点时关闭非必要外设(如二维码模块电源),降低静态功耗,延长电池续航,同时通过OLED或蜂鸣器实时反馈状态,方便运维人员及时发现问题,快速修复。
综上,6.5寸无刷轮毂电机的自动跟随机器人核心是“传感器感知-算法决策-轮毂电机执行”的闭环设计,后面三个案例覆盖静态目标、动态目标、轨迹目标三类典型场景,代码逻辑兼顾实用性与扩展性,开发者可根据实际场景调整传感器接口、控制参数,快速搭建稳定可靠的自动跟随底盘。需注意:实际无刷轮毂电机的驱动需搭配专用无刷驱动模块,案例中为简化展示采用有刷电机驱动逻辑模拟,落地时需根据驱动模块的通信协议(如CAN、PWM+方向)修改电机控制代码,同时可加入编码器反馈实现更精准的速度闭环。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

|