【花雕学编程】Arduino BLDC 之智能仓储拣货机器人:FSM驱动+任务队列+状态指示

Arduino BLDC之智能仓储拣货机器人(FSM驱动+任务队列+状态指示)是一套以有限状态机为决策核心、任务队列为执行载体、状态指示为运维窗口的三层解耦机器人控制系统,其核心优势在于将复杂的拣货流程拆解为确定性的状态转换,主要适用于中小型仓储、教育科研及轻量级自动化场景,但落地时需重点解决算力瓶颈、状态死锁与电源隔离等工程问题。
1、系统架构与技术原理
该系统采用"决策-调度-执行"三层解耦架构,将行为逻辑、任务管理与电机驱动彻底分离:
FSM决策层:有限状态机(FSM)是系统的"大脑",由状态(State)、事件(Event)、转换(Transition)三要素构成。典型状态包括:IDLE(空闲等待)、NAVIGATING(导航中)、PICKING(拣货中)、RETURNING(返程中)、CHARGING(充电中)、ERROR(故障)。每个状态对应一组确定的行为动作,事件触发状态之间的确定性转换,例如"收到新任务"事件将系统从IDLE切换到NAVIGATING。
任务队列调度层:系统内部维护一个FIFO(先进先出)或优先级队列,存储待执行的拣货任务(包含目标货架坐标、SKU编号、数量等)。上位机(WMS/WCS)通过无线通信下发任务,MCU将其入队;FSM每完成一个任务周期后自动出队并执行下一个。
BLDC执行驱动层:基于Arduino生态(如SimpleFOC库),通过FOC(磁场定向控制)驱动无刷电机,实现位置/速度/力矩三环闭环控制,确保机器人在导航、停靠、取放货等环节的精准运动。
2、主要特点
FSM驱动的确定性行为控制
逻辑清晰、可预测:FSM将复杂的拣货流程拆解为有限的离散状态,系统在任何时刻都处于且仅处于一个确定状态,避免了传统"意大利面条式"代码中条件分支交叉混乱的问题。
Moore型与Mealy型混合设计:状态内部的动作(如导航时持续运行PID速度环)采用Moore型——输出仅依赖当前状态;状态转换时的瞬时动作(如到达目标点时触发机械臂抓取)采用Mealy型——输出依赖当前事件,使逻辑表达最为清晰。
异常处理天然内嵌:任何状态下均可响应EMERGENCY_STOP、OBSTACLE_DETECTED、LOW_BATTERY等异常事件,统一跳转至ERROR或SAFE_STOP状态,确保系统在任何异常下都有明确的退出路径,不会陷入未定义行为。
任务队列的弹性调度
异步任务接收:上位机可随时向队列中追加任务,无需等待当前任务完成。MCU通过中断或轮询方式接收无线指令并入队,实现任务下发与执行的完全解耦。
动态优先级插入:支持紧急任务(如补货优先级高于常规拣货)插入队列头部,或在队列中按任务紧急度、货架距离等条件进行排序,实现轻量级的任务优先级仲裁。
断点续执行:当任务因低电量中断时,当前任务信息保留在队列中,充电完成后自动恢复执行,无需上位机重新下发。
多维度状态指示
本地可视化:通过LED阵列或OLED屏幕实时显示当前状态(如绿灯慢闪=空闲、蓝灯快闪=导航中、黄灯常亮=拣货中、红灯闪烁=故障),使现场运维人员无需连接电脑即可判断机器人运行状况。
远程状态上报:通过Wi-Fi/MQTT协议周期性向上位机上报当前状态、队列深度、电池SOC、电机温度等信息,支持WMS系统实时监控多台机器人的运行态势。
故障码标准化:不同故障类型对应不同的LED闪烁频率或错误码(如0x01=编码器故障、0x02=电机过流、0x03=通信超时),便于快速定位问题。
BLDC高效精准执行
高效率长续航:BLDC电机效率>85%,配合FOC正弦波驱动消除转矩脉动,机器人启停极其平滑,有效保护所承载的货物。
多模式切换:导航阶段采用速度闭环维持巡航效率,接近目标点时切换至位置闭环实现毫米级精准停靠,拣货阶段切换至力矩闭环保持机械臂柔顺抓取。
3、典型应用场景
中小型电商仓库的"货到人"拣货
在SKU种类多、订单碎片化的电商仓库中,机器人根据WMS下发的拣货任务队列,依次导航至各货架位,通过二维码/RFID定位精准停靠,配合升降机构或机械臂完成取货,再运送至拣货工作站。FSM确保每个环节严格按序执行,任务队列保证多订单的高效串行处理。
工厂线边物流配送
在生产车间内,机器人根据工位的物料呼叫请求(入队),按优先级依次从仓库取料并配送至指定工位。FSM管理"等待呼叫→取料→配送→返回"的完整循环,状态指示让车间工人直观了解机器人的当前位置和预计到达时间。
图书馆/档案馆的自动归架
机器人接收归还书籍的扫码信息后入队,依次导航至对应书架位置完成上架。任务队列支持批量处理,FSM确保机器人在密集书架通道中的安全行驶和精准定位。
教育科研与算法验证平台
作为机器人学、嵌入式系统、状态机理论的教学实验平台,学生可以直观观察FSM状态转换过程,修改任务调度策略,验证新的路径规划或避障算法,是低成本、高可视化的理想验证环境。
医院药品/样本配送
在楼宇内自动运送药品、检验样本或医疗器械,FSM管理电梯呼叫、楼层切换、走廊导航等复杂流程,任务队列支持多科室请求的优先级排序。
4、注意事项与关键技术挑战
MCU算力与实时性瓶颈
Arduino Uno/Nano等8位MCU主频仅16MHz,难以同时处理FSM推理、任务队列管理、PID运算和无线通信,容易导致控制周期不稳定。
对策:必须选用带硬件FPU的32位MCU(如ESP32-S3双核、STM32F4),或采用"上位机(树莓派/Jetson)负责FSM与任务调度 + Arduino作为底层BLDC执行器"的分层架构,通过UART/SPI高速通信解耦。
FSM状态死锁与非法转换
若状态转换表设计不严谨,可能出现"两个状态互相等待"的死锁,或因传感器噪声触发非法转换导致系统崩溃。
对策:编码前必须绘制完整的状态转换图(STD),穷举所有可能的状态×事件组合;为每个状态设置超时保护(如NAVIGATING超过预定时间未到达则跳转ERROR);启用硬件看门狗定时器防止程序跑飞。
任务队列的线程安全
无线接收中断与主循环同时访问任务队列时,若不加保护会导致数据竞争和队列损坏。
对策:采用SPSC(单生产者单消费者)环形缓冲区模型——中断服务程序(ISR)只负责写入,主循环只负责读取,通过head/tail指针天然避免竞争;若使用FreeRTOS,则直接使用线程安全的xQueueSend/xQueueReceive API。
电源隔离与电磁兼容(EMC)
BLDC电机启停和堵转时产生的大电流尖峰会通过共地线路干扰MCU,导致FSM状态错乱或通信中断。
对策:电机驱动电源与MCU逻辑电源必须物理隔离(独立DC-DC模块);电机相线套屏蔽网;母线并联≥470μF电解电容抑制电压跌落;急停信号采用硬件中断直接切断电机使能,绕过软件调度层。
通信可靠性与离线容错
仓库中金属货架会造成Wi-Fi信号遮挡和多径反射,通信中断时机器人可能丢失任务或停在通道中央。
对策:MCU端必须本地缓存任务队列,网络恢复后自动续传上报状态;通信协议加入CRC校验和心跳检测;连续N次心跳丢失后自动进入安全停车状态。
编码器精度与里程计漂移
仅靠编码器积分的里程计会随时间累积误差,导致机器人在长距离导航后偏离目标货架。
对策:在关键节点(如货架位、充电座)部署二维码/RFID地标进行绝对位置校正;结合IMU进行航向角补偿;定期执行"归零校准"消除累积误差。
安全合规性
拣货机器人在人机混行环境中运行,必须满足工业车辆安全标准(如ISO 3691-4)。
对策:配备激光雷达/超声波阵列实现动态避障;设置空载/满载不同速度档位;急停按钮符合ISO 13850标准;FSM中任何状态下均可响应急停事件并立即切断电机动力。

1、基础FSM顺序拣货(固定航点)
场景:标准化仓库中,机器人按固定路径依次前往多个货架点位,执行“到达→取货→前往下一站”的循环。
核心逻辑:将整个拣货任务分解为离散状态(IDLE→MOVING→LOADING→UNLOADING→RETURNING),用FSM管理状态间的跳转。每个状态负责一段行为逻辑:前进至目标点、执行取货动作、前往下一站点等。到达一个航点后执行动作并切换至下一个任务索引。
#include <SimpleFOC.h>
// ===== BLDC差速电机 =====
BLDCMotor motorL(7), motorR(8);
// ===== FSM状态枚举 =====
enum RobotState {
STATE_IDLE,
STATE_MOVING_TO_SHELF, // 前往货架
STATE_LOADING, // 取货
STATE_MOVING_TO_DROP, // 前往卸货点
STATE_UNLOADING, // 卸货
STATE_RETURNING, // 返回充电/起点
STATE_EMERGENCY_STOP // 紧急状态
};
RobotState currentState = STATE_IDLE;
// ===== 拣货任务队列(货架坐标,简化用脉冲数)=====
struct PickTask {
int targetPulse; // 目标位置(编码器脉冲)
int shelfID; // 货架编号
int actionDuration; // 取货/卸货停留时间(ms)
};
PickTask pickQueue[] = {
{1000, 1, 2000}, // 货架1:前进至1000脉冲,停留2秒
{2500, 2, 1500}, // 货架2
{4000, 3, 3000} // 货架3
};
int numTasks = sizeof(pickQueue) / sizeof(pickQueue[0]);
int currentTaskIndex = 0;
// ===== 状态指示(LED+蜂鸣器)=====
#define LED_RED 3
#define LED_GREEN 4
#define LED_BLUE 5
#define BUZZER 6
// ===== 控制变量 =====
const int SPEED_FORWARD = 130; // PWM值
bool motorRunning = false;
void setup() {
Serial.begin(115200);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(BUZZER, OUTPUT);
// BLDC电机初始化...
currentState = STATE_IDLE;
Serial.println("拣货机器人启动,等待指令");
}
void loop() {
// 1. 获取编码器脉冲
long currentPulse = getEncoderPulse();
// 2. FSM主循环
switch (currentState) {
case STATE_IDLE: {
motorL.move(0); motorR.move(0);
setIndicator(0, 255, 0); // 绿灯:待命
// 收到启动指令后进入拣货状态
if (Serial.read() == 'S') {
currentState = STATE_MOVING_TO_SHELF;
Serial.println("→ 开始拣货任务");
}
break;
}
case STATE_MOVING_TO_SHELF: {
setIndicator(255, 255, 0); // 黄灯:行驶中
int targetPulse = pickQueue[currentTaskIndex].targetPulse;
int error = targetPulse - currentPulse;
if (abs(error) < 10) { // 到达目标
motorL.move(0); motorR.move(0);
motorRunning = false;
currentState = STATE_LOADING;
Serial.print("→ 到达货架"); Serial.println(pickQueue[currentTaskIndex].shelfID);
} else {
int speed = constrain(abs(error) * 0.5, 30, SPEED_FORWARD);
motorL.move(error > 0 ? speed : -speed);
motorR.move(error > 0 ? speed : -speed);
motorRunning = true;
}
break;
}
case STATE_LOADING: {
setIndicator(0, 0, 255); // 蓝灯:执行取货
static unsigned long loadStart = 0;
if (loadStart == 0) {
loadStart = millis();
digitalWrite(BUZZER, HIGH);
delay(100);
digitalWrite(BUZZER, LOW);
Serial.print("→ 取货中 (货架"); Serial.print(pickQueue[currentTaskIndex].shelfID);
Serial.println(")");
}
if (millis() - loadStart > pickQueue[currentTaskIndex].actionDuration) {
loadStart = 0;
currentTaskIndex++;
if (currentTaskIndex >= numTasks) {
currentState = STATE_MOVING_TO_DROP;
} else {
currentState = STATE_MOVING_TO_SHELF;
}
}
break;
}
case STATE_MOVING_TO_DROP: {
setIndicator(255, 255, 0); // 黄灯:行驶中
// 前往卸货点(固定位置)
int dropPulse = 5000; // 卸货点编码器脉冲
int error = dropPulse - currentPulse;
if (abs(error) < 10) {
motorL.move(0); motorR.move(0);
currentState = STATE_UNLOADING;
Serial.println("→ 到达卸货点");
} else {
int speed = constrain(abs(error) * 0.5, 30, SPEED_FORWARD);
motorL.move(error > 0 ? speed : -speed);
motorR.move(error > 0 ? speed : -speed);
}
break;
}
case STATE_UNLOADING: {
setIndicator(0, 255, 255); // 青色:卸货中
static unsigned long unloadStart = 0;
if (unloadStart == 0) {
unloadStart = millis();
digitalWrite(BUZZER, HIGH);
delay(200);
digitalWrite(BUZZER, LOW);
Serial.println("→ 卸货中");
}
if (millis() - unloadStart > 2000) {
unloadStart = 0;
currentState = STATE_RETURNING;
Serial.println("→ 任务完成,返回起点");
}
break;
}
case STATE_RETURNING: {
setIndicator(255, 0, 0); // 红灯:返回中
if (abs(currentPulse) < 10) {
motorL.move(0); motorR.move(0);
currentState = STATE_IDLE;
setIndicator(0, 255, 0);
Serial.println("→ 已回到起点,等待新任务");
} else {
motorL.move(-SPEED_FORWARD * 0.6);
motorR.move(-SPEED_FORWARD * 0.6);
}
break;
}
case STATE_EMERGENCY_STOP: {
motorL.move(0); motorR.move(0);
setIndicator(255, 0, 0); // 红灯快闪
digitalWrite(BUZZER, HIGH);
Serial.println("⚠ 紧急停止!");
break;
}
}
// 3. 状态指示器(当前状态输出到串口/屏幕)
printStateToSerial();
delay(50);
}
void setIndicator(int r, int g, int b) {
analogWrite(LED_RED, r);
analogWrite(LED_GREEN, g);
analogWrite(LED_BLUE, b);
}
void printStateToSerial() {
static RobotState lastState = STATE_IDLE;
if (currentState != lastState) {
Serial.print("当前状态: ");
switch(currentState) {
case STATE_IDLE: Serial.println("待命"); break;
case STATE_MOVING_TO_SHELF: Serial.println("前往货架"); break;
case STATE_LOADING: Serial.println("取货中"); break;
case STATE_MOVING_TO_DROP: Serial.println("前往卸货点"); break;
case STATE_UNLOADING: Serial.println("卸货中"); break;
case STATE_RETURNING: Serial.println("返回起点"); break;
case STATE_EMERGENCY_STOP: Serial.println("紧急停止"); break;
}
lastState = currentState;
}
}
2、双机协同 + 任务队列(动态分配)
场景:两台拣货机器人在同一仓储区域作业,通过无线通信(UWB/ESP-NOW)共享任务负载,实现动态任务分配与负载均衡。
核心逻辑:借鉴“双机器人智能补货”架构,每台机器人维护自己的任务队列,通过无线交换任务数量和状态。负载均衡采用模糊协商——电量高、距任务点近的机器人承接更多任务;当某机任务受阻(如堵转),可通过任务窃取(Work Stealing)广播释放任务。
#include <SimpleFOC.h>
#include <esp_now.h>
// ===== BLDC差速电机 =====
BLDCMotor motorL(7), motorR(8);
// ===== 拣货任务结构 =====
struct PickTask {
int id;
int shelfX, shelfY; // 货架坐标(栅格)
bool active;
unsigned long assignedTime;
int priority; // 1=普通, 2=紧急(低库存)
};
// ===== 任务队列(环形缓冲区)=====
#define MAX_TASKS 10
PickTask myQueue[MAX_TASKS];
int taskCount = 0;
// ===== 邻机状态(无线接收)=====
struct PeerState {
int id;
float battery; // 0~1
int taskCount;
float x, y;
};
PeerState peer;
// ===== 机器人自身状态 =====
float selfBattery = 1.0;
float selfX = 0, selfY = 0;
bool isStuck = false;
// ===== 任务分配函数 =====
float evalPriority(float bat, float distToTask) {
float p = 0.5;
p += bat * 0.3;
p += (1.0 - constrain(distToTask / 20.0, 0, 1)) * 0.3;
return constrain(p, 0.2, 0.9);
}
void loop() {
// 1. 更新自身状态
updateBattery();
updatePosition();
// 2. 接收邻机状态(非阻塞)
if (esp_now_receive_peer(&peer)) {
// 已更新邻机数据
}
// 3. 【核心】负载均衡:比较自身与邻机的任务队列长度
int myTasks = taskCount;
int peerTasks = peer.taskCount;
if (myTasks > peerTasks + 2) {
// 自身任务过多,让渡非紧急任务给邻机
for (int i = 0; i < myTasks; i++) {
if (myQueue[i].priority == 1 && myQueue[i].active) {
sendTaskToPeer(myQueue[i]);
myQueue[i].active = false;
taskCount--;
Serial.printf("让渡任务 #%d 给机器人 %d\n", myQueue[i].id, peer.id);
break;
}
}
}
// 4. 若邻机任务过多且自身空闲,窃取任务
if (myTasks == 0 && peerTasks > 3) {
if (requestTaskFromPeer()) {
// 成功窃取,更新队列
}
}
// 5. 执行当前任务
if (taskCount > 0) {
executeNextTask();
} else {
goToChargingStation(); // 无任务时回充
}
delay(50);
}
3、完整拣货闭环 + MQTT任务下发 + 状态上报
场景:与WMS(仓库管理系统)对接,通过MQTT接收任务,执行后上报完成状态。货架库存低于阈值自动生成补货任务。
核心逻辑:采用FSM管理拣货生命周期——从接收任务、导航到货架、取货、卸货到上报完成,每个状态切换时通过MQTT同步状态。任务队列由WMS通过MQTT动态填充,支持紧急任务插队。
#include <SimpleFOC.h>
#include <WiFi.h>
#include <PubSubClient.h>
// ===== BLDC差速电机 =====
BLDCMotor motorL(7), motorR(8);
// ===== MQTT配置 =====
WiFiClient espClient;
PubSubClient mqtt(espClient);
#define MQTT_TOPIC_TASK "warehouse/robot/task"
#define MQTT_TOPIC_STATUS "warehouse/robot/status"
// ===== FSM状态 =====
enum State { IDLE, MOVING, PICKING, DROPPING, COMPLETE, ERROR };
State currentState = IDLE;
// ===== 当前任务 =====
struct Task {
int id;
int shelfX, shelfY;
int dropX, dropY;
bool isEmergency;
};
Task currentTask;
bool hasTask = false;
// ===== MQTT回调:接收WMS下发的任务 =====
void mqttCallback(char* topic, byte* payload, unsigned int length) {
if (strcmp(topic, MQTT_TOPIC_TASK) == 0) {
String msg = String((char*)payload).substring(0, length);
// 解析JSON格式任务: {"id":101,"shelfX":3,"shelfY":5,"dropX":0,"dropY":0,"emergency":false}
parseTask(msg, ¤tTask);
hasTask = true;
currentState = MOVING;
mqtt.publish(MQTT_TOPIC_STATUS, "状态:前往货架");
Serial.printf("收到任务 #%d\n", currentTask.id);
}
}
void loop() {
// 1. MQTT保活
mqtt.loop();
// 2. FSM任务执行
switch (currentState) {
case IDLE:
// 空闲,等待任务
break;
case MOVING: {
// 导航到货架坐标
if (navigateTo(currentTask.shelfX, currentTask.shelfY)) {
currentState = PICKING;
mqtt.publish(MQTT_TOPIC_STATUS, "状态:取货中");
}
break;
}
case PICKING: {
// 执行取货动作(机械臂/夹爪)
if (performPickup()) {
currentState = DROPPING;
mqtt.publish(MQTT_TOPIC_STATUS, "状态:前往卸货点");
}
break;
}
case DROPPING: {
if (navigateTo(currentTask.dropX, currentTask.dropY)) {
if (performDropoff()) {
currentState = COMPLETE;
// 上报任务完成
mqtt.publish(MQTT_TOPIC_STATUS, "状态:任务完成");
Serial.printf("任务 #%d 完成\n", currentTask.id);
hasTask = false;
currentState = IDLE;
}
}
break;
}
case ERROR:
// 故障处理,上报错误
mqtt.publish(MQTT_TOPIC_STATUS, "状态:故障");
break;
}
delay(50);
}
要点解读
FSM是“行为确定性”的保障,但需警惕状态爆炸:FSM将复杂拣货流程分解为离散的、有限的状态,每个状态有明确的入口和出口逻辑。当状态数量超过10个时,转移条件会变得极其复杂,建议采用分层状态机(Hierarchical FSM)——顶层管理大流程(巡航、暂停、故障),每个顶层状态内部嵌套子状态机管理细节。
任务队列结合负载均衡是实现“多机协同”的核心:智能仓储场景中多机协同依赖动态任务分配。通过无线通信交换任务数量和电量状态,采用模糊协商——距离任务点近、电量高的机器人优先承接任务。任务窃取(Work Stealing)机制可防止单点死锁。
状态指示是工程调试的“生命线”:FSM的每个状态切换本质上是离散的。绑定LED指示灯(红/黄/蓝/青/绿对应不同状态)能让调试者不依赖上位机即可快速定位机器人所处的逻辑阶段。结合串口打印状态名称,对排查“死锁”和“意外状态跳转”至关重要。
BLDC软启动与过流保护是频繁启停场景的“安全红线”:拣货机器人频繁启停,必须避免直接给最大PWM值启动,否则会导致抖动、打滑,影响定位精度。最佳实践是在状态机内部加入软启动逻辑(Ramp Up),PWM值从0逐渐增加到目标值。BLDC启动电流可达额定值的3~5倍,必须设置软件过流保护。
控制实时性保障:严禁使用delay()阻塞:控制回路必须使用硬件定时器中断或millis()非阻塞定时,确保采样周期的精确性。电机控制频率建议≥50Hz。在ESP32双核架构中,可Core0负责FSM决策和通信,Core1负责电机控制和传感器采样,实现任务隔离。

4、平库货架拣货机器人——FSM全状态覆盖+循环任务队列+声光指示
适用场景:中小型平库的多货架拣货(如电商小件仓),要求机器人自动完成待机→导航→拣货→返回→待机全流程,支持任务批量录入、队列化处理,同时通过声光实时反馈状态,便于仓库人员监控。
核心设计:
FSM状态机:定义5个核心状态,覆盖拣货全流程,状态切换基于传感器触发和任务完成条件;
循环任务队列:用数组模拟循环队列存储拣货任务,支持队列增删、出队处理;
状态指示:LED灯(红/黄/绿)指示待机、导航、拣货、故障状态,蜂鸣器提示任务完成和异常。
/* 平库拣货机器人:FSM驱动+循环任务队列+声光状态指示
核心逻辑:全流程状态覆盖(待机/导航/拣货/返回/故障)+ 循环任务队列 + 声光反馈
硬件:Arduino Mega + 2路BLDC伺服(导航/拣货)+ 激光雷达(避障)+ 货架RFID读写器 + RGB LED + 蜂鸣器
状态参数:待机超时5s,导航速度10,拣货等待2s
*/
// --- FSM状态定义 ---
typedef enum {
STATE_IDLE = 0, // 待机(等待任务/充电)
STATE_NAVIGATE, // 导航至货架
STATE_PICKING, // 执行拣货动作
STATE_RETURNING, // 返回分拣点
STATE_FAULT // 故障停机
} FSMState;
FSMState currentState = STATE_IDLE;
unsigned long stateStartTime = 0;
const unsigned long IDLE_TIMEOUT = 5000; // 待机超时时间(ms)
// --- 任务队列定义 ---
struct PickTask {
uint8_t shelfId; // 货架ID
uint8_t itemId; // 货物ID
uint8_t qty; // 拣货数量
};
#define TASK_QUEUE_SIZE 10
PickTask taskQueue[TASK_QUEUE_SIZE];
uint8_t queueHead = 0, queueTail = 0, taskCount = 0;
// --- 硬件引脚定义 ---
#define NAV_MOTOR_PWM 9 // 导航电机(水平移动)
#define PICKING_MOTOR_PWM 10 // 拣货电机(垂直抓取)
#define LASER_TRIG 11 // 激光雷达触发
#define LASER_ECHO 12 // 激光雷达回波
#define RFID_CLK 13
#define RFID_DATA 14
#define RGB_LED_R 15
#define RGB_LED_G 16
#define RGB_LED_B 17
#define BUZZER 18
// --- 任务队列操作函数 ---
bool enqueueTask(PickTask task) {
if ((queueTail + 1) % TASK_QUEUE_SIZE == queueHead) {
Serial.println("任务队列已满!");
return false; // 队列满,拒绝添加
}
taskQueue[queueTail] = task;
queueTail = (queueTail + 1) % TASK_QUEUE_SIZE;
taskCount++;
return true;
}
PickTask dequeueTask() {
if (taskCount == 0) {
Serial.println("任务队列为空!");
return {0, 0, 0}; // 空任务
}
PickTask task = taskQueue[queueHead];
queueHead = (queueHead + 1) % TASK_QUEUE_SIZE;
taskCount--;
return task;
}
bool isEmptyQueue() { return taskCount == 0; }
// --- FSM状态处理函数 ---
void handleIdleState() {
if (isEmptyQueue()) {
// 无任务,保持待机,触发超时进入充电/待机休眠(简化:循环待机)
setStatusLED(0, 1, 0); // 绿灯常亮
if (millis() - stateStartTime > IDLE_TIMEOUT) {
Serial.println("待机超时,等待新任务");
stateStartTime = millis();
}
} else {
// 有任务,切换至导航状态
currentState = STATE_NAVIGATE;
stateStartTime = millis();
Serial.println("切换至导航状态");
}
}
void handleNavigateState() {
if (isEmptyQueue()) {
currentState = STATE_IDLE;
return;
}
PickTask task = taskQueue[queueHead]; // 预览任务(不弹出)
Serial.printf("导航至货架%d,距离150cm\n", task.shelfId);
// 模拟导航移动(BLDC驱动轮移动,简化为等待导航完成)
moveToShelf(task.shelfId);
// 导航完成条件:激光雷达检测到货架(简化:延时后判断到达)
if (millis() - stateStartTime > 3000) {
currentState = STATE_PICKING;
stateStartTime = millis();
Serial.println("导航完成,切换至拣货状态");
}
}
void handlePickingState() {
if (isEmptyQueue()) {
currentState = STATE_IDLE;
return;
}
PickTask task = dequeueTask(); // 出队任务
Serial.printf("拣取货物%d,数量%d\n", task.itemId, task.qty);
// 模拟拣货动作:垂直电机下降→抓取→上升(简化为延时)
pickingAction(task.qty);
// 拣货完成条件:RFID确认货物拾取(简化:延时后判断完成)
if (millis() - stateStartTime > 2000) {
currentState = STATE_RETURNING;
stateStartTime = millis();
Serial.println("拣货完成,切换至返回状态");
}
}
void handleReturningState() {
Serial.println("返回分拣点");
// 模拟返回动作(BLDC驱动轮返回)
moveToBase();
// 返回完成条件:到达分拣点(简化:延时后判断完成)
if (millis() - stateStartTime > 3000) {
currentState = STATE_IDLE;
stateStartTime = millis();
Serial.println("返回完成,切换至待机状态");
// 任务完成提示
beep(200, 100); // 蜂鸣200ms,间隔100ms
}
}
void handleFaultState() {
setStatusLED(1, 0, 0); // 红灯常亮
beep(500, 200); // 长鸣提示故障
// 停止所有电机
analogWrite(NAV_MOTOR_PWM, 0);
analogWrite(PICKING_MOTOR_PWM, 0);
// 等待复位指令(简化:延时后切换回待机)
if (millis() - stateStartTime > 3000) {
currentState = STATE_IDLE;
stateStartTime = millis();
Serial.println("故障复位,返回待机状态");
}
}
// --- 状态指示函数 ---
void setStatusLED(int r, int g, int b) {
digitalWrite(RGB_LED_R, r);
digitalWrite(RGB_LED_G, g);
digitalWrite(RGB_LED_B, b);
}
void beep(int duration, int interval) {
digitalWrite(BUZZER, HIGH);
delay(duration);
digitalWrite(BUZZER, LOW);
delay(interval);
}
// --- 模拟电机动作函数 ---
void moveToShelf(uint8_t shelfId) {
analogWrite(NAV_MOTOR_PWM, 200); // 导航电机运转
}
void pickingAction(uint8_t qty) {
analogWrite(PICKING_MOTOR_PWM, 150); // 拣货电机动作
}
void moveToBase() {
analogWrite(NAV_MOTOR_PWM, -200); // 返回电机反转
}
// --- 故障检测函数 ---
bool checkFault() {
// 模拟故障:激光雷达检测障碍物过近
long distance = pulseIn(LASER_ECHO, HIGH) * 0.0343 / 2;
if (distance < 10) { // 障碍物距离<10cm
Serial.println("检测到障碍物,触发故障");
return true;
}
return false;
}
void setup() {
Serial.begin(115200);
// 初始化引脚
pinMode(LASER_TRIG, OUTPUT);
pinMode(LASER_ECHO, INPUT);
pinMode(RGB_LED_R, OUTPUT);
pinMode(RGB_LED_G, OUTPUT);
pinMode(RGB_LED_B, OUTPUT);
pinMode(BUZZER, OUTPUT);
pinMode(NAV_MOTOR_PWM, OUTPUT);
pinMode(PICKING_MOTOR_PWM, OUTPUT);
// 初始化状态
stateStartTime = millis();
// 添加初始任务
enqueueTask({1, 101, 3});
enqueueTask({2, 102, 5});
Serial.println("初始化完成,添加2个拣货任务");
}
void loop() {
// 故障检测(优先处理)
if (checkFault()) {
currentState = STATE_FAULT;
stateStartTime = millis();
}
// FSM状态切换
switch (currentState) {
case STATE_IDLE:
handleIdleState();
break;
case STATE_NAVIGATE:
handleNavigateState();
break;
case STATE_PICKING:
handlePickingState();
break;
case STATE_RETURNING:
handleReturningState();
break;
case STATE_FAULT:
handleFaultState();
break;
}
delay(50); // 控制循环频率
}
5、立体库高位拣货机器人——FSM状态分层+任务优先级队列+可视化指示
适用场景:立体库高位货架拣货(如自动化立体仓库),要求机器人具备提升机构控制、高度定位、任务优先级处理能力,同时通过数码管和指示灯显示当前状态、任务进度,便于中控系统监控。
核心设计:
FSM状态分层:在基础状态机中加入提升控制子状态,覆盖平层移动、垂直提升、高度定位全流程;
优先级任务队列:任务按优先级排序(高优先级优先处理),支持紧急任务插队;
可视化状态指示:数码管显示当前状态、货架层数,LED指示运行/待机/提升中状态。
/* 立体库高位拣货机器人:FSM分层+优先级任务队列+数码管指示
核心逻辑:FSM加入提升子状态 + 优先级任务队列 + 数码管状态显示
硬件:ESP32 + 3路BLDC(导航/提升/抓取)+ 高度编码器 + 数码管(7段) + 高优先级任务按钮
状态参数:提升速度5,提升高度定位精度±5cm
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <TM1637Display.h>
// --- FSM状态定义(含提升子状态) ---
typedef enum {
STATE_IDLE = 0,
STATE_NAVIGATE,
STATE_ELEVATING, // 提升机构动作
STATE_PICKING,
STATE_RETURNING,
STATE_FAULT
} FSMState;
// 提升子状态
typedef enum {
SUB_STATE_HORIZONTAL = 0, // 水平移动
SUB_STATE_VERTICAL, // 垂直提升
SUB_STATE_ALIGN // 高度对准
} SubState;
FSMState currentState = STATE_IDLE;
SubState subState = SUB_STATE_HORIZONTAL;
unsigned long stateStartTime = 0;
// --- 优先级任务队列 ---
struct PickTask {
uint8_t shelfId;
uint8_t itemId;
uint8_t qty;
uint8_t priority; // 0-3,3为最高优先级(紧急任务)
};
// 任务队列按优先级排序(高优先级在前)
PickTask taskQueue[10];
uint8_t taskCount = 0;
// --- 数码管初始化 ---
#define CLK 4
#define DIO 5
TM1637Display display(CLK, DIO);
// --- 硬件引脚定义 ---
#define NAV_MOTOR 18 // 水平导航电机
#define ELEVATOR_MOTOR 19 // 提升电机
#define GRAB_MOTOR 21 // 抓取电机
#define PRIORITY_BUTTON 22 // 紧急任务按钮
#define HEIGHT_ENCODER A0 // 高度编码器(模拟输入)
// --- 任务队列操作(优先级排序) ---
void sortTaskQueue() {
for (int i = 0; i < taskCount - 1; i++) {
for (int j = 0; j < taskCount - i - 1; j++) {
if (taskQueue[j].priority < taskQueue[j+1].priority) {
PickTask temp = taskQueue[j];
taskQueue[j] = taskQueue[j+1];
taskQueue[j+1] = temp;
}
}
}
}
bool enqueueTask(PickTask task) {
if (taskCount >= 10) {
Serial.println("任务队列已满!");
return false;
}
taskQueue[taskCount++] = task;
sortTaskQueue(); // 插入后重新排序
return true;
}
PickTask dequeueTask() {
if (taskCount == 0) return {0,0,0,0};
PickTask task = taskQueue[0];
for (int i = 0; i < taskCount - 1; i++) {
taskQueue[i] = taskQueue[i+1];
}
taskCount--;
return task;
}
bool isEmptyQueue() { return taskCount == 0; }
// --- FSM状态处理(含提升子状态) ---
void handleIdleState() {
setStatusIndicator(0); // 待机:绿灯
displayState("IDLE");
if (!isEmptyQueue() || digitalRead(PRIORITY_BUTTON) == HIGH) {
if (digitalRead(PRIORITY_BUTTON) == HIGH) {
// 紧急任务插队:添加最高优先级任务
enqueueTask({5, 205, 1, 3});
Serial.println("检测到紧急任务,已插队");
}
currentState = STATE_NAVIGATE;
subState = SUB_STATE_HORIZONTAL;
stateStartTime = millis();
displayState("NAV");
}
}
void handleNavigateState() {
setStatusIndicator(1); // 导航:黄灯
if (isEmptyQueue()) {
currentState = STATE_IDLE;
return;
}
PickTask task = taskQueue[0];
Serial.printf("水平导航至货架%d层\n", task.shelfId);
analogWrite(NAV_MOTOR, 180);
// 导航完成:到达货架底部
if (millis() - stateStartTime > 2000) {
currentState = STATE_ELEVATING;
subState = SUB_STATE_VERTICAL;
stateStartTime = millis();
displayState("ELEV");
}
}
void handleElevatingState() {
setStatusIndicator(2); // 提升中:闪烁黄灯
PickTask task = taskQueue[0];
uint16_t targetHeight = task.shelfId * 100; // 目标高度=层数×100cm
uint16_t currentHeight = analogRead(HEIGHT_ENCODER);
Serial.printf("提升至高度%dcm,当前%dcm\n", targetHeight, currentHeight);
switch (subState) {
case SUB_STATE_VERTICAL:
analogWrite(ELEVATOR_MOTOR, 150); // 启动提升
if (currentHeight >= targetHeight - 5 && currentHeight <= targetHeight + 5) {
subState = SUB_STATE_ALIGN;
analogWrite(ELEVATOR_MOTOR, 0);
Serial.println("高度对准完成");
}
break;
case SUB_STATE_ALIGN:
// 微调对准
analogWrite(ELEVATOR_MOTOR, currentHeight < targetHeight ? 50 : -50);
if (abs(currentHeight - targetHeight) < 2) {
subState = SUB_STATE_HORIZONTAL;
analogWrite(ELEVATOR_MOTOR, 0);
currentState = STATE_PICKING;
stateStartTime = millis();
displayState("PICK");
}
break;
}
}
void handlePickingState() {
setStatusIndicator(1); // 拣货:黄灯
if (isEmptyQueue()) {
currentState = STATE_IDLE;
return;
}
PickTask task = dequeueTask();
Serial.printf("高位拣取货物%d\n", task.itemId);
analogWrite(GRAB_MOTOR, 200); // 抓取动作
if (millis() - stateStartTime > 1500) {
analogWrite(GRAB_MOTOR, 0);
currentState = STATE_RETURNING;
stateStartTime = millis();
displayState("RETURN");
}
}
void handleReturningState() {
setStatusIndicator(0); // 返回:绿灯
Serial.println("返回立体库入口");
analogWrite(NAV_MOTOR, -180);
analogWrite(ELEVATOR_MOTOR, -150); // 下降至地面
if (millis() - stateStartTime > 4000) {
analogWrite(NAV_MOTOR, 0);
analogWrite(ELEVATOR_MOTOR, 0);
currentState = STATE_IDLE;
displayState("IDLE");
}
}
void handleFaultState() {
setStatusIndicator(3); // 故障:红灯
displayState("FAULT");
analogWrite(NAV_MOTOR, 0);
analogWrite(ELEVATOR_MOTOR, 0);
analogWrite(GRAB_MOTOR, 0);
if (millis() - stateStartTime > 3000) {
currentState = STATE_IDLE;
displayState("IDLE");
}
}
// --- 状态指示函数 ---
void setStatusIndicator(int mode) {
// 简化:通过ESP32的LED引脚指示(实际可扩展RGB)
switch (mode) {
case 0: digitalWrite(23, LOW); digitalWrite(24, HIGH); digitalWrite(25, LOW); break; // 绿灯
case 1: digitalWrite(23, HIGH); digitalWrite(24, LOW); digitalWrite(25, LOW); break; // 黄灯
case 2: digitalWrite(23, HIGH); digitalWrite(24, HIGH); digitalWrite(25, LOW); break; // 闪烁黄灯(简化为常亮)
case 3: digitalWrite(23, LOW); digitalWrite(24, LOW); digitalWrite(25, HIGH); break; // 红灯
}
}
void displayState(const char* stateStr) {
display.clear();
display.showNumberDec(currentState, false); // 显示状态编号
display.setBrightness(5);
}
// --- 模拟传感器与电机 ---
uint16_t getHeightSensor() {
// 模拟高度编码器:返回0-1000的数值
static uint16_t h = 0;
if (currentState == STATE_ELEVATING) h += 2;
else if (currentState == STATE_RETURNING && subState == SUB_STATE_VERTICAL) h -= 3;
return h;
}
void setup() {
Serial.begin(115200);
pinMode(NAV_MOTOR, OUTPUT);
pinMode(ELEVATOR_MOTOR, OUTPUT);
pinMode(GRAB_MOTOR, OUTPUT);
pinMode(PRIORITY_BUTTON, INPUT_PULLUP);
pinMode(23, OUTPUT); pinMode(24, OUTPUT); pinMode(25, OUTPUT);
// 初始化数码管
display.setBrightness(5);
// 添加初始任务
enqueueTask({3, 201, 2, 1});
enqueueTask({5, 202, 1, 2});
Serial.println("立体库拣货初始化完成");
}
void loop() {
// 高度传感器更新
// 故障检测(简化)
if (getHeightSensor() > 1000 || getHeightSensor() < 0) {
currentState = STATE_FAULT;
stateStartTime = millis();
}
// FSM状态处理
switch (currentState) {
case STATE_IDLE: handleIdleState(); break;
case STATE_NAVIGATE: handleNavigateState(); break;
case STATE_ELEVATING: handleElevatingState(); break;
case STATE_PICKING: handlePickingState(); break;
case STATE_RETURNING: handleReturningState(); break;
case STATE_FAULT: handleFaultState(); break;
}
delay(50);
}
6、应急越障拣货机器人——FSM中断响应+任务中断队列+多模态指示
适用场景:仓储火灾、局部障碍等应急场景下的拣货,要求机器人在执行常规任务时,能响应紧急任务中断,自主越障后恢复任务,同时通过多模态指示(声光+数码管)实时反馈状态,便于应急指挥。
核心设计:
FSM中断响应机制:支持紧急任务触发状态中断,执行越障子状态后,恢复原任务;
任务中断队列:存储被中断的常规任务,越障完成后自动恢复队列处理;
多模态状态指示:结合LED闪烁、蜂鸣器频率、数码管文字显示,区分常规、应急、越障状态。
/* 应急越障拣货机器人:FSM中断响应+任务中断队列+多模态指示
核心逻辑:紧急任务触发FSM中断→执行越障→恢复原任务 + 中断任务队列 + 声光+数码管指示
硬件:Arduino Mega + 3路BLDC(导航/越障/抓取)+ 障碍传感器 + 紧急任务红外传感器 + 多模态指示模块
状态参数:越障速度3,越障高度15cm,紧急任务优先级最高
*/
// --- FSM状态定义(含中断、越障子状态) ---
typedef enum {
STATE_IDLE = 0,
STATE_NORMAL_TASK, // 执行常规任务
STATE_INTERRUPT, // 紧急任务中断
STATE_OBSTACLE, // 越障状态
STATE_FAULT
} FSMState;
// 越障子状态
typedef enum {
SUB_OBSTACLE_DETECT = 0, // 障碍检测
SUB_OBSTACLE_CLIMB, // 越障动作
SUB_OBSTACLE_RESUME // 恢复原路径
} ObstacleSubState;
FSMState currentState = STATE_IDLE;
ObstacleSubState obsSubState = SUB_OBSTACLE_DETECT;
unsigned long stateStartTime = 0;
// --- 任务队列(常规+中断任务) ---
struct PickTask {
uint8_t shelfId;
uint8_t itemId;
uint8_t qty;
bool isEmergency; // 是否紧急任务
};
#define TASK_QUEUE_SIZE 12
PickTask taskQueue[TASK_QUEUE_SIZE];
uint8_t queueHead = 0, queueTail = 0, taskCount = 0;
PickTask interruptedTask; // 被中断的常规任务
bool hasInterruptedTask = false;
// --- 硬件引脚定义 ---
#define NAV_MOTOR 9
#define OBSTACLE_MOTOR 10 // 越障专用电机(抬升轮)
#define GRAB_MOTOR 11
#define EMERGENCY_SENSOR 12 // 紧急任务红外传感器
#define OBSTACLE_SENSOR 13 // 障碍检测传感器
#define SEGMENT_A 14, SEGMENT_B 15, SEGMENT_C 16, SEGMENT_D 17
#define SEGMENT_E 18, SEGMENT_F 19, SEGMENT_G 20, DP 21
#define BUZZER 22
#define STATUS_LED 23
// --- 任务队列操作 ---
bool enqueueTask(PickTask task) {
if ((queueTail + 1) % TASK_QUEUE_SIZE == queueHead) {
Serial.println("任务队列已满!");
return false;
}
taskQueue[queueTail] = task;
queueTail = (queueTail + 1) % TASK_QUEUE_SIZE;
taskCount++;
return true;
}
PickTask dequeueTask() {
if (taskCount == 0) return {0,0,0,false};
PickTask task = taskQueue[queueHead];
queueHead = (queueHead + 1) % TASK_QUEUE_SIZE;
taskCount--;
return task;
}
bool isEmptyQueue() { return taskCount == 0; }
// --- FSM状态处理(含中断机制) ---
void handleIdleState() {
setMultiIndicator(0, "IDLE"); // 待机:绿灯常亮,数码管显示IDLE
if (!isEmptyQueue()) {
currentState = STATE_NORMAL_TASK;
stateStartTime = millis();
}
// 检测紧急任务触发
if (digitalRead(EMERGENCY_SENSOR) == HIGH) {
// 紧急任务入队(最高优先级)
enqueueTask({9, 301, 1, true});
setMultiIndicator(3, "EMERGENCY!"); // 红灯闪烁,蜂鸣长鸣
Serial.println("紧急任务触发!");
}
}
void handleNormalTaskState() {
setMultiIndicator(1, "TASK"); // 常规任务:黄灯,数码管显示TASK
PickTask task = dequeueTask();
if (task.isEmergency) {
// 中断当前常规任务,执行紧急任务
currentState = STATE_INTERRUPT;
hasInterruptedTask = true;
interruptedTask = task; // 保存被中断的任务(实际应保存原任务,简化为记录)
stateStartTime = millis();
setMultiIndicator(2, "INTERRUPT");
return;
}
Serial.printf("执行常规任务:拣取货架%d货物\n", task.shelfId);
// 模拟常规任务动作
analogWrite(NAV_MOTOR, 150);
if (millis() - stateStartTime > 3000) {
// 任务完成,返回待机
analogWrite(NAV_MOTOR, 0);
currentState = STATE_IDLE;
}
}
void handleInterruptState() {
setMultiIndicator(2, "INTERRUPT");
// 检测障碍物
if (digitalRead(OBSTACLE_SENSOR) == HIGH) {
currentState = STATE_OBSTACLE;
obsSubState = SUB_OBSTACLE_DETECT;
stateStartTime = millis();
setMultiIndicator(4, "OBSTACLE"); // 越障:蓝灯闪烁,蜂鸣急促
return;
}
// 无障碍,直接执行紧急任务
executeEmergencyTask();
}
void handleObstacleState() {
switch (obsSubState) {
case SUB_OBSTACLE_DETECT:
Serial.println("检测障碍物,准备越障");
analogWrite(NAV_MOTOR, 0);
obsSubState = SUB_OBSTACLE_CLIMB;
stateStartTime = millis();
break;
case SUB_OBSTACLE_CLIMB:
// 执行越障:抬升轮+越障电机联动
analogWrite(OBSTACLE_MOTOR, 200); // 抬升越障轮
analogWrite(NAV_MOTOR, 100); // 低速前进越障
Serial.println("执行越障动作");
if (millis() - stateStartTime > 4000) {
obsSubState = SUB_OBSTACLE_RESUME;
analogWrite(OBSTACLE_MOTOR, 0);
stateStartTime = millis();
}
break;
case SUB_OBSTACLE_RESUME:
// 越障完成,恢复路径
analogWrite(NAV_MOTOR, 150);
Serial.println("越障完成,恢复紧急任务");
if (millis() - stateStartTime > 2000) {
obsSubState = SUB_OBSTACLE_DETECT;
currentState = STATE_INTERRUPT;
stateStartTime = millis();
setMultiIndicator(2, "INTERRUPT");
}
break;
}
}
void executeEmergencyTask() {
if (isEmptyQueue()) {
// 紧急任务完成后,恢复被中断的常规任务
if (hasInterruptedTask) {
enqueueTask(interruptedTask);
hasInterruptedTask = false;
}
currentState = STATE_IDLE;
return;
}
PickTask task = dequeueTask();
Serial.printf("执行紧急任务:拣取货架%d货物\n", task.shelfId);
analogWrite(NAV_MOTOR, 200); // 高速前往紧急任务点
if (millis() - stateStartTime > 3000) {
analogWrite(NAV_MOTOR, 0);
if (hasInterruptedTask) {
enqueueTask(interruptedTask);
hasInterruptedTask = false;
}
currentState = STATE_IDLE;
setMultiIndicator(0, "IDLE");
}
}
void handleFaultState() {
setMultiIndicator(5, "FAULT"); // 故障:红灯常亮,蜂鸣连续
analogWrite(NAV_MOTOR, 0);
analogWrite(OBSTACLE_MOTOR, 0);
if (millis() - stateStartTime > 3000) {
currentState = STATE_IDLE;
setMultiIndicator(0, "IDLE");
}
}
// --- 多模态状态指示 ---
void setMultiIndicator(int mode, const char* text) {
switch (mode) {
case 0: // 待机:绿灯常亮,蜂鸣短鸣1次
digitalWrite(STATUS_LED, LOW);
beep(100, 50);
break;
case 1: // 常规任务:黄灯常亮,蜂鸣无
digitalWrite(STATUS_LED, HIGH);
noBeep();
break;
case 2: // 中断:黄灯闪烁,蜂鸣短鸣2次
digitalWrite(STATUS_LED, !digitalRead(STATUS_LED));
beep(100, 50); beep(100, 50);
break;
case 3: // 紧急任务触发:红灯闪烁,蜂鸣长鸣
digitalWrite(STATUS_LED, HIGH);
beep(500, 0);
break;
case 4: // 越障:蓝灯闪烁(实际用RGB扩展),蜂鸣急促
for (int i = 0; i < 5; i++) { beep(100, 0); }
break;
case 5: // 故障:红灯常亮,蜂鸣连续
digitalWrite(STATUS_LED, HIGH);
for (int i = 0; i < 10; i++) { beep(200, 0); }
break;
}
// 数码管显示文字(简化:显示状态编号)
displayText(text);
}
void displayText(const char* text) {
// 简化:7段数码管显示文字首字母(实际可扩展为点阵屏)
// 此处省略具体数码管驱动,需结合硬件扩展
}
void beep(int duration, int interval) {
digitalWrite(BUZZER, HIGH);
delay(duration);
digitalWrite(BUZZER, LOW);
if (interval > 0) delay(interval);
}
void noBeep() { digitalWrite(BUZZER, LOW); }
// --- 初始化与主循环 ---
void setup() {
Serial.begin(115200);
pinMode(NAV_MOTOR, OUTPUT);
pinMode(OBSTACLE_MOTOR, OUTPUT);
pinMode(GRAB_MOTOR, OUTPUT);
pinMode(EMERGENCY_SENSOR, INPUT_PULLUP);
pinMode(OBSTACLE_SENSOR, INPUT_PULLUP);
pinMode(STATUS_LED, OUTPUT);
pinMode(BUZZER, OUTPUT);
// 初始化数码管(简化)
initSegmentDisplay();
// 添加常规任务
enqueueTask({1, 302, 2, false});
enqueueTask({2, 303, 3, false});
Serial.println("应急拣货机器人初始化完成");
}
void loop() {
// 障碍检测与紧急任务检测(实时响应)
if (digitalRead(EMERGENCY_SENSOR) == HIGH && currentState == STATE_NORMAL_TASK) {
enqueueTask({9, 301, 1, true});
setMultiIndicator(3, "EMERGENCY!");
}
if (digitalRead(OBSTACLE_SENSOR) == HIGH && (currentState == STATE_INTERRUPT || currentState == STATE_NORMAL_TASK)) {
if (currentState != STATE_OBSTACLE) {
currentState = STATE_OBSTACLE;
obsSubState = SUB_OBSTACLE_DETECT;
stateStartTime = millis();
}
}
// FSM状态处理
switch (currentState) {
case STATE_IDLE: handleIdleState(); break;
case STATE_NORMAL_TASK: handleNormalTaskState(); break;
case STATE_INTERRUPT: handleInterruptState(); break;
case STATE_OBSTACLE: handleObstacleState(); break;
case STATE_FAULT: handleFaultState(); break;
}
delay(50);
}
// 简化的数码管初始化
void initSegmentDisplay() {
pinMode(SEGMENT_A, OUTPUT);
pinMode(SEGMENT_B, OUTPUT);
pinMode(SEGMENT_C, OUTPUT);
pinMode(SEGMENT_D, OUTPUT);
pinMode(SEGMENT_E, OUTPUT);
pinMode(SEGMENT_F, OUTPUT);
pinMode(SEGMENT_G, OUTPUT);
pinMode(DP, OUTPUT);
// 初始清屏
digitalWrite(SEGMENT_A, LOW);
digitalWrite(SEGMENT_B, LOW);
// ... 其他段同理
}
要点解读
- FSM驱动:拣货机器人的“行为骨架”,实现状态可控与流程闭环
FSM(有限状态机)是智能仓储拣货机器人的核心控制逻辑,通过明确的状态划分和切换规则,实现全流程行为的精准可控,是应对复杂仓储流程的核心保障:
状态划分需覆盖全流程:从待机、导航、拣货到返回、故障,所有关键动作必须对应独立状态,确保每个流程节点有明确的行为边界,避免逻辑混乱;案例4的5状态设计覆盖平库拣货全流程,案例5通过子状态实现立体库提升的精细化控制,案例6通过中断机制实现应急场景的状态切换;
状态切换依赖明确触发条件:切换必须基于传感器信号、任务完成、超时等可量化条件,而非随机跳转,确保流程的严谨性;例如平库机器人从导航到拣货的触发条件是“到达货架位置(时间/传感器判断)”,而非固定延时;
资源管控与故障隔离:FSM需对硬件资源(电机、传感器)按状态进行管控,仅在当前状态下启用相关硬件;故障状态必须独立隔离,优先停止所有电机,保障设备安全,避免故障蔓延。 - 任务队列:拣货任务的“调度中枢”,保障多任务有序执行与优先级管理
任务队列是多任务调度的核心,解决仓储机器人面临的多订单、多优先级任务处理需求,通过队列化管理实现任务的有序调度与灵活适配:
队列结构适配场景需求:平库常规拣货采用循环队列(案例4),支持批量任务顺序处理,避免空间浪费;立体库和应急场景采用优先级队列(案例5、6),确保紧急任务、高位任务优先执行,满足业务优先级需求;
队列操作需边界安全:入队、出队必须判断队列空/满边界,避免数组越界;案例1通过队列头尾指针和任务计数实现循环队列,案例2通过排序算法实现优先级队列,核心是保障数据操作的安全性与可靠性;
任务队列与FSM深度协同:FSM的状态切换依赖队列状态(空/非空),而队列的任务流转依赖FSM的处理进度,形成“队列驱动状态、状态消费任务”的闭环,确保任务不丢失、流程不中断;例如案例1中待机状态检测队列非空即切换至导航,导航完成后消费队列中的任务。 - 状态指示:人机交互的“信息窗口”,实现状态可视化与故障快速定位
状态指示是拣货机器人的必备功能,通过直观的声光、数码管等多模态反馈,让操作人员实时掌握机器人状态,快速响应异常,是提升运维效率的核心:
指示维度贴合业务需求:不同状态需对应差异化的反馈方式,让操作人员一眼识别状态类型;案例1用绿/黄/红LED区分待机、任务、故障,案例2用数码管显示货架层数,案例3用蜂鸣频率区分应急与越障,核心是让状态信息可视化、可感知;
指示需与FSM状态严格同步:状态指示的更新必须与FSM的状态切换同步,确保指示信息的真实性,避免误导操作人员;例如故障状态时必须同步切换为红灯和长鸣蜂鸣器,且仅在故障恢复后切换回待机指示;
指示设计兼顾简洁与冗余:在复杂场景下,单一指示方式可能失效,需设计冗余指示(如声光组合、数码管+LED组合),确保在噪音、强光环境下仍能传递信息;案例3采用声光+数码管的多模态指示,兼顾不同环境的信息传递需求。 - BLDC执行机构:状态落地的“动力引擎”,匹配FSM的动态执行需求
BLDC电机作为核心执行机构,直接决定FSM状态意图的落地效果,需与FSM的动态切换深度适配,满足拣货机器人的速度、扭矩、精度需求:
控制模式适配状态动作:不同状态下BLDC电机需采用对应控制模式,实现精准动作;例如导航状态采用速度模式实现快速移动,拣货状态采用位置模式实现精准抓取,越障状态采用扭矩模式提供大驱动力,确保动作与状态匹配;
动态响应匹配状态切换节奏:FSM状态切换具有实时性要求,BLDC需具备毫秒级的动态响应速度,在状态切换时快速调整输出,避免因电机响应滞后导致流程卡顿;例如应急场景中越障电机需在状态切换后立即启动,快速完成抬升动作;
闭环控制保障动作精度:BLDC必须结合编码器实现闭环控制,精准跟踪FSM输出的目标轨迹,避免开环控制的误差累积;例如立体库提升时,编码器反馈高度位置,结合PID算法实现精准的层高定位,确保提升到位后误差在允许范围内;
安全保护适配故障状态:BLDC需具备过流、过压、堵转保护机制,当FSM切换至故障状态时,电机能立即停止并触发保护,避免硬件损坏,保障设备安全。 - 工程落地:FSM、队列、指示的协同优化,兼顾稳定性与扩展性
工程落地的核心是实现FSM、任务队列、状态指示的高效协同,在资源有限的Arduino平台上兼顾系统稳定性、可维护性与功能扩展性:
代码模块化降低耦合度:将FSM状态处理、队列操作、状态指示、BLDC控制封装为独立模块,通过接口通信,降低模块间的耦合度;例如案例中把任务队列操作独立为函数,FSM通过调用接口实现队列交互,便于后续维护和功能扩展;
资源分配适配硬件约束:Arduino平台资源有限,需合理分配引脚、算力、内存资源;例如使用ESP32或Mega等高性能平台应对多电机控制和队列运算,避免8位平台算力不足;PWM通道按电机数量合理分配,避免资源冲突;
调试机制保障问题定位:代码需内置串口调试信息,记录状态切换、任务队列变化、电机动作等关键信息,便于快速定位问题;同时设计仿真模式,通过模拟传感器信号验证FSM和队列逻辑,降低调试成本;
扩展性预留适配需求变化:架构设计需预留扩展接口,例如支持新增FSM状态、扩展任务队列容量、新增指示方式;例如案例2预留数码管扩展接口,案例3预留多模态指示接口,为后续新增仓储功能提供扩展空间。
请注意:以上案例仅作为思路拓展的参考示例,不保证完全正确、适配所有场景或可直接编译运行。由于硬件平台、实际使用场景、Arduino 版本的差异,均可能影响代码的适配性与使用方法的选择。在实际编程开发时,请务必根据自身硬件配置、使用场景及具体功能需求进行针对性调整,并通过多次实测验证效果;同时需确保硬件接线正确,充分了解所用传感器、执行器等设备的技术规范与核心特性。对于涉及硬件操作的代码,使用前务必核对引脚定义、电平参数等关键信息的准确性与安全性,避免因参数错误导致硬件损坏或运行异常。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)