
个人主页:
wengqidaifeng
✨ 永远在路上,永远向前走
个人专栏:
数据结构
文章目录
-
- 博客系列总目录与阅读路线
- 推荐阅读顺序
- 全系列知识地图
- 总览
- 仓库资料边界
- 开源说明
- [第五篇:8 路灰度循迹、直线/半圆分段控制与 S1/S2/S3 状态机](#第五篇:8 路灰度循迹、直线/半圆分段控制与 S1/S2/S3 状态机)
-
- [1. 从"看见黑线"到"稳定跑完赛道"](#1. 从“看见黑线”到“稳定跑完赛道”)
- [2. 加权质心与偏差符号](#2. 加权质心与偏差符号)
- [3. 直线控制为什么要温和](#3. 直线控制为什么要温和)
- [4. 半圆控制为什么需要前馈](#4. 半圆控制为什么需要前馈)
- [5. 直线和半圆如何可靠区分](#5. 直线和半圆如何可靠区分)
- [6. 入弯与出弯的过渡](#6. 入弯与出弯的过渡)
- [7. 全白、重捕获和断线](#7. 全白、重捕获和断线)
- [8. 启停线和终点停车](#8. 启停线和终点停车)
- [9. S1、S2、S3 三种模式](#9. S1、S2、S3 三种模式)
- [10. 往年循迹题经验怎样迁移](#10. 往年循迹题经验怎样迁移)
- [11. 本篇结论](#11. 本篇结论)
- [12. 赛道参数表和现场快速校准](#12. 赛道参数表和现场快速校准)
- 赛道状态机
博客系列总目录与阅读路线
本系列记录 MSPM0G3507 双主控小车从赛题分析、硬件搭建、底层驱动、循迹调试,到摆杆滚球控制和最终测评复盘的完整过程。文章中的路径均为仓库相对路径,代码、测试脚本和模块封装可以直接从公开仓库取得。
推荐阅读顺序
- 赛题拆解、总体方案与往年控制类小车赛题比较
- 硬件选型、接线与上电验证
- 软件架构、SysConfig 与模块化设计
- 编码器速度环、差速运动学、MPU6050 航向环与直线稳定
- 8 路灰度循迹、直线/半圆分段与 S1/S2/S3 状态机
- 摆杆机械结构、MaxiCamPro 测量、步进执行器与三层 PID
- MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法
- 完整测评流程、失败案例复盘、比赛策略与开源交付
全系列知识地图
#mermaid-svg-2rmsBUJ0gozw9RYA{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-2rmsBUJ0gozw9RYA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2rmsBUJ0gozw9RYA .error-icon{fill:#552222;}#mermaid-svg-2rmsBUJ0gozw9RYA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2rmsBUJ0gozw9RYA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2rmsBUJ0gozw9RYA .marker.cross{stroke:#333333;}#mermaid-svg-2rmsBUJ0gozw9RYA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2rmsBUJ0gozw9RYA p{margin:0;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge{stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 text{fill:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth--1{stroke-width:17;}#mermaid-svg-2rmsBUJ0gozw9RYA .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-0{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-0{stroke-width:14;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-1{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-1{stroke-width:11;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 text{fill:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-2{stroke-width:8;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-3{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-3{stroke-width:5;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-4{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-4{stroke-width:2;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-5{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-5{stroke-width:-1;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-6{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-6{stroke-width:-4;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-7{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-7{stroke-width:-7;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-8{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-8{stroke-width:-10;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-9{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-9{stroke-width:-13;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 polygon,#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 text{fill:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .node-icon-10{font-size:40px;color:black;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .edge-depth-10{stroke-width:-16;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled circle,#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:lightgray;}#mermaid-svg-2rmsBUJ0gozw9RYA .disabled text{fill:#efefef;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-root rect,#mermaid-svg-2rmsBUJ0gozw9RYA .section-root path,#mermaid-svg-2rmsBUJ0gozw9RYA .section-root circle,#mermaid-svg-2rmsBUJ0gozw9RYA .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-2rmsBUJ0gozw9RYA .section-root text{fill:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-root span{color:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .section-2 span{color:#ffffff;}#mermaid-svg-2rmsBUJ0gozw9RYA .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-2rmsBUJ0gozw9RYA .edge{fill:none;}#mermaid-svg-2rmsBUJ0gozw9RYA .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-2rmsBUJ0gozw9RYA :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/ 进入。文章中的结论区分为"源码已实现""主机测试已验证"和"必须在实车/机构上复测"三类,便于后来者复现实验并继续改进。
第五篇:8 路灰度循迹、直线/半圆分段控制与 S1/S2/S3 状态机
1. 从"看见黑线"到"稳定跑完赛道"
8 路数字灰度只能告诉控制器哪些探头位于黑线上,真正的循迹能力来自四层处理:传感器位图与极性、加权偏差、控制律、赛道状态机。任何一层错误都可能表现为"PID 不好"。例如 X1/X8 左右接反会让控制方向完全相反;黑白极性颠倒会把全白当终点;第二段直线被相位逻辑判断成半圆,会调用高差速参数,即使直线 PID 本身正确也会蛇形。
本项目经历过从 8 路红外数字模块到 8 路灰度模块的更换。旧模块黑线低电平,新模块实测黑线高电平,并增加电平转换。若只替换硬件而不重新做逐路位图测试,旧代码里的取反会把全部状态反转。因此每次换传感器、安装高度或阈值后,都要从原始位图开始验证,而不是直接上赛道调 P。
2. 加权质心与偏差符号
X1 位于车体左侧,X8 位于右侧。可给八路分配 -7,-5,-3,-1,+1,+3,+5,+7,对所有 active 通道计算加权平均。若黑线落在 X4/X5,偏差接近零;落在右侧 X7/X8,偏差为正;落在左侧则为负。实际控制符号还要结合电机混控验证。
数字阵列的分辨率有限,位图会从 0x18 突然跳到 0x30,而不是连续变化。可以对质心偏差做一阶低通,但不能过度平滑;高速时延迟一两个周期就可能错过边缘。半圆内更需要保留原始偏差响应,直线则可以适度滤波。项目中采用原始值与滤波值混合,并对转向输出单独限速。
多个通道同时为黑可能表示宽线、启停线、弯道横向覆盖或传感器高度变化,不能简单等同于"十字路口"。有效通道数量、位图形状、持续时间、运行相位和里程应联合判断。全白表示暂时丢线、地图断线或传感器故障,也不能第一拍就停机。
3. 直线控制为什么要温和
直线的目标是让车头方向与黑线平行,而不是每次看到一点偏差就把车身快速横移。小车在直线上有前进速度,任何差速都会积累偏航角。P 过大时,边缘通道触发后车头迅速转向,等中心探头重新看到黑线时角速度仍然存在,车辆会穿过中心。
因此直线采用低 P、适量 D/角速度阻尼、小转向上限和较慢转向斜坡。中心 X4/X5 可设死区,避免传感器量化在中心两侧抖动;X2/X3 或 X6/X7 则逐级增加修正;X1/X8 最外侧需要更强救线,但仍要限幅,防止一次拉回直接越过另一侧。出弯恢复窗口的边缘救线与普通直线不同,应同时参考残余 yaw 和 gyroZ。
直线全白时,若最后有效线在左侧,应向左搜索;在右侧则向右搜索。搜索动作要比半圆正常控制保守,前进速度和半差速不能太大。重新检测到线后清除旧积分、微分和丢线方向历史,避免把搜索时的大误差带入正常 PID。
4. 半圆控制为什么需要前馈
BC/DA 是半径约 0.5 m 的半圆。若只用灰度误差反馈,车辆必须先偏离黑线才产生差速,速度越快越容易向圆外冲。几何前馈可以根据前进速度和目标曲率提前给出左右速度差。理想差速近似与 v·B/R 成正比,其中 v 为车速、B 为轮距、R 为半径。
前馈只提供维持圆弧的基础转向,灰度 PID 负责修正实际偏差,MPU 角速度/目标航向负责抑制过转或欠转。半圆内允许更高 P 和更大转向上限,但输出仍要斜坡,避免入弯第一拍从直线 0 差速跳到完整圆弧差速。
弯道中 X6、X7、X8(以实际转向方向为例)应逐级增强救线。最外侧通道代表车辆已经接近冲出圆外,调控力度必须大于内侧通道,但不能简单把全局 P 提高,因为全局 P 会同时破坏直线。正确做法是弯道状态下使用独立增益、独立上限和边缘增益表。
5. 直线和半圆如何可靠区分
仅靠编码器里程判断相位,会受到轮径、打滑、左右计数差异和地图尺寸误差影响。项目早期使用错误地图,并把旧地图里程按比例映射到正式地图,导致进入半圆晚、退出半圆晚:第一段弯道已经开始时仍用直线参数,第二段直线开始时还在用弯道参数。结果就是入弯靠全白搜索、出弯后大幅摆动。
可靠方法是"计划相位+传感器证据+IMU 角度+滞回"。编码器给出大致区间,灰度边缘和持续偏差表明真实曲线,MPU 同向角速度确认车体正在转弯,累计航向接近 180°提示即将出弯。进入条件和退出条件应不同,避免在边界来回切换。计划相位不能因短暂中心灰度或低角速度立即关闭圆弧,也不能在真实直线中只因一次边缘触发升级为半圆。
S3 第一段直线还采用运行时直线锁:前约 140 cm 保持起步航向和直线参数,防止编码器提前进入 BC 相位;随后解除锁,让正常半圆控制在 150 cm 切点前接管。若里程无效,则用约 7 s 作为释放兜底,但不自动停车。
6. 入弯与出弯的过渡
高速 S1 在进入半圆前需要提前降低前进目标,使车体在弯道入口前完成减速;但不能提前施加圆弧差速,否则直线末端会摆动。项目采用"提前减 forward、不提前转向"的策略。到达实际弯道证据后,再用转向斜坡建立前馈和反馈。
出弯更困难。车辆在累计转过接近 180°时,黑线可能已经回到中心,但车头仍有角度误差或角速度。若立即切回普通直线,车辆会沿错误切线冲向边缘。出弯恢复需要保留受限的航向修正,等中线稳定、角速度下降并经过一定距离后再完全释放。对于边缘仍压线的情况,应暂时保留同向转向而不是立刻反向制动,否则会在"少转"和"过转"之间反复摆动。
7. 全白、重捕获和断线
全白状态要区分三种场景。第一,直线短暂丢线:按最后偏差方向低速搜索;第二,半圆冲出:保持半圆方向并适当增强差速,但设时间和速度上限;第三,地图有明确断线区:保持低速等速直行,不按旧偏差持续转向。重捕获时用当前位图重新初始化误差滤波,不继承丢线前的微分尖峰。
项目曾出现小车几乎完全依赖全白后的右转完成半圆。表面上能绕过一圈,实质上灰度循迹没有及时起作用,速度稍变就丢线。判定是否真正循迹,可以看半圆中大部分时间是否有有效位图、转向是否在有线状态建立、全白占比是否低。全白搜索只能救急,不能成为主控制律。
8. 启停线和终点停车
正式启停线约覆盖四个探头,不再是 8 路全黑。起点按 K1 时中间四路可能为黑,状态机要允许启动并在连续离开宽线后重新武装。终点检测则必须满足运行时间门、末圈/相位门和通道模式。项目还加入"启动后前 10 s 禁止正常终点停车",防止第一半圆前的四路黑或传感器毛刺导致提前停止。
终点命中后,要立刻把目标置零并选择主动刹车,不能继续执行一两个控制周期的旧 PWM。主动短刹保持有限时间后再释放,可缩短停车误差;时间过长会冲击齿轮和电源。S2 为连续带球循迹,按需求取消自动终点停止,只有 K1、显式命令或安全故障能停车。S3 也取消 B 点自动停车,避免编码器误差造成提前或延后停止。
9. S1、S2、S3 三种模式
S1 是不带滚球的高速整圈模式,目标是稳定满足 20 s 内一圈。它允许较快起步和更高直线速度,但半圆内部前进速度会受稳定性限幅,出弯和终点使用较强保护。S1 的高速并不是所有路段都同一个 forward,而是在不改变已验证弯道骨架的前提下提高直线效率。
S2 是带滚球的低扰动连续模式,直线与半圆速度更低,加速斜坡更缓,避免车体姿态突变。S2 不自动计圈停车,给摆杆主控连续稳定的运动环境。其成功标准不仅是跑完,还包括第二段直线不蛇形、两个半圆转向对称和滚球误差可控。
S3 用于第一段直线/入弯验证,当前采用约 27 cm/s,前 140 cm 保持直线锁,随后连续进入半圆。理论 A 到 B 时间约 6.23 s,为 8 s 指标留余量。S3 取消自动停车后,避免了"距离 B 还有 5~20 cm 提前停"和"减速后直接冲进半圆却不停"的不确定性,停止改由 K1 明确控制。
10. 往年循迹题经验怎样迁移
往年小车题常用固定赛道、十字、直角或 S 弯。本题的半圆曲率已知,更适合使用几何前馈和分段增益;长直线则更需要航向保持。直角题常采用原地转向和角度锁,本题半圆不应频繁原地转,否则速度和滚球都会受到冲击。S 弯题常用连续曲率估计,本题可简化为明确的直线/圆弧状态,但边界必须可靠。
通用经验仍然成立:先低速跑通、再增加速度;先保证方向和极性、再调 PID;终点识别要有重新武装;丢线恢复要保存方向;速度环比直接 PWM 更稳定。不同之处在于本题带有上层滚球对象,所有速度阶跃、急刹和车身振荡都会影响另一个控制系统,因此"能跑完"还不够,必须追求平滑和可预测。
11. 本篇结论
循迹稳定的核心不是一个神奇 PID,而是让每种赛道状态使用合适的模型:直线低增益和航向阻尼,半圆几何前馈和高差速,边界采用证据与滞回,丢线采用受限搜索,终点采用严格门控和主动停车。S1/S2/S3 把不同评分目标封装为模式,避免现场临时改常数。只要状态判定可靠,调参才会表现出一致性;状态判定错误时,继续放大 P 只会让问题更严重。
12. 赛道参数表和现场快速校准
正式调试前可建立一张赛道参数表,记录 AB/CD 直线长度、BC/DA 半径、启停线宽度、黑线宽度、传感器有效宽度和车辆起始位置。再用低速跑一圈记录编码器里程和 MPU 累计角度,把理论切点与实测事件对齐。相位边界不应直接用一次结果,而要统计多圈分布,选择既不提前进入又不明显滞后的门限,并给灰度/IMU 证据留接管空间。
现场更换地图或轮胎后,先跑 S3 或低速诊断模式,不要直接使用高速 S1。观察前 140~150 cm 的里程是否对应实际 B 点,半圆累计航向是否接近 180°,第二段直线开始时状态是否已经退出弯道。若仅里程整体成比例偏差,可以小范围校准每圈计数;若左右半圆偏差不同,应检查打滑、轮胎和机械,而不是用一个比例强行修正。
灰度安装高度也应纳入参数表。高度变化会改变黑线在探头上的覆盖宽度,使同一位置的 activeCount 不同,从而影响宽线和边缘判断。正式比赛前固定螺丝并标记高度,清洁探头和地图,避免调好的位图因灰尘或支架弯曲改变。一个可靠的循迹系统不仅有软件参数,还有被固定和记录的物理条件。
赛道状态机
#mermaid-svg-6cdffFhBJtnH8aG1{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-6cdffFhBJtnH8aG1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6cdffFhBJtnH8aG1 .error-icon{fill:#552222;}#mermaid-svg-6cdffFhBJtnH8aG1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6cdffFhBJtnH8aG1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6cdffFhBJtnH8aG1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 .marker.cross{stroke:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6cdffFhBJtnH8aG1 p{margin:0;}#mermaid-svg-6cdffFhBJtnH8aG1 defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-6cdffFhBJtnH8aG1 g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-6cdffFhBJtnH8aG1 g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-6cdffFhBJtnH8aG1 g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-6cdffFhBJtnH8aG1 g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-6cdffFhBJtnH8aG1 .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-6cdffFhBJtnH8aG1 .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6cdffFhBJtnH8aG1 .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-6cdffFhBJtnH8aG1 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-6cdffFhBJtnH8aG1 .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-6cdffFhBJtnH8aG1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6cdffFhBJtnH8aG1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6cdffFhBJtnH8aG1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6cdffFhBJtnH8aG1 .edgeLabel .label text{fill:#333;}#mermaid-svg-6cdffFhBJtnH8aG1 .label div .edgeLabel{color:#333;}#mermaid-svg-6cdffFhBJtnH8aG1 .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-6cdffFhBJtnH8aG1 .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-6cdffFhBJtnH8aG1 .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-6cdffFhBJtnH8aG1 .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 #statediagram-barbEnd{fill:#333333;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6cdffFhBJtnH8aG1 .cluster-label,#mermaid-svg-6cdffFhBJtnH8aG1 .nodeLabel{color:#131300;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-6cdffFhBJtnH8aG1 .note-edge{stroke-dasharray:5;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-note text{fill:black;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram-note .nodeLabel{color:black;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagram .edgeLabel{color:red;}#mermaid-svg-6cdffFhBJtnH8aG1 #dependencyStart,#mermaid-svg-6cdffFhBJtnH8aG1 #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-6cdffFhBJtnH8aG1 .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6cdffFhBJtnH8aG1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上电
中间四路全黑
缓速启动完成
灰度趋势+里程确认入弯
角速度接近180°
中线重新建立
任意四路全黑且已过最短里程
PWM=0
全白
全白
找回黑线
WAIT
START_LINE
STRAIGHT
CORNER
EXIT_RECOVER
FINISH
STOP
LOST_RECOVER
| 判据 | 原始条件 | 防误判措施 | 动作 |
|---|---|---|---|
| 启动线 | 中间四路全黑 | 只在 WAIT 接受 | 进入直线 |
| 入弯 | 偏差趋势、角速度、编码器里程 | 连续多帧确认 | 提高弯道差速 |
| 全白 | 8 路均为白 | 记住最后有效方向 | 低速搜线 |
| 终点 | 任意四路全黑 | 起步后最短里程/时间门槛 | 立即清零 PWM |
