3、机器人任务划分:采集、推理、控制、通信四大任务的功能边界与数据流设计

在基于 STM32 与 FreeRTOS 的机器人架构中,任务划分的合理性直接决定了系统的实时性、可维护性与可扩展性。一个典型的机器人系统通常包含传感器数据采集、环境感知与决策推理、运动执行控制、以及与上位机或云端的数据交互四大核心功能。本章将深入剖析这四大任务的功能边界,明确各自职责,并重点设计它们之间的数据流通道,确保在 FreeRTOS 多任务环境下,数据能够高效、有序、无冲突地流转。

采集任务 传感器驱动 数据预处理 推理任务 特征提取 决策输出 控制任务 运动学解算 PWM/方向输出 通信任务 协议解析 数据上报 传感器数据 Queue 控制指令 Queue 状态/日志 远程指令 执行反馈 共享资源保护 Mutex / 临界区 防止数据竞争 时间约束 采集周期 10ms 控制周期 5ms

上图清晰地展示了四大任务在 FreeRTOS 中的典型拓扑关系。采集任务负责从物理世界获取原始数据,推理任务基于这些数据做出决策,控制任务将决策转化为物理动作,而通信任务则负责与外部世界交换信息。数据流以队列(Queue)为主要载体,辅以信号量或事件标志组进行同步。下面我们将逐一剖析每个任务的功能边界与实现要点。

3.1 采集任务:数据入口的守门员

核心原理

采集任务(Sensor Task)是机器人感知层的核心。它的职责是周期性地从各类传感器(如 IMU、激光雷达、编码器、超声波等)读取原始数据,并进行必要的预处理(如滤波、去噪、单位换算),然后将干净、规范的数据打包发送给推理任务。在 FreeRTOS 架构中,采集任务通常以最高优先级运行,因为它为整个系统提供时间基准和数据基础。

功能边界与操作流程

  • 边界定义:采集任务只负责“获取”和“初步加工”,不负责“理解”数据。它不判断数据是否有效(那是推理任务的事),也不执行任何控制动作。
  • 实现步骤:
    1. 初始化传感器驱动(I2C、SPI、UART 等外设)。
    2. 创建 FreeRTOS 队列,用于存放采集到的数据。
    3. 在任务循环中,调用传感器读取函数,获取原始数据。
    4. 进行数据校验(如 CRC 校验、范围检查)和预处理(如滑动平均滤波)。
    5. 将处理后的数据通过 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任意保持当前状态
风险说明:推理任务的算法复杂度直接影响系统响应时间。如果决策算法耗时超过 100ms,建议拆分为多个子任务,或使用更高效的算法(如查表法代替实时计算)。

3.3 控制任务:精准的执行者

核心原理

控制任务(Control Task)是机器人的肌肉。它接收推理任务下发的控制指令,通过运动学解算(如差速轮逆解算)计算出电机目标转速,然后通过 PID 控制器输出 PWM 信号驱动电机。该任务对实时性要求最高,通常以 1-5ms 为周期运行。

功能边界与操作流程

  • 边界定义:控制任务只负责“执行”,不关心“为什么执行”。它不判断指令是否合理,只保证指令被精确执行。
  • 操作流程:
    1. 从控制指令队列接收目标速度/位置。
    2. 进行运动学解算,得到左右轮目标转速。
    3. 读取编码器反馈,计算当前实际转速。
    4. 运行 PID 控制器,计算 PWM 占空比。
    5. 更新定时器比较寄存器,输出 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;
复盘经验:在实际调试中,我们发现 PID 积分项容易导致超调。解决方案是加入积分限幅(Integral Clamping),当输出饱和时停止积分累加。此外,编码器读数需要经过滤波,否则微分项会产生剧烈抖动。

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低

在设计数据流时,应遵循以下原则:单向流动(避免循环依赖)、解耦(通过队列隔离任务)、流量控制(队列长度应匹配数据产生速率)。

经验总结:在 STM32F407 上运行 FreeRTOS 时,建议将采集任务优先级设为 4,控制任务设为 5,推理任务设为 3,通信任务设为 2。空闲任务优先级为 0。这样既能保证控制的实时性,又能避免采集任务饿死推理任务。

通过合理的任务划分与数据流设计,我们可以构建一个高内聚、低耦合的机器人软件架构。每个任务专注于自己的职责,通过标准化的接口进行通信,使得系统易于调试、测试和扩展。在实际项目中,这种架构能够显著提高开发效率,并保证系统的长期稳定运行。