3、机器人任务划分:采集、推理、控制、通信四大任务的功能边界与数据流设计
在基于 STM32 与 FreeRTOS 的机器人架构中,任务划分的合理性直接决定了系统的实时性、可维护性与可扩展性。一个典型的机器人系统通常包含传感器数据采集、环境感知与决策推理、运动执行控制、以及与上位机或云端的数据交互四大核心功能。本章将深入剖析这四大任务的功能边界,明确各自职责,并重点设计它们之间的数据流通道,确保在 FreeRTOS 多任务环境下,数据能够高效、有序、无冲突地流转。
上图清晰地展示了四大任务在 FreeRTOS 中的典型拓扑关系。采集任务负责从物理世界获取原始数据,推理任务基于这些数据做出决策,控制任务将决策转化为物理动作,而通信任务则负责与外部世界交换信息。数据流以队列(Queue)为主要载体,辅以信号量或事件标志组进行同步。下面我们将逐一剖析每个任务的功能边界与实现要点。
3.1 采集任务:数据入口的守门员
核心原理
采集任务(Sensor Task)是机器人感知层的核心。它的职责是周期性地从各类传感器(如 IMU、激光雷达、编码器、超声波等)读取原始数据,并进行必要的预处理(如滤波、去噪、单位换算),然后将干净、规范的数据打包发送给推理任务。在 FreeRTOS 架构中,采集任务通常以最高优先级运行,因为它为整个系统提供时间基准和数据基础。
功能边界与操作流程
- 边界定义:采集任务只负责“获取”和“初步加工”,不负责“理解”数据。它不判断数据是否有效(那是推理任务的事),也不执行任何控制动作。
- 实现步骤:
- 初始化传感器驱动(I2C、SPI、UART 等外设)。
- 创建 FreeRTOS 队列,用于存放采集到的数据。
- 在任务循环中,调用传感器读取函数,获取原始数据。
- 进行数据校验(如 CRC 校验、范围检查)和预处理(如滑动平均滤波)。
- 将处理后的数据通过
xQueueSend()发送到队列。
案例拆解:IMU 数据采集
假设我们使用 MPU6050 六轴 IMU,采样频率为 100Hz(周期 10ms)。采集任务的关键参数如下:
// 队列定义
QueueHandle_t xImuQueue;
#define IMU_QUEUE_LENGTH 10
#define IMU_DATA_SIZE sizeof(ImuData_t)
typedef struct {
float ax, ay, az; // 加速度
float gx, gy, gz; // 角速度
uint32_t timestamp;
} ImuData_t;
// 采集任务实现
void vImuTask(void *pvParameters) {
ImuData_t imuData;
TickType_t xLastWakeTime = xTaskGetTickCount();
while(1) {
// 读取原始数据
mpu6050_read_raw(&imuData);
// 预处理:低通滤波
imuData.ax = low_pass_filter(imuData.ax, 0.8);
// 发送到队列
xQueueSend(xImuQueue, &imuData, 0);
// 精确延时 10ms
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
}
}
vTaskDelay),而应使用 vTaskDelayUntil 实现固定频率采样。同时,队列长度应足够容纳最坏情况下的数据积压,防止数据丢失。
常见误区
- 误区一:在采集任务中做复杂的数学运算(如卡尔曼滤波),这会阻塞任务,导致采样周期抖动。
- 误区二:多个传感器共用一个队列,导致数据无法区分来源。建议每个传感器独立队列,或使用结构体包含传感器 ID。
3.2 推理任务:机器人的大脑
核心原理
推理任务(Decision Task)是机器人的决策中心。它从采集队列中获取传感器数据,运行算法(如路径规划、避障策略、状态机、甚至轻量级神经网络)来产生控制指令。该任务对实时性要求较高,但允许一定的延迟(通常 20-50ms),因为它需要等待完整的数据帧。
功能边界与判断方法
- 边界定义:推理任务只做“思考”,不直接操作硬件。它输出的是抽象的控制指令(如目标速度、转向角度),而不是具体的 PWM 值。
- 判断方法:当系统需要增加新功能时,判断该功能属于“感知增强”还是“决策优化”。如果是前者,应修改采集任务;如果是后者,应修改推理任务。
数据流设计
推理任务通常订阅多个队列(如 IMU 队列、激光雷达队列),并使用事件标志组来同步多源数据。例如,只有当 IMU 和激光雷达数据都到达时,才执行融合算法。
// 等待多源数据
EventBits_t bits = xEventGroupWaitBits(
xDataEventGroup,
IMU_READY | LIDAR_READY,
pdTRUE, pdTRUE,
pdMS_TO_TICKS(50)
);
if ((bits & IMU_READY) && (bits & LIDAR_READY)) {
// 执行融合推理
vFusionAndDecision();
}
案例拆解:避障决策
假设机器人前方 30cm 处检测到障碍物,推理任务需要决定是停止、转向还是减速。关键输入数据包括:超声波距离(cm)、当前速度(m/s)、目标方向(角度)。决策逻辑如下:
| 距离 (cm) | 当前速度 (m/s) | 决策输出 |
|---|---|---|
| < 10 | 任意 | 紧急停止 |
| 10 - 20 | > 0.5 | 减速至 0.2m/s |
| 20 - 30 | > 0.8 | 转向 30° |
| > 30 | 任意 | 保持当前状态 |
3.3 控制任务:精准的执行者
核心原理
控制任务(Control Task)是机器人的肌肉。它接收推理任务下发的控制指令,通过运动学解算(如差速轮逆解算)计算出电机目标转速,然后通过 PID 控制器输出 PWM 信号驱动电机。该任务对实时性要求最高,通常以 1-5ms 为周期运行。
功能边界与操作流程
- 边界定义:控制任务只负责“执行”,不关心“为什么执行”。它不判断指令是否合理,只保证指令被精确执行。
- 操作流程:
- 从控制指令队列接收目标速度/位置。
- 进行运动学解算,得到左右轮目标转速。
- 读取编码器反馈,计算当前实际转速。
- 运行 PID 控制器,计算 PWM 占空比。
- 更新定时器比较寄存器,输出 PWM。
案例拆解:差速轮 PID 控制
关键参数:控制周期 5ms,PID 参数 Kp=1.2,Ki=0.05,Kd=0.1。目标速度 0.5m/s,轮距 0.2m,轮径 0.065m。
// PID 计算
float v_error = target_speed - current_speed;
integral += v_error * dt;
derivative = (v_error - prev_error) / dt;
float output = Kp * v_error + Ki * integral + Kd * derivative;
// 限幅
if (output > PWM_MAX) output = PWM_MAX;
if (output < PWM_MIN) output = PWM_MIN;
// 更新 PWM
TIM3->CCR1 = (uint32_t)output;
3.4 通信任务:对外交互的窗口
核心原理
通信任务(Communication Task)负责与上位机、手机 App 或云端服务器进行数据交互。它通常使用 UART、Wi-Fi 或蓝牙模块,遵循自定义协议或标准协议(如 MAVLink、ROS Serial)。该任务优先级最低,因为它允许较大的延迟(100ms 级别)。
功能边界与数据流设计
- 边界定义:通信任务只负责“传输”,不负责“解释”数据。它接收推理任务的状态信息,打包成协议帧发送出去;同时接收远程指令,解析后转发给推理任务。
- 数据流设计:
- 上行数据:推理任务 → 状态队列 → 通信任务 → 串口发送。
- 下行数据:串口接收 → 通信任务解析 → 指令队列 → 推理任务。
案例拆解:自定义协议帧设计
协议帧格式:帧头(0xAA 0x55)+ 数据长度(1字节)+ 数据类型(1字节)+ 数据体(N字节)+ CRC16 校验(2字节)。
// 发送状态帧
uint8_t tx_buffer[32];
tx_buffer[0] = 0xAA;
tx_buffer[1] = 0x55;
tx_buffer[2] = 10; // 数据长度
tx_buffer[3] = 0x01; // 类型:状态
memcpy(&tx_buffer[4], &robot_state, sizeof(robot_state));
uint16_t crc = crc16(tx_buffer, 4 + 10);
tx_buffer[14] = crc >> 8;
tx_buffer[15] = crc & 0xFF;
HAL_UART_Transmit(&huart1, tx_buffer, 16, 100);
HAL_UART_Transmit 的阻塞模式),否则会阻塞任务调度。应使用 DMA 或中断方式,配合 FreeRTOS 的信号量进行同步。
3.5 任务间数据流设计总结
四大任务之间的数据流设计是 FreeRTOS 架构的核心。下表总结了各任务间的数据交互方式与推荐机制:
| 数据流方向 | 数据内容 | 推荐机制 | 优先级 |
|---|---|---|---|
| 采集 → 推理 | 传感器原始/预处理数据 | 队列(Queue) | 高 |
| 推理 → 控制 | 目标速度/位置指令 | 队列(Queue) | 最高 |
| 推理 → 通信 | 状态信息、日志 | 队列 + 流缓冲区 | 低 |
| 通信 → 推理 | 远程控制指令 | 队列 + 事件标志组 | 中 |
| 控制 → 通信 | 执行反馈、故障信息 | 直接写入共享内存 + Mutex | 低 |
在设计数据流时,应遵循以下原则:单向流动(避免循环依赖)、解耦(通过队列隔离任务)、流量控制(队列长度应匹配数据产生速率)。
通过合理的任务划分与数据流设计,我们可以构建一个高内聚、低耦合的机器人软件架构。每个任务专注于自己的职责,通过标准化的接口进行通信,使得系统易于调试、测试和扩展。在实际项目中,这种架构能够显著提高开发效率,并保证系统的长期稳定运行。