《架构特别篇四:CONTROL 层》
架构特别篇四CONTROL 层 — 20 个任务的编排艺术这是架构中最大的一层——20 个 FreeRTOS 任务、7 个控制模式、50 多个模块文件。怎么组织不混乱答案任务按离推进器距离分配优先级数据按谁写谁读设计单向流。1. CONTROL 层的内部结构CONTROL/ ├── motion/ ← 运动控制 (最大的子模块) │ ├── perception/ ← 感知: 代价地图 传感器融合 │ └── planning/ ← 规划: D* Lite DWA path_planner ├── sensor/ ← 传感器采集任务 (imu/rtk/radar/lidar/rc) ├── communication/ ← 通信 (lwIP/MQTT/主机协议) ├── power/ ← 电源管理 ├── security/ ← 安全监控 ├── storage/ ← 参数持久化 ├── thruster/ ← 推进器控制 ├── modbus/ ← Modbus 主站 ├── apm/ ← PX4 桥接 └── system/ ← 状态上报按功能分子目录不是按层次分。每个子目录是独立的功能域——运动控制、传感器、通信、电源。它们之间通过全局数据结构体交换信息不直接相互调用。2. 优先级设计离推进器越近优先级越高优先级 7: IWDG Monitor ← 看门狗, 系统最后防线 优先级 6: Motion Control ← 50Hz 控制回路, 离推进器最近 优先级 5: Thruster/PWM ← 推进器输出 优先级 4: IMU/RTK/Radar/LiDAR/RC ← 传感器采集, 实时但可容一两帧延迟 优先级 3: Power/Host/IoT ← 低频任务, 不紧急 优先级 2: Ship State ← 遥测打包, 最低优先级设计原则如果 CPU 满载高优先级任务总能抢占。控制回路绝对不能延迟延迟 20ms → 船偏航。遥测延迟 1 秒 → 地面站卡一下没关系。3. 任务间的三种通信模式模式 1全局结构体生产者-消费者最常用。一个任务写一个或多个任务读。// radar_task.c (生产者, 33Hz)g_radar_data.min_distance2.5f;g_radar_data.data_validtrue;g_radar_data.last_update_msnow;// motion_control.c (消费者, 50Hz)if(g_radar_data.data_valid(now-g_radar_data.last_update_ms200)){avoid_update(g_radar_data.min_distance,...);}适用数据单向流动写入者单一读取者不需要确认。模式 2函数调用同步请求-响应// lidar_task.c → path_plannerpath_planner_update_sensors(g_path_planner,NULL,s_lidar_ranges,count,angle_min,angle_inc);适用调用者需要立即得到结果代价地图更新是同步的不能异步排队。模式 3系统健康快照多消费者单源// navigation_update (10Hz) → 更新系统健康快照g_system_health.gps_ok...;g_system_health.battery_voltage...;// IOT 遥测 (1Hz, 只读)send_telemetry(g_system_health);// 日志 (事件驱动, 只读)log_system_status(g_system_health);适用多个消费者需要同一份数据且数据更新频率远低于消费频率。下面是三种通信模式的对比图示模式3系统健康快照多消费者单源写 g_system_health只读只读生产者任务(navigation_update, 10Hz)全局结构体g_system_health消费者IOT 遥测(1Hz)消费者日志(事件驱动)模式2函数调用同步请求-响应直接调用并传参立即返回结果生产者任务(lidar_task)消费者函数(path_planner_update_sensors)模式1全局结构体生产者-消费者写 g_radar_data读生产者任务(radar_task, 33Hz)全局结构体g_radar_data消费者任务(motion_control, 50Hz)4. motion/ 子目录 — 核心中的核心motion/是 CONTROL 层最大的子模块它内部又分三层motion/ ├── perception/ ← 看到什么 — 传感器融合 代价地图 │ ├── costmap ← 栅格地图 │ └── sensor_fusion ← 雷达LiDAR 融合 ├── planning/ ← 怎么走 — 规划器 │ ├── d_star_lite ← 全局规划 (1Hz) │ ├── dwa_planner ← 局部规划 (10Hz) │ └── path_planner ← 规划总成 ├── pid_controller ← 怎么执行 — 控制执行 ├── auto_tune ← PID 自动调参 ├── cruise_controller ← L1 航点导航 ├── nav_ekf ← EKF 导航融合 ├── motion_control ← 主控循环 (50Hz) ├── obstacle_avoidance← VFH 紧急避障 └── rc_mixer ← 差分混控三层的数据流perception (传感器 → 代价地图) ↓ planning (代价地图 → 速度/航向指令) ↓ motion_control (速度/航向 → PID → 推进器)每一层只依赖其下层。规划器不知道传感器是 MR72 还是 M10——它只知道代价地图。控制器不知道规划器是 DWA 还是手动输入——它只知道速度/航向指令。5. 模式切换 vs 传感器降级CONTROL 层最复杂的两个状态机都在motion_control.c里模式切换操作员主动/任务触发switch(g_motion_ctrl.mode){caseMOTION_MODE_MANUAL:rc_mixer_update();// RC 直通break;caseMOTION_MODE_POS_HOLD:position_hold_update();// 定点保持 PIDbreak;caseMOTION_MODE_AUTO:cruise_update();// 航点导航path_planner_step();// DWA D* Litebreak;// INS, GPS_ONLY, GEOFENCE, EMERGENCY...}传感器降级系统自动触发GPS 正常 IMU 正常 → 全功能 AUTO GPS 丢失 IMU 正常 → 切入 INS (EKF 纯预测, 10 秒内恢复) GPS 正常 IMU 故障 → 切入 GPS_ONLY (原始 GPS, 精度下降) GPS 丢失 IMU 故障 → EMERGENCY 急停两个状态机的交互降级只改变数据源EKF/原始 GPS/无不改变控制模式。INS 模式下船还在 AUTO只是使用纯 IMU 预测的位置而不是 GPS 修正后的位置。6. 全局聚合头文件 control_list.h// control_list.h — 一次 include 引入 CONTROL 层所有模块// 比 devices_list.h 更大, 因为 CONTROL 层模块最多#includebsp.h// BSP SYSTEM DEVICES 全部#includecontrol_def.h// 通用数据结构定义#includecommunication/net_init_task.h#includecommunication/iot_task.h// ...#includesensor/imu_task.h#includesensor/rtk_task.h#includesensor/radar_task.h#includesensor/lidar_task.h// 我们新加的#includemotion/motion_control.h// 核心#includemotion/perception/costmap.h#includemotion/planning/path_planner.h// ... 30 includes依赖链回顾sys_config.h → devices_list.h → control_list.h → zscode_app.h (SYSTEM) (DEVICES) (CONTROL) (APP)为什么这个链条重要新人 clone 项目后从main.c开始跟着#include链往下走——SYSTEM → DEVICES → CONTROL → 所有模块一览无余。不需要猜哪些文件参与了编译。7. 加一个 CONTROL 层模块的标准流程以我们加的 LiDAR 功能为例① 在 sensor/ 下创建 lidar_task.h/.c → 文件头注释标注 layer CONTROL ② 在 control_list.h 加一行 → #include sensor/lidar_task.h ③ 在 zscode_app.c 加 lidar_task_init() → 决定任务启动时机 ④ 如果模块输出全局数据 → 结构体定义在 lidar_task.h → 外部声明 extern ⑤ .sct 如果用 DTCM → 变量加 SECTION_DTCM 属性 ⑥ Keil 工程加 .c 文件到项目 → .uvprojx .uvoptx .BAT .lnp现有模块零改动。这就是分层架构的兑现——新功能扩展不影响现有功能。本文是《从零搭建无人船控制系统》架构特别篇。项目地址[煜坤 YuKun]开源准备中。