🌊「芯火·破浪」STM32嵌入式AI开发训练营(L2)
L2 在 不增加任何外围硬件 的前提下,从软件架构、通信协议、数据处理、上位机四个维度全面升级。以下是具体变化对比:
| 维度 | L1「芯火·启航」 | L2「芯火·破浪」 | 提升价值 |
|---|---|---|---|
| 软件架构 | 裸机 Super Loop (while(1) 大循环) |
FreeRTOS 多任务 (6任务 + 4任务 双节点) |
从"能跑通"到"会设计"——掌握任务优先级、消息队列、信号量、互斥量,这正是企业嵌入式岗位的核心技能 |
| 节点间通信 | HC-05 蓝牙透传 (发什么收什么) |
Modbus RTU 工业总线 (寄存器映射 + CRC校验 + 主从轮询) |
理解工业现场通信标准——Modbus 是全球PLC/传感器/执行器的事实标准,学会后可直接对接工业设备 |
| 上报协议 | ESP8266 AT 透传 (raw TCP 发字符串) |
MQTT 协议 (Topic 发布/订阅 + JSON 体 + QoS) |
接入 IoT 云平台的标准方式——学会 MQTT 即可对接阿里云/华为云/OneNET/自建EMQX等主流 IoT 平台 |
| 数据处理 | 原始传感器数值 (直接读取→直接显示) |
CMSIS-DSP 数字信号处理 (FIR/IIR滤波 + 卡尔曼 + 互补滤波 + FFT + PID) |
从"采数据"到"用数据"——掌握信号处理算法,这是竞赛中区分三等奖和一等奖的分水岭 |
| 控制能力 | 开环控制 (手动PWM调节) |
PID 闭环控制 (温度/光照自动调节 + 参数调优) |
控制理论落地实践——PID是90%工业控制场景的核心算法,参数调优能力是电赛控制类题目的必备技能 |
| PC 上位机 | matplotlib 脚本 (简单曲线刷新) |
PyQt5 工业监控面板 (仪表盘 + 实时曲线 + 3D姿态球 + FFT频谱瀑布图 + 控制面板) |
从"看数据"到"控制数据"——PyQt5 是 Python 生态最成熟的桌面GUI框架,可复用到毕业设计、竞赛上位机和实际工业项目 |
| 可靠性 | 基础 IWDG 看门狗 | 多级异常处理体系 (任务级看门狗 + HardFault 捕获 + 通信超时重连 + 栈溢出检测 + 传感器热插拔恢复) |
从"实验室 demo"到"准产品级"——培养工程化思维,理解嵌入式系统的鲁棒性设计 |
| 综合复杂度 | ★★☆☆☆ | ★★★★★ | L2 结业后具备独立承担电赛/蓝桥杯/物联网大赛等高水平竞赛嵌入式部分的能力 |
🧵 掌握实时操作系统(RTOS)
将 L1 裸机代码全部重构为 FreeRTOS 多任务架构,学会任务优先级设计、消息队列通信、信号量同步、互斥量保护共享资源、软件定时器驱动。FreeRTOS 是 STM32 生态最主流的 RTOS,学会后可直接复用至 Infineon/NXP/ESP32 等平台,是嵌入式工程师简历上含金量最高的关键词之一。
🔌 精通工业通信协议
实现 Modbus RTU 主机/从机协议栈和 MQTT 发布/订阅模型,理解工业现场总线和 IoT 云平台的标准通信范式。学会设计带有 CRC 校验、超时重传、地址寻址的健壮通信协议——这项能力在电赛通信类题目、物联网大赛和工业自动化岗位中直接可用。
📊 数字信号处理落地
利用 F407 的 FPU + CMSIS-DSP 库实现 FIR/IIR 数字滤波、卡尔曼滤波、互补滤波、1024 点 FFT 频谱分析、PID 闭环控制。将抽象的数学公式变成嵌入式 C 代码,体验算法在单片机上运行的实时效果。DSP 能力是电赛信号处理类题目的核心竞争力。
🎨 构建专业级上位机
用 Claude Code + PyQt5 搭建工业级监控面板:仪表盘控件、多通道实时曲线、OpenGL 3D 姿态球、频谱瀑布图、PID 调参界面。整个 GUI 由 AI 辅助生成代码,学生聚焦于系统集成和功能验证,体验 "AI 时代嵌入式全栈开发" 的高效工作流。
| 阶段 | 天数 | 核心任务 | 关键产出 |
|---|---|---|---|
| 阶段1:FreeRTOS 内核 | Day 1—6 | 多任务消息队列信号量互斥量软件定时器中断管理 | L1 裸机代码全部改造为 FreeRTOS 多任务架构 |
| 阶段2:工业通信协议 | Day 7—12 | Modbus RTUMQTTCRC校验JSON协议设计 | Modbus 寄存器映射表 + MQTT Topic 设计文档 |
| 阶段3:DSP 信号处理 | Day 13—18 | CMSIS-DSPFIR/IIR卡尔曼滤波FFTPID互补滤波 | 每个算法独立 Demo + 可视化对比 |
| 阶段4:系统重构 | Day 19—24 | 全栈整合PyQt5面板任务联调异常处理 | 全链路跑通:采集节点→Modbus→网关→MQTT→PC面板 |
| 阶段5:性能优化+展示 | Day 25—30 | 性能测试量化指标技术答辩 | 量化指标报告 + 展示PPT + 技术文档 |
① L1 的 HC-05 蓝牙透传 → L2 升级为 Modbus RTU 工业总线(直接用 UART TX/RX 交叉直连两块 F407,不新增硬件)
② L1 的 ESP8266 AT 透传 → L2 升级为 MQTT 协议(Topic 发布/订阅,支持双向通信——PC 可下发控制指令)
③ L1 的裸机大循环 → L2 全部改造为 FreeRTOS 多任务(消息队列+信号量+互斥量+软件定时器)
④ L1 只读原始传感器值 → L2 加入 卡尔曼滤波、互补滤波、FFT、PID 一整条 DSP 处理链
⑤ L1 的 matplotlib 脚本 → L2 升级为 PyQt5 工业监控面板(仪表盘+实时曲线+3D球+频谱图+控制面板)
汇总节点(边缘网关)— 7 个任务
| 任务名 | 优先级 | 栈大小 | 触发方式 | 功能 | 从 L1 迁移来源 |
|---|---|---|---|---|---|
| Task_Watchdog | 最高(5) | 256B | 1s 定时 | IWDG 喂狗 + 各任务心跳计数 + HardFault 异常捕获与日志 | L1 基础 IWDG → 升级为多任务监控 |
| Task_SensorPoll | 高(4) | 1024B | 20ms 定时 | Modbus RTU 主机:轮询采集节点寄存器,CRC16 校验,超时重试 | L1 蓝牙接收逻辑 → 改为 Modbus 查询 |
| Task_PIDCtrl | 高(4) | 1024B | 消息队列触发 | PID 计算(温度/光照)→ PWM 输出(SG90 舵机角度 + L298N 风扇转速) | 新增 |
| Task_DataFusion | 中(3) | 2048B | 消息队列触发 | 卡尔曼滤波(温度) + 互补滤波(MPU6050 欧拉角) + 数据融合打包 | 新增 |
| Task_FFT | 中(3) | 4096B | DMA 完成信号量 | ADC DMA 双缓冲 1024 点采集 → CMSIS-DSP arm_rfft_f32 → 提取主频 & 谐波 | 新增 |
| Task_WiFiReport | 中(2) | 2048B | 100ms 定时 | MQTT 协议打包(JSON)→ ESP8266 AT 命令发送 → 接收 PC 下发指令 | L1 WiFi 透传 → 升级为 MQTT |
| Task_OLEDMenu | 低(1) | 1024B | 200ms 定时 | OLED 多页面显示(主页面 / 传感器详情 / 通信状态 / PID参数) + 按键切换 | L1 OLED 显示逻辑 |
采集节点 — 4 个任务
| 任务名 | 优先级 | 栈大小 | 触发方式 | 功能 |
|---|---|---|---|---|
| Task_SensorAcq | 高(4) | 2048B | 50ms 定时 | 5 传感器轮询:BH1750(I²C) + DS18B20(OneWire) + 光敏(ADC) + MPU6050(I²C) + HC-SR04(GPIO中断+定时器) |
| Task_ADCDMA | 高(4) | 1024B | DMA 完成信号量 | ADC DMA 双缓冲连续采样(光敏电阻振动信号),供 FFT 分析 |
| Task_ModbusSlave | 中(3) | 1024B | UART 中断触发 | Modbus RTU 从机:解析主机命令 → 填充寄存器 → CRC16 应答 |
| Task_Alert | 低(1) | 512B | 消息队列触发 | 本地告警:温度超阈值→蜂鸣器响,传感器故障→红灯闪烁,通信超时→蓝灯闪烁 |
任务间通信机制
| 通信对象 | 类型 | 流向 | 数据内容 |
|---|---|---|---|
| Queue_SensorData | 消息队列 | SensorPoll → DataFusion → PIDCtrl | 传感器原始值结构体(温度/光照/距离/姿态/ADC值) |
| Queue_FFTResult | 消息队列 | FFT → WiFiReport | 频谱峰值频率 + 幅值 + 时间戳 |
| Sem_DMADone | 二值信号量 | ADC DMA ISR → Task_FFT | DMA 传输完成信号 |
| Mutex_I2C | 互斥量 | BH1750/MPU6050/OLED 共享 | 保护 I²C1 总线互斥访问 |
| Timer_ModbusPoll | 软件定时器 | 系统 → Task_SensorPoll | 20ms 周期 Modbus 轮询 |
| Timer_WiFiReport | 软件定时器 | 系统 → Task_WiFiReport | 100ms 周期 MQTT 上报 |
| Queue_Alert | 消息队列 | 各任务 → Task_Alert | 告警类型 + 告警等级 + 传感器ID |
策略:不重新设计新任务,而是将 L1 裸机代码逐步重构进 FreeRTOS 框架。学生已熟悉 L1 的传感器驱动,精力集中在任务设计、同步和通信上。
FreeRTOS 入门 + 第一个多任务程序
- FreeRTOS 概念:任务、调度器、优先级、时间片
- PlatformIO 集成 FreeRTOS:在 platformio.ini 中启用 FreeRTOS 中间件
- 创建两个简单任务:TaskA 闪红灯(1Hz)、TaskB 闪蓝灯(3Hz)
- 观察两个任务并发运行效果,与裸机顺序执行对比
- 用 Claude Code 交互学习 FreeRTOS API:
xTaskCreate/vTaskDelay/vTaskStartScheduler
任务优先级 + 抢占调度实验
- 理解 FreeRTOS 抢占式调度:高优先级任务就绪立即抢占低优先级
- 实验:创建3个不同优先级的任务,串口打印切换顺序
- 任务状态:Running / Ready / Blocked / Suspended
- 实验:用
vTaskSuspend/vTaskResume手动控制任务启停 - 迁移 L1 代码:将 LED 流水灯逻辑从裸机 main() 抽离为独立 Task_LED
消息队列(Queue)
- 消息队列原理:生产者→队列→消费者,FIFO 缓冲
- 实验:Task_SensorSim 模拟产生温度数据 → 队列 → Task_Display 串口打印
- 队列满/空的阻塞处理:
xQueueSend和xQueueReceive的超时参数 - 迁移 L1 代码:将 DS18B20 温度读取和 OLED 显示拆分为两个任务,通过队列传递温度值
- 对比:裸机时代"读完直接显示" vs RTOS 时代"读到→丢队列→显示任务自己去取"
信号量(Semaphore)+ 互斥量(Mutex)
- 二值信号量:中断 → 任务的同步机制(如 DMA 完成中断释放信号量,任务获取后处理数据)
- 计数信号量:管理多个相同资源(如多个 DMA 缓冲区)
- 互斥量:保护共享资源——I²C1 总线被 BH1750/MPU6050/OLED 三个任务共享
- 优先级反转问题 + 互斥量的优先级继承机制
- 实验:两个任务同时通过同一个 I²C 总线读写不同传感器,不加/加互斥量对比
软件定时器 + 中断管理
- 软件定时器 vs 硬件定时器:FreeRTOS 软件定时器基于 SysTick,精度为 tick 周期(通常1ms)
- 实验:用软件定时器实现周期性传感器轮询(替代 L1 的 HAL_TIM 定时中断)
- FreeRTOS 中断管理:
FromISR系列 API(xQueueSendFromISR等) - 中断优先级:FreeRTOS 可管理的中断优先级范围(≤ configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)
- 迁移 L1 代码:将 L1 的 UART 接收中断改为 FreeRTOS 中断安全版本
FreeRTOS 综合实战 + 任务栈分析
- 将 L1 采集节点 5 传感器驱动全部迁入 FreeRTOS 任务框架
- 使用
uxTaskGetStackHighWaterMark检查每个任务的剩余栈空间 - 使用
vTaskList打印任务运行状态表(名称/状态/优先级/剩余栈/任务号) - 实验:人为制造栈溢出(分配过小栈空间),观察 FreeRTOS 栈溢出 Hook 回调
- 阶段1 验收标准:采集节点 4 任务 + 汇总节点 6 任务框架搭建完毕,串口打印任务运行状态正常
此阶段将 L1 的蓝牙透传和 WiFi AT 透传升级为工业标准协议栈。所有通信走 UART 直连(不新增 RS-485/CAN 硬件),但协议层面完全模拟工业总线行为。
Modbus RTU 协议原理 + 帧结构实现
- Modbus 协议体系:RTU / ASCII / TCP 三种变体,本课程学 RTU
- 帧结构:地址码(1B) + 功能码(1B) + 数据区(N B) + CRC16(2B)
- 功能码:03(读保持寄存器)、06(写单个寄存器)、10(写多个寄存器)
- CRC16 查表法实现:用 Claude Code 生成 CRC16-Modbus 校验函数
- 实验:PC 串口助手发 Modbus RTU 帧 → STM32 解析 → LED 响应
Modbus 从机实现(采集节点)
- 在采集节点 FreeRTOS 框架中实现 Task_ModbusSlave
- 传感器数据寄存器映射:温度(0x0000)、光照(0x0002)、ADC电压(0x0004)、加速度XYZ(0x0006-0x000B)、欧拉角(0x000C-0x0011)、超声波距离(0x0012)、心跳(0x0014)
- 错误处理:非法功能码(0x01)、非法数据地址(0x02)异常码响应
- UART 空闲中断 + 定时器实现帧间隔检测(Modbus RTU 要求 ≥3.5字符间隔)
- 验证:汇总节点通过 UART 发 Modbus 查询 → 采集节点回寄存器数据 → 串口打印验证
MQTT 协议原理 + ESP8266 AT 固件升级
- MQTT 协议核心概念:Broker(服务器)、Topic(主题)、Publish/Subscribe(发布/订阅)、QoS(0/1/2)
- 免费 MQTT Broker:EMQX Public Broker(broker.emqx.io:1883)或本地 Mosquitto
- ESP8266 AT 固件确认:检查 AT 版本 ≥1.7(支持 MQTT AT 指令)
- MQTT AT 指令:
AT+MQTTUSERCFG/AT+MQTTCONN/AT+MQTTPUB/AT+MQTTSUB - 实验:STM32 发 AT 命令让 ESP8266 连接 WiFi + 连接 MQTT Broker + 发布一条消息
MQTT 数据上报 + PC 端接收
- 汇总节点 Task_WiFiReport:传感器数据 → JSON 序列化 → MQTT Publish
- Topic 设计:
sensor/temp/sensor/light/sensor/imu/sensor/distance/sensor/status - PC 端用 Python paho-mqtt 库订阅所有 Topic,验证数据接收
- 下行控制:
cmd/pid(PID 设定值)/cmd/led(远程控制LED) - 实验:PC 通过 MQTT 发布
cmd/led→ 汇总节点接收 → LED 亮灭响应
自定义应用层协议设计
- 学会从零设计一个健壮的二进制帧协议:
- 帧头(2B: 0xA5 0x5A) + 数据长度(2B) + 帧类型(1B) + 源地址(1B) + 目标地址(1B) + 序列号(2B) + 数据载荷(N B) + CRC16(2B) + 帧尾(1B: 0x0D)
- 帧类型定义:0x01=传感器数据 / 0x02=控制指令 / 0x03=心跳 / 0x04=ACK应答 / 0x05=NACK否定应答
- 状态机实现:WAIT_HEADER1 → WAIT_HEADER2 → WAIT_LENGTH → WAIT_DATA → CHECK_CRC → WAIT_TAIL
- 对比:Modbus RTU vs MQTT vs 自定义协议,各适合什么场景
协议联调 + 通信性能测试
- 全协议栈联调:采集节点(Modbus从机) → 汇总节点(Modbus主机+MQTT客户端) → PC(MQTT订阅)
- 测试 Modbus 轮询周期稳定性(目标 20ms ±2ms)
- 测试 MQTT 端到端延迟(传感器读数变化 → PC 收到主题消息)
- 通信异常测试:UART 断开重连、WiFi 断网重连、MQTT Broker 掉线重连
- 阶段2验收标准:Modbus + MQTT 双协议全链路稳定运行 ≥ 30分钟无丢帧
📋 Modbus RTU 寄存器映射表(采集节点从机,地址=0x01)
| 寄存器地址 | 内容 | 数据类型 | 单位 / 分辨率 | 访问 |
|---|---|---|---|---|
| 0x0000-0x0001 | 温度(DS18B20) | int16(×100) | 0.01°C | 只读(03) |
| 0x0002-0x0003 | 光照度(BH1750) | uint16 | 1 lux | 只读(03) |
| 0x0004-0x0005 | 光敏 ADC 电压 | uint16(×1000) | 1 mV | 只读(03) |
| 0x0006-0x0007 | 加速度 X 轴(MPU6050) | int16(×100) | 0.01 g | 只读(03) |
| 0x0008-0x0009 | 加速度 Y 轴 | int16(×100) | 0.01 g | 只读(03) |
| 0x000A-0x000B | 加速度 Z 轴 | int16(×100) | 0.01 g | 只读(03) |
| 0x000C-0x000D | 欧拉角 Roll | int16(×100) | 0.01° | 只读(03) |
| 0x000E-0x000F | 欧拉角 Pitch | int16(×100) | 0.01° | 只读(03) |
| 0x0010-0x0011 | 欧拉角 Yaw | int16(×100) | 0.01° | 只读(03) |
| 0x0012-0x0013 | 超声波距离 | uint16 | 1 mm | 只读(03) |
| 0x0014-0x0015 | ADC DMA 采样值(供 FFT) | uint16 | 原始 ADC 值 | 只读(03) |
| 0x0016 | 心跳/状态字 | uint16(bit0-15) | bit0=运行 bit1=传感器故障 bit2=通信超时 | 只读(03) |
| 0x0017 | PID 设定值(温度) | uint16(×100) | 0.01°C | 读写(03/06) |
📡 MQTT Topic 设计
| Topic | 方向 | QoS | 内容示例 |
|---|---|---|---|
sensor/temp | 汇总→PC | 1 | {"value":25.68,"unit":"C","ts":1623456789} |
sensor/light | 汇总→PC | 1 | {"value":325,"unit":"lux","ts":1623456790} |
sensor/imu | 汇总→PC | 1 | {"roll":12.5,"pitch":-3.2,"yaw":178.1,"ts":1623456791} |
sensor/distance | 汇总→PC | 0 | {"value":150,"unit":"mm","ts":1623456792} |
sensor/fft | 汇总→PC | 1 | {"peak_freq":123.5,"amplitude":0.87,"ts":1623456793} |
sensor/pid | 汇总→PC | 1 | {"setpoint":30.0,"actual":29.6,"output":0.45,"ts":1623456794} |
sensor/status | 汇总→PC | 0 | {"uptime":3600,"free_heap":32768,"err_cnt":0,"ts":1623456795} |
cmd/pid | PC→汇总 | 1 | {"target_temp":30.0,"kp":2.5,"ki":0.3,"kd":0.1} |
cmd/led | PC→汇总 | 1 | {"led":1,"state":true} |
cmd/servo | PC→汇总 | 1 | {"angle":90} |
docker run -d --name mosquitto -p 1883:1883 -p 9001:9001 eclipse-mosquitto
L2 技术含量最高的阶段。利用 F407 的 FPU(单精度浮点运算单元)和 CMSIS-DSP 库,将数字信号处理算法落地到嵌入式平台。每个算法先用官方库跑通,再对比手写版本性能差异。
FPU 使能 + CMSIS-DSP 库集成
- 确认 F407 FPU 已使能:PlatformIO 中
build_flags = -mfloat-abi=hard -mfpu=fpv4-sp-d16 - CMSIS-DSP 库导入:从 STM32CubeF4 固件包中提取或 PlatformIO 直接添加依赖
- 基础运算验证:
arm_add_f32/arm_mult_f32/arm_mat_mult_f32(3×3矩阵乘法) - FPU 性能对比实验:1000 次浮点乘法——硬件 FPU vs 软件浮点(关闭 FPU),测量执行时间差异(预期 FPU 快 5—10 倍)
- 用 Systick 微秒级计时函数测量算法耗时
数字滤波器 — FIR / IIR
- 滑动平均滤波(最简单的低通滤波,用 Claude Code 手写)
- 一阶 IIR 低通滤波器:
y[n] = α·x[n] + (1-α)·y[n-1],用手写实现 - CMSIS-DSP FIR 滤波器:
arm_fir_init_f32+arm_fir_f32,设计一个 32 阶低通 FIR - 对比实验:ADC 采集电位器信号(叠加手抖动噪声),分别用滑动滤波 / 手写 IIR / CMSIS-DSP FIR 处理,对比滤波前后波形
- PC 端验证:串口将原始+滤波后数据同时发 PC,用 Python 画三条曲线叠加显示
卡尔曼滤波
- 一维卡尔曼滤波原理(用 Claude Code 学习):预测→更新→迭代
- 手写一维卡尔曼滤波对 DS18B20 温度数据进行实时滤波
- 对比实验:同一杯热水冷却过程,原始温度值 vs 滑动滤波 vs 卡尔曼滤波,三条曲线 PC 端叠加显示
- 调参体验:改变过程噪声协方差 Q 和测量噪声协方差 R,观察滤波效果变化(R 越小越相信测量值,R 越大越平滑但响应慢)
- 扩展到 MPU6050 加速度数据去噪:单轴一维卡尔曼
- 理解核心:卡尔曼滤波不是简单的"平滑",而是融合了"系统模型预测"和"传感器测量"的最优估计
互补滤波 + 姿态解算
- 姿态解算问题:加速度计(长期稳定但短时噪声大)+ 陀螺仪(短时精确但长期漂移)→ 两者融合
- 互补滤波原理:
angle = 0.98×(angle + gyro·dt) + 0.02×acc_angle(高通+低通互补) - 用加速度计原始数据算 Roll/Pitch:
roll = atan2(ay, az)/pitch = atan2(-ax, sqrt(ay²+az²)) - 陀螺仪积分:
angle += gyro_rate × dt - 对比实验:加速度计直接算的角度(抖动大)vs 互补滤波后角度(平滑稳定),PC 端双曲线叠加
- 手持 MPU6050 模块在空中翻转,观察 PC 端欧拉角曲线实时跟踪
FFT 频谱分析
- FFT 原理直觉(Claude Code 解释):时域信号 → 频域分解,看到"有哪些频率成分"
- ADC DMA 双缓冲高速采集:BufferA 采满 → DMA 自动切到 BufferB → 信号量通知 Task_FFT 处理 BufferA(乒乓缓冲,连续不丢点)
- 采样率设置:Timer 触发 ADC 在 1kHz 采样,1024 点 → FFT 频率分辨率 = 1kHz / 1024 ≈ 1Hz
- CMSIS-DSP
arm_rfft_fast_f32做 1024 点实数 FFT,计算各频率幅值arm_cmplx_mag_f32 - 提取峰值频率:在频谱中搜索幅值最大的 bin,输出主频 + 二次/三次谐波
- 核心实验:用手指敲击桌面(连光敏电阻模块 AO 到 ADC)→ FFT 识别敲击主频;不同力度/频率敲击 → 观察频谱峰值移动
- 验证:用函数信号发生器或手机 App 产生已知频率声音/振动 → FFT 测量值与已知值对比,误差应 < 2Hz
PID 闭环控制
- PID 控制原理:P(比例)快速响应 + I(积分)消除静差 + D(微分)抑制超调
- 位置式 PID vs 增量式 PID 对比
- 实验1 — 温度闭环控制:DS18B20 测温 → PID 计算 → PWM 控制 L298N 驱动风扇(模拟加热/制冷)
- 实验2 — 光照闭环控制:BH1750 测光 → PID 计算 → PWM 控制 LED 亮度
- PID 参数手动调优实验(经典的 Ziegler-Nichols 方法):
- ① 先设 Ki=Kd=0,逐步增大 Kp 直到系统出现等幅振荡 → 记下临界增益 Ku 和振荡周期 Tu
- ② 查表计算 Kp/Ki/Kd → 微调观察超调、调节时间、稳态误差变化
- PC 端实时显示设定值 vs 实际值双曲线(红色=设定/蓝色=实际),PID 收敛过程可视化
- 阶段3验收标准:每个算法独立 Demo 可运行,PC 端可视化对比验证效果
前三个阶段各自独立验证后,此阶段将 FreeRTOS + Modbus + DSP + MQTT + PyQt5 全部整合为一个完整系统。
采集节点全功能重构
- 整合 FreeRTOS 4 任务框架 + Modbus 从机 + 数字滤波(FIR/IIR 对 ADC 数据实时滤波)
- 传感器数据先滤波再存入 Modbus 寄存器(滤波在 Task_SensorAcq 中完成)
- ADC DMA 双缓冲:BufferA/BufferB 乒乓切换,供远端 FFT(通过 Modbus 寄存器 0x0014 读取原始 ADC)
- Modbus 从机通信鲁棒性:CRC 错误 → 不响应;地址不匹配 → 静默忽略;功能码不支持 → 返回异常码
- 测试:汇总节点连续轮询 1000 次,记录应答率 ≥ 99.9%
汇总节点重构(数据链路)
- 整合 FreeRTOS 7 任务框架 + Modbus 主机 + 数据融合通道
- 数据流:Modbus 轮询 → Queue_SensorData → Task_DataFusion(卡尔曼+互补滤波)→ Queue_SensorData → Task_WiFiReport(MQTT 上报)
- OLED 多页面:Page1=传感器数值 / Page2=通信状态(轮询周期/丢包) / Page3=PID参数 / 按键短按切换页面,长按进入设置
- 先不接入 FFT 和 PID,确保数据链路(采集→Modbus→融合→MQTT→PC)畅通
汇总节点重构(DSP + 控制链路)
- 加入 Task_FFT:ADC DMA 双缓冲 → 1024 点 FFT → 峰值频率提取 → 结果入队
- 加入 Task_PIDCtrl:从消息队列获取传感器数据 → PID 计算 → PWM 输出(舵机角度 + 风扇占空比)
- 所有 7 个任务同时运行,重点检查:
- ①
uxTaskGetStackHighWaterMark检查每个任务栈余量 ≥ 20% - ② 优先级反转检查:低优先级任务持有互斥量时,高优先级任务是否被阻塞
- ③ 消息队列是否溢出(生产快于消费)→ 增大队列深度或降低生产者频率
- 使用
vTaskGetRunTimeStats查看每个任务的 CPU 占用率
PC PyQt5 工业监控面板开发
- 用 Claude Code 生成完整的 PyQt5 工业监控面板代码(6 大模块)
- 模块1:实时数据仪表盘 — 圆形温度表盘 + 光照进度条 + 距离数字 + 姿态角数字显示
- 模块2:6 通道实时曲线 — pyqtgraph 实现高性能滚动波形(温度/光照/ADC电压/距离/Roll/Pitch),支持鼠标缩放和暂停
- 模块3:PID 控制面板 — 设定值滑条 + P/I/D 参数输入框 + 设定值 vs 实际值双曲线(红vs蓝)
- 模块4:3D 姿态球 — PyOpenGL 渲染,MPU6050 欧拉角驱动球体实时旋转
- 模块5:FFT 频谱瀑布图 — 横轴频率/纵轴时间/颜色幅值(类似专业频谱仪),最新频谱显示在底部
- 模块6:控制与报警 — LED/舵机远程控制按钮 + 温度超阈值红色闪烁 + 事件日志列表
- 数据源:paho-mqtt 订阅 MQTT Topic(主通道)+ pyserial 串口(备用通道)
全链路联调
- 端到端打通:采集节点(传感器) → Modbus RTU(UART) → 汇总节点(FreeRTOS 7任务) → MQTT(WiFi) → PC PyQt5 面板
- 验证 6 大面板模块全部接收实时数据
- 测试双向通信:PC PyQt5 下发 PID 设定值 → MQTT → 汇总节点 → PID 执行 → 实际值变化反馈到 PC 面板
- 测试闭环控制回路:PC 拖动设定值滑条 → 5 秒内观察实际值曲线追踪设定值
- 记录端到端延迟:传感器值变化 → PC 面板刷新(目标 < 200ms)
- 记录发现的所有问题(为 Day 25 性能优化做准备)
异常处理 + 鲁棒性加固
- 通信超时重连:Modbus 轮询连续 3 次无应答 → 复位通信状态机 → 重新建连
- WiFi/MQTT 断线恢复:Task_WiFiReport 检测 MQTT 连接状态 → 断线自动重连 Broker + 重新订阅 Topic
- 传感器热插拔:I²C 设备无应答(HAL_I2C_IsDeviceReady 返回错误)→ 标记故障 → Task_Alert 告警 → 定期尝试恢复
- 栈溢出 Hook:实现
vApplicationStackOverflowHook,栈溢出时记录出问题的任务名和栈地址到备份寄存器(复位后仍可读取) - HardFault 异常捕获:实现 HardFault_Handler,读取 stacked PC/LR/SP 寄存器值,通过串口打印崩溃现场(类似 Linux kernel panic 信息),用 Claude Code 辅助分析崩溃原因
- 阶段4验收标准:全链路运行正常,PyQt5 面板 6 模块全部有数据,双向控制可工作
最后阶段聚焦于量化指标的测量与优化,以及可观赏性强的展示内容准备。
性能基准测试 + 优化
- Modbus 轮询效率:测量 3 次连续轮询总时间 → 目标 < 20ms(修改波特率或减少寄存器读取量优化)
- MQTT 上报频率:测量从数据入队到 ESP8266 发出 MQTT Publish 的延迟 → 目标 < 50ms
- FFT 运算速度:测量 arm_rfft_fast_f32(1024点)的耗时 → 目标 < 3ms(FPU 加速下应 < 1ms)
- PID 控制周期:测量从传感器读数到 PWM 输出的闭环延迟 → 目标 < 10ms
- 记录所有指标到基准测试表,分析瓶颈(CPU/内存/通信带宽)
长期稳定性测试
- 连续运行 2—4 小时,记录:
- ① 数据刷新率稳定性(每 10 分钟采样一次刷新率,标准差 < 5%)
- ② 通信丢包率(Modbus 总查询次数 / 无应答次数)
- ③ 内存泄漏检查:每 30 分钟读取
xPortGetFreeHeapSize,绘制剩余堆内存曲线 - ④ 任务栈余量变化(uxTaskGetStackHighWaterMark 的谷值)
- ⑤ 传感器读数漂移(MPU6050 陀螺仪零偏在 2 小时内的变化)
- 人为注入故障:拔掉传感器 I²C 线 → 观察系统恢复时间 → 优化恢复逻辑
展示效果打磨
- PID 收敛演示:在 PC 面板上展示"改变设定值→实际值追踪"的完整过程,确保双曲线动态效果好
- 3D 姿态球演示:手持 MPU6050 翻转,确认姿态球丝滑跟随无卡顿
- FFT 频谱演示:连续敲击桌面不同频率,观察频谱瀑布图变化,确保峰值清晰可见
- Modbus 通信演示:在 OLED 上实时显示轮询周期和丢包率,展示通信质量
- 报警演示:遮挡光敏电阻或将 DS18B20 靠近热源 → PC 面板红色闪烁 + 蜂鸣器报警
- 整体演示流畅度编排:按照"数据展示→PID控制→3D姿态→FFT分析→报警"自然流程串联
代码审查 + 文档撰写
- 用 Claude Code 对全部代码做审查:指出潜在 bug、内存泄漏风险、未初始化变量、中断安全违规
- Git 整理:合并临时 commit → 打 tag
v2.0-rc1→ 冻结展示版本 - 撰写技术文档(不少于 2000 字):
系统架构设计 / FreeRTOS 任务设计理由 / Modbus 寄存器映射表 / MQTT Topic 设计说明 / DSP 算法选型与对比 / PID 调参过程记录 / 性能基准测试结果 / 已知问题和改进方向 - 代码注释补全,确保后人能看懂
展示准备 — PPT + 演示脚本
- 制作展示 PPT(15—20 页):
① 封面(项目名/姓名/日期) ② 项目背景与目标 ③ L1→L2提升对比 ④ 系统架构总图 ⑤ FreeRTOS 任务设计 ⑥ Modbus 寄存器映射 ⑦ MQTT Topic 设计 ⑧ DSP 算法链(滤波→卡尔曼→FFT→PID) ⑨ PyQt5 面板截图 ⑩ PID调参过程实验数据 ⑪ 性能基准测试结果 ⑫ 稳定性测试数据 ⑬ 实物照片 ⑭ 技术难点与解决方案 ⑮ 个人收获与总结 ⑯ 致谢 - 演示脚本编排(8 分钟流畅演示):
- 第1分钟:系统整体介绍 + 实物展示
- 第2-3分钟:PC PyQt5 面板 6 模块逐一展示(数据仪表盘→实时曲线→3D姿态球→FFT频谱→PID面板→控制与报警)
- 第4-5分钟:PID 闭环控制演示(拖动设定值滑条,观察实际值追踪过程)
- 第6-7分钟:FFT 频谱分析演示(敲击桌面不同频率,观察频谱峰值变化)
- 第8分钟:性能数据展示(量化指标汇总表)+ 技术问答
最终展示 + 成果提交
- 上午:正式作品展示 + 技术答辩
- 下午:提交最终交付物
- ① 完整源码(Git 仓库,含清晰的 commit 历史)
- ② 技术文档(PDF,≥2000 字)
- ③ 展示 PPT(15—20 页)
- ④ 演示录屏视频(8 分钟,含语音解说)
- ⑤ 量化指标报告(系统各项性能数据汇总)
- ⑥ PC PyQt5 面板可执行文件(pyinstaller 打包成 .exe)
- 打 tag
v2.0-release - 训练营结业
以下指标在 Day 25 性能测试阶段逐项测量,Day 30 展示答辩时向评委公开。所有指标均有明确的目标值和测量方法,不可模糊评价。
| 编号 | 指标 | 目标值 | 测量方法 | 展示方式 |
|---|---|---|---|---|
| Q1 | Modbus RTU 轮询周期 | ≤ 20ms(3 从机数据) | OS 定时器打时间戳,计算两次 Modbus 查询间隔 | OLED 实时显示 + PC 面板 |
| Q2 | Modbus 通信丢包率 | ≤ 0.1%(1000 次查询) | 主机计数:发送次数 vs 收到有效应答次数 | 量化指标报告 |
| Q3 | MQTT 端到端延迟 | ≤ 200ms(传感器变化→PC显示) | STM32 发送时间戳 → PC 接收时间戳差值 | PC 面板状态栏 |
| Q4 | 1024 点 FFT 运算耗时 | ≤ 3ms(FPU 加速) | SysTick 微秒计时:arm_rfft_f32 前后时间戳差 | 串口打印 + PC 面板 |
| Q5 | FFT 频率分辨率 | ≤ 1Hz(@ 1kHz 采样率) | 已知频率输入(如手机 App 产生 440Hz 正弦波)→ FFT 测量值与真实值对比 | FFT 频谱瀑布图 + 报告 |
| Q6 | PID 温度控制稳态误差 | ≤ ±0.3°C | 设定值 30°C → 系统稳定后,2 分钟内实际值偏差范围 | PID 面板双曲线(设定值 vs 实际值) |
| Q7 | PID 阶跃响应调节时间 | ≤ 15 秒(设定值 25→30°C) | 从拖动滑条改变设定值开始,到实际值首次进入 ±0.5°C 范围的时间 | PID 面板双曲线 + 调节时间标注 |
| Q8 | MPU6050 姿态角精度 | Roll/Pitch 误差 ≤ 1°(静态) | 互补滤波后角度 vs 手机水平仪 App 读数对比 | 3D 姿态球 + 数字显示 |
| Q9 | 姿态解算刷新率 | ≥ 100Hz | Task_DataFusion 中姿态计算循环计数,每秒统计 | PC 面板 3D 球更新频率 |
| Q10 | 系统连续稳定运行 | ≥ 4 小时不死机/不重启 | IWDG 未触发复位 + 任务心跳计数递增正常 | PC 面板运行时长显示 |
| Q11 | FreeRTOS CPU 占用率 | ≤ 70%(所有任务总和) | vTaskGetRunTimeStats 统计各任务 CPU 时间占比 | 量化指标报告 |
| Q12 | 内存剩余(Free Heap) | ≥ 20KB(总 192KB SRAM) | xPortGetFreeHeapSize 在运行 30 分钟后读取 | OLED 显示 + PC 面板 |
| 模块 | 使用库 | 功能 | 观赏性 |
|---|---|---|---|
| 1. 实时数据仪表盘 | PyQt5 QPainter 自绘或 qwt | 圆形温度表盘(0—50°C,指针+数字) 光照进度条(0—1000 lux,颜色渐变) 距离数字显示(mm) 姿态角 Roll/Pitch/Yaw 数字(°) |
★★★ |
| 2. 6 通道实时曲线 | pyqtgraph | 6 条曲线同屏滚动显示(温度/光照/ADC/距离/Roll/Pitch) 支持鼠标框选缩放、右键复位、空格暂停 Y 轴自适应范围 X 轴显示最近 30 秒数据(滚动窗口) |
★★★★ |
| 3. PID 控制面板 | PyQt5 + pyqtgraph | 设定值横向滑条(温度 0—50°C) P/I/D 参数输入框(带 SpinBox 微调) 设定值(红色)vs 实际值(蓝色)双曲线 调节时间/超调量/稳态误差 实时计算并显示 "参数下发"按钮 → MQTT 发送 PID 参数到汇总节点 |
★★★★★ |
| 4. 3D 姿态球 | PyOpenGL | 球体 + 经纬线 + 坐标系箭头(RGB = XYZ) MPU6050 欧拉角驱动球体实时旋转 下方显示 Roll/Pitch/Yaw 数值 |
★★★★★ |
| 5. FFT 频谱瀑布图 | pyqtgraph ImageView 或 matplotlib | 横轴 = 频率(0—500Hz) 纵轴 = 时间(从上到下,最新在底部) 颜色 = 幅值(蓝=低→红=高,热力图色阶) 右侧显示当前帧峰值频率 + 幅值 |
★★★★★ |
| 6. 控制与报警 | PyQt5 | LED1/LED2 远程开关按钮 舵机角度滑条(0—180°) 温度超阈值报警:红色闪烁 + 蜂鸣 事件日志列表(时间戳 + 事件类型 + 内容) 系统运行时长显示 |
★★★ |
| 时间 | 任务 |
|---|---|
| 8:30—9:00 | 开 VS Code,回顾昨天代码和笔记。在终端输入当日目标给 Claude Code,获取今日开发建议 |
| 9:00—11:30 | 新知识学习 + 代码编写。L2 阶段更依赖 Claude Code 做深度技术问答(如 "请解释 FreeRTOS 的优先级反转问题" / "帮我分析这段卡尔曼滤波的 Q 和 R 矩阵设置是否合理") |
| 11:30—12:00 | 开发笔记更新:记录今日理解的核心概念、踩坑记录、性能数据 |
| 13:30—16:30 | 动手实验 + 系统联调。L2 的重心在软件调试(FreeRTOS 任务堆栈分析、DSP 算法精度验证、通信协议状态机排查) |
| 16:30—17:00 | Git 提交当日代码,写有意义的 commit message(如 "feat: 完成 Modbus 从机 CRC16 校验和异常码响应") |
| 17:00—17:30 | 回顾当日成果,记录未解决的问题。用 Claude Code 做代码审查("请审查今天的代码改动,指出潜在问题") |
FreeRTOS 相关
claude "我的 FreeRTOS 任务栈溢出了,uxTaskGetStackHighWaterMark 返回 0。请帮我分析可能的原因并给出解决方案"claude "我有 6 个 FreeRTOS 任务,请帮我设计合理的优先级分配方案,避免优先级反转"claude "请审查我的 FreeRTOS 中断服务函数,检查是否有不符合 FromISR API 规范的地方"
通信协议相关
claude "请帮我写一个 Modbus RTU CRC16 查表法的 C 语言实现,用于 STM32F407"claude "我的 Modbus 从机偶尔不响应主机查询,可能是什么原因?如何加入调试日志定位问题?"claude "请解释 MQTT QoS 0/1/2 的区别,并帮我在 ESP8266 AT 固件上实现 QoS 1 的消息发布"
DSP / 算法相关
claude "请用 C 语言实现一维卡尔曼滤波,用于温度传感器数据去噪,并解释每个参数的含义"claude "我在 STM32F407 上用 arm_rfft_fast_f32 做 1024 点 FFT,但频谱结果不对。请帮我检查代码"claude "我的 PID 温度控制出现明显的超调和震荡,请帮我分析是 P/I/D 哪个参数的问题,并指导如何调优"
PyQt5 相关
claude "请帮我用 PyQt5 和 pyqtgraph 创建一个 6 通道实时曲线显示窗口,数据通过 MQTT 接收"claude "请用 PyOpenGL 在 PyQt5 窗口中创建一个 3D 球体,根据实时欧拉角旋转,球体上要画经纬线和坐标系箭头"claude "请用 pyinstaller 把我的 PyQt5 工程打包成一个独立的 Windows .exe 文件"
| 库/工具 | 用途 | 安装方式 |
|---|---|---|
| FreeRTOS | 实时操作系统内核 | PlatformIO 内置,platformio.ini 中启用 |
| CMSIS-DSP | 数字信号处理库(FIR/IIR/FFT/矩阵/数学) | 从 STM32CubeF4 固件包提取,或 PlatformIO 添加依赖 |
| paho-mqtt | Python MQTT 客户端库 | pip install paho-mqtt |
| PyQt5 | Python GUI 框架 | pip install PyQt5 |
| pyqtgraph | 高性能实时图表库 | pip install pyqtgraph |
| PyOpenGL | Python OpenGL 绑定(3D 姿态球) | pip install PyOpenGL PyOpenGL_accelerate |
| pyinstaller | Python 打包为 .exe | pip install pyinstaller |
| EMQX Public Broker | 免费公网 MQTT 测试服务器 | 无需安装,连接 broker.emqx.io:1883 |
| MQTTX | MQTT 桌面调试客户端 | mqttx.app 下载(调试 MQTT 通信时使用) |
| Modbus Poll | Modbus 主机调试工具 | modbustools.com(免费试用版,调试 Modbus 通信) |
| Serial Oscilloscope | 串口数据可视化(波形查看) | github.com/xioTechnologies/Serial-Oscilloscope(调试 ADC/DSP 算法) |