
个人主页:
wengqidaifeng
✨ 永远在路上,永远向前走
个人专栏:
数据结构
C语言
嵌入式小白启动!
重要OJ算法题详解
蓝桥杯备战
C++从菜鸟到强手
python启航
AI大模型Agent:拥抱未来,赋能自己
文章目录
-
- 博客系列总目录与阅读路线
- 推荐阅读顺序
- 全系列知识地图
- 总览
- 仓库资料边界
- 开源说明
- [第七篇:MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法](#第七篇:MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法)
-
- [1. 调试的本质是建立证据链](#1. 调试的本质是建立证据链)
- [2. MaxiCamPro 的两条链路](#2. MaxiCamPro 的两条链路)
- [3. 双主控通信协议](#3. 双主控通信协议)
- [4. VOFA+ 遥测字段设计](#4. VOFA+ 遥测字段设计)
- [5. 如何用波形定位直线振荡](#5. 如何用波形定位直线振荡)
- [6. 如何用波形定位滚球超调](#6. 如何用波形定位滚球超调)
- [7. 实验设计:一次只验证一个假设](#7. 实验设计:一次只验证一个假设)
- [8. 测试金字塔](#8. 测试金字塔)
- [9. 视频复盘方法](#9. 视频复盘方法)
- [10. 开源项目如何让别人复现](#10. 开源项目如何让别人复现)
- [11. 与往年调试方式的比较](#11. 与往年调试方式的比较)
- [12. 本篇结论](#12. 本篇结论)
- [13. 数据处理与展示的注意事项](#13. 数据处理与展示的注意事项)
博客系列总目录与阅读路线
本系列记录 MSPM0G3507 双主控小车从赛题分析、硬件搭建、底层驱动、循迹调试,到摆杆滚球控制和最终测评复盘的完整过程。文章中的路径均为仓库相对路径,代码、测试脚本和模块封装可以直接从公开仓库取得。
推荐阅读顺序
- 赛题拆解、总体方案与往年控制类小车赛题比较
- 硬件选型、接线与上电验证
- 软件架构、SysConfig 与模块化设计
- 编码器速度环、差速运动学、MPU6050 航向环与直线稳定
- 8 路灰度循迹、直线/半圆分段与 S1/S2/S3 状态机
- 摆杆机械结构、MaxiCamPro 测量、步进电机与三层 PID
- MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法
- 完整测评流程、失败案例复盘、比赛策略与开源交付
全系列知识地图
#mermaid-svg-Uwy5AZvdLI860rt5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Uwy5AZvdLI860rt5 .error-icon{fill:#552222;}#mermaid-svg-Uwy5AZvdLI860rt5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Uwy5AZvdLI860rt5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Uwy5AZvdLI860rt5 .marker.cross{stroke:#333333;}#mermaid-svg-Uwy5AZvdLI860rt5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Uwy5AZvdLI860rt5 p{margin:0;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge{stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 text{fill:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth--1{stroke-width:17;}#mermaid-svg-Uwy5AZvdLI860rt5 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-0{stroke-width:14;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-1{stroke-width:11;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 text{fill:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-2{stroke-width:8;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-3{stroke-width:5;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-4{stroke-width:2;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-5{stroke-width:-1;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-6{stroke-width:-4;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-7{stroke-width:-7;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-8{stroke-width:-10;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-9{stroke-width:-13;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 polygon,#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 text{fill:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .edge-depth-10{stroke-width:-16;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled circle,#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:lightgray;}#mermaid-svg-Uwy5AZvdLI860rt5 .disabled text{fill:#efefef;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-root rect,#mermaid-svg-Uwy5AZvdLI860rt5 .section-root path,#mermaid-svg-Uwy5AZvdLI860rt5 .section-root circle,#mermaid-svg-Uwy5AZvdLI860rt5 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-Uwy5AZvdLI860rt5 .section-root text{fill:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-root span{color:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .section-2 span{color:#ffffff;}#mermaid-svg-Uwy5AZvdLI860rt5 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-Uwy5AZvdLI860rt5 .edge{fill:none;}#mermaid-svg-Uwy5AZvdLI860rt5 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-Uwy5AZvdLI860rt5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} H题车载平衡滚球
赛题与指标
赛道圈时
启停判据
滚球目标点
硬件系统
MSPM0G3507
TB6612与编码器
灰度与MPU6050
MaxiCamPro与步进电机
软件控制
速度环
航向环
灰度状态机
三层滚球PID
工程方法
接线验证
VOFA遥测
回归测试
测评复盘
总览
这道题不是单独写一个循迹 PID,而是把机械结构、电源、传感器、实时调度、速度闭环、航向辅助、视觉通信、双主控安全和赛道状态机组合成一个可测量、可恢复、可验收的系统。调试时坚持"每次只改变一个因素",并用串口遥测、主机回归测试和实车视频记录证据。
仓库资料边界
- 小车运动主控工程:公开仓库根目录
smartcar_keil_diag/ - 通用模块封装:公开仓库中的
ModulePackage/ - 摆杆/滚球控制参考工程:
BalanceController/ - 硬件与接线说明:
Hardware/ - 主机测试与回归脚本:
Tests/ - 主控、蓝牙、VOFA+ 和双主控协议说明:仓库根目录下相应的
*.md文件
主控采用 LCKFB 天猛星 MSPM0G3507,车体约为 30 cm × 24 cm,车轮直径约 65 mm。实际使用的主要器件包括 JGB37-520 直流减速电机、TB6612 驱动器、MPU6050、8 路灰度传感器、MaxiCamPro 和无编码器步进电机。涉及具体电气参数、安装尺寸和控制增益时,应以实测记录和仓库中的配置为准,不要直接照搬示例数值。
开源说明
完整工程、博客、测试脚本和模块封装均发布在 GitHub:
https://github.com/wengqidaifeng/smartcar_keil_diag
建议按 01→08 的顺序阅读;需要查代码时从 User/、Source/、ModulePackage/ 和 Tests/ 进入。文章中的结论区分为"源码已实现""主机测试已验证"和"必须在实车/机构上复测"三类,便于后来者复现实验并继续改进。
第七篇:MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法
1. 调试的本质是建立证据链
<!CDATA\[实车视频能告诉我们"小车在第二段直线偏离""半圆出弯冲了一下""球越过目标",却无法直接说明原因。控制系统中多个变量同时变化:目标速度、实测速度、PWM、灰度偏差、航向角、角速度、赛道状态、视觉位置和执行器角度。没有同步数据时,调参往往退化为根据单次视频猜测,今天加大 P,明天又降低 P,版本之间无法比较。]>
本项目把可观测性视为控制系统的一部分。OLED 用于现场快速确认模式、状态、灰度位图和故障;蓝牙/USB 串口向 VOFA+ 输出连续遥测;MaxiCamPro 输出视觉测量;视频记录宏观轨迹。四种手段各有职责:OLED 不承担高速波形,VOFA 不替代画面,视频不能替代数值,视觉帧也不等于控制命令。
2. MaxiCamPro 的两条链路
MaxiCamPro 可以同时提供视频显示/录像和 UART 测量。视频链路用于裁判和复盘,UART 链路将球位置发送给摆杆主控。典型 ASCII 帧为:
text
BALL,3,-35,36,78,26,-24,35,75,26,-16,37,78,26
含义是检测到 3 个球,每个球依次提供 x、y、z 和像素直径。控制端必须校验类型、数量、字段数、数值范围和换行结束。若 count=3,字段数应与 2+4×3 一致;若距离未标定,z 可能为 -1,不能当作负距离参与控制。
另一种脚本会输出钢球中心、球槽中心和轴向偏差。无论协议形式如何,帧都应包含或由接收时记录时间戳。接收中断只入环形缓冲,遇到换行后在任务上下文解析。超长帧应丢弃并重同步,不能写入越界;部分帧不能更新控制状态;解析成功也要检查数据年龄。
图传与 UART 同开是否可靠不能凭规格推断。需要连续运行数分钟,统计视觉有效帧率、最大帧间隔、视频卡顿和串口错误。若同开明显降低控制帧率,可使用独立 ESP32-S3 图传,只负责显示和录像;MaxiCamPro 保留测量。独立图传不进入闭环,也不通过 UART 指挥步进电机。
3. 双主控通信协议
小车主控和摆杆主控之间应传输结构化状态,而不是共享零散文本命令。建议至少包含协议版本、序号、运行模式、车速、车体姿态、任务状态、故障位和校验。接收方使用序号检测丢帧,用时间戳判断新鲜度,用 CRC 或校验和检测错误。
小车发给摆杆的信息可以包括 S1/S2/S3、当前 forward、yaw/roll/pitch、gyroZ、启动/急停和赛道相位;摆杆返回球控制就绪、当前目标、球位置误差、球杆角度、视觉在线和故障。双方都不能直接使用对方最后一条 PWM。通信超时后,小车按自身安全策略停车或继续保底循迹,摆杆把前馈归零并进入安全角或失能,具体行为由比赛任务决定。
协议要有版本兼容。新增字段时不能改变旧字段含义;接收长度不匹配时拒绝整帧。调试文本与控制帧最好使用不同 UART 或明确前缀,避免 VOFA 文本混入主控间协议。任何串口回调都不执行 PID 和电机动作,解析结果只更新状态,控制器在固定周期读取。
4. VOFA+ 遥测字段设计
小车遥测建议包含:
text
time_ms, line_error, filtered_error,
target_left, target_right, measured_left, measured_right,
pwm_left, pwm_right, line_bits, line_state,
speed_mode, oval_section, distance_cm,
yaw_deg, gyro_z_degps, steering_cmps,
task_state, stop_reason, fault_code
滚球遥测建议包含:
text
time_ms, visual_age_ms, ball_position_mm, ball_speed_mmps,
ball_target_mm, ball_error_mm,
rod_target_deg, rod_angle_deg, rod_rate_degps,
step_rate, limit_state, balance_state, fault_code
字段单位必须固定,文档写清正方向。每帧字段数量固定,缺失值用明确哨兵,不随便少一列。采样频率可低于控制频率,例如控制 100 Hz、遥测 20~50 Hz;但关键事件如状态切换、故障和终点可以单独输出事件行。
如果串口带宽不足,优先保留目标、反馈、输出和状态四类量。大量帮助文本只在停车状态按命令返回。遥测发送应使用缓冲和非阻塞策略,不能因为上位机未接收而阻塞 10 ms 控制。
5. 如何用波形定位直线振荡
<!CDATA\[第一步看 \`line_state/oval_section\`。如果第二段直线仍显示半圆,先修状态判定,不能调直线 PID。第二步看目标速度:左右目标是否在数个控制周期内从大正差速跳到大负差速?若是,问题在循迹增益、边缘救线或转向斜坡。第三步看实测速度:目标平滑但实测振荡,说明速度环、死区或编码器量化存在问题。第四步看 yaw/gyroZ:灰度回中心后角速度是否仍很大?若是,需要出弯阻尼和航向锁。]>
PWM 饱和也很关键。若外轮长期顶到最大,继续增加转向只会降低内轮,前进速度突然变化,车体和滚球都会受冲击。此时应降低半圆 forward、提高可用外轮余量或重新检查电机能力,而不是盲目提高 P。
6. 如何用波形定位滚球超调
把球位置、球速度、目标倾角和实际倾角画在同一时间轴。若位置接近目标时球速度仍很大,而目标倾角没有提前反向,速度环太弱或速度估算延迟;若目标倾角已经反向但实际角度跟不上,问题在角度内环、步频限制或机械卡滞;若角度跟随正常但球仍不动,检查槽摩擦、标定方向和实际倾角。
视觉位置抖动会使速度差分出现尖峰。观察 visual_age 和帧间隔,若不均匀,应使用真实时间差而不是固定 20 ms。球位置偶发跳变时,目标选择可能在多个球之间切换,需要轨迹关联和直径/位置门限。不能用大低通掩盖错误目标,因为大低通会增加制动延迟。
7. 实验设计:一次只验证一个假设
每次测试前写出假设和预测。例如:"若第二段直线掉线是出弯残余角速度造成,那么延长受限航向恢复后,gyroZ 峰值和边缘位图持续时间应下降。"只修改一个变量,跑相同起点、相同模式、相近电池条件,比较波形。若预测不成立,回退并更换假设。
推荐记录模板包括:日期、Git 提交号、固件模式、地图版本、电池电压、机械状态、修改参数、预期、结果、最差指标、VOFA 文件和视频文件。文件名包含时间和版本,视频中用口头或纸条标记模式。失败数据同样保留,因为它能说明某个参数区间不可用。
一组参数至少重复三次。第一次成功可能是起始角度、轮胎位置或电池状态偶然合适。统计平均圈时、最差圈时、丢线次数、最大偏差和停车误差,比只展示最好一次更接近比赛可靠性。
8. 测试金字塔
最底层是主机单元/回归测试,快速验证 PID、协议、状态机和限幅;上一层是 SysConfig 静态与隔离生成,验证外设配置;再上一层是 Keil 全量编译,验证工程集成;然后是架空台架,验证方向、编码器和安全;最后才是场地实车和联合滚球。
每层通过只证明该层。主机测试通过不代表线接对,编译通过不代表电机方向对,烧录成功不代表实车满足时间,偶尔跑完不代表稳定。报告和开源 README 应明确区分"源码实现""测试通过""编译通过"和"实车验证"。
9. 视频复盘方法
视频最好固定机位覆盖完整赛道,帧率和时间戳稳定。复盘时标记入弯、半圆中点、出弯、第二段直线边缘和终点时刻,再到 VOFA 中找对应时间。若视频与串口时钟不同,可在启动时让 LED 或蜂鸣器产生可见/可听同步事件。
观察车体姿态时,不只看车头位置,还要看传感器板相对黑线、左右轮速度变化、万向轮摆动和上层球槽振动。半圆转不过去可能是转向指令不足,也可能是内轮打滑、外轮饱和或万向轮方向卡住。视频提供机械证据,遥测提供控制证据,二者结合才能下结论。
10. 开源项目如何让别人复现
仓库应提供总 README、接线表、工程入口、SysConfig、串口协议、测试命令、模式说明、参数单位和已知边界。排除 Objects、Listings、日志、视频和个人 IDE 状态,避免仓库庞大且难以审查。第三方 SDK 和生成文件应说明来源与许可证。
Issue 模板应要求硬件版本、接线、提交号、复现步骤、遥测和视频时间点。只写"车乱跑"无法定位。PR 应说明修改哪个控制环、为什么、测试结果和实车最差数据。这样开源不仅是上传代码,而是分享一套可验证的方法。
11. 与往年调试方式的比较
过去很多小车题可以通过现场肉眼和少量串口打印完成,因为状态少、速度低。随着视觉、IMU、双主控和执行器加入,单次现象对应的原因更多。VOFA+ 多通道遥测、版本控制和回归测试已经从"锦上添花"变成必要工具。
但工具数量并不等于质量。输出几十个没有单位的变量、每次改变字段顺序、只保存成功数据,仍然无法复现。真正有效的是稳定协议、明确假设、单变量实验和最差指标。这个方法同样适用于往年的云台、倒立摆、无人机、搬运车和路径规划题。
12. 本篇结论
可观测性让调参从经验变成工程。MaxiCamPro 提供目标测量,双主控协议传递必要状态,VOFA+ 把目标、反馈、输出和状态放在同一时间轴,视频补充机械与路径证据,版本控制保证每次结果可追溯。只有建立这条证据链,才能在复杂耦合系统中避免反复猜测,也才能让开源读者真正复现而不是只下载一份代码。
13. 数据处理与展示的注意事项
VOFA 曲线需要正确的时间轴。不要默认每一行严格等间隔,最好发送 MCU 时间戳并在分析时按时间重采样。串口丢行时,使用行号或序号检测;如果只按上位机接收时间绘图,USB 缓冲可能把多帧集中送达,造成虚假的周期抖动。重要事件可发送单独标记,例如 EVENT,ARC_ENTER、EVENT,LINE_LOST,便于与视频对齐。
滤波后的数据和原始数据应同时保留一部分。只输出滤波值会隐藏传感器毛刺,只输出原始值又难以看清趋势。调灰度时输出 raw bits、active bits 和 filtered error;调速度时输出原始计数增量、滤波速度和目标;调视觉时输出原始位置、选中目标编号和滤波位置。这样才能判断问题发生在测量、滤波还是控制。
公开数据时应附字段说明、单位、正方向、采样频率和固件提交号。截取一小段曲线可以说明某个现象,但完整结论最好提供原始 CSV 或文本。若数据包含个人串口设备名、路径或其他无关信息,可清理后再上传,技术字段不要为了美观而省略。
数据分析的目标不是画出平滑曲线,而是回答可证伪问题。例如比较两个版本的第二段直线最大 |line_error|、出弯后 1 s 内 gyroZ 峰值、全白累计时间和圈时;比较滚球有无前馈时的误差峰值与稳定时间。使用量化指标后,参数选择才不会由观看者主观决定。