2026年TI杯H题:完赛工程、技术方案与完整复盘

check_circle
完赛状态

比赛已经结束。唯一最终工程为C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2026-8-1,最终MSPM0固件已由参赛者手动烧录。本文是唯一Obsidian入口,技术方案、最终代码、关键接口、完整时间线、失败经验和原始记录均已合并在本页。

页面导航

info
阅读建议

只想了解作品:看第1~2章。需要复现系统:看第3章。要吸取调参经验:看第4~5章。第6章是保留的原始现场记录,第7章汇总跨项目可复用经验。

信息边界

标记/位置 含义 使用方式
第 1~3 章 完赛工程、接口和任务结论 作为当前版本的复现主线
第 4~5 章 按时间和问题组织的实验复盘 用于理解为什么这样调参,以及哪些方案被放弃
第 6 章 原始现场记录 只用于追溯,不把其中的临时判断当成最终结论
第 7 章 可迁移的跨项目经验 迁移到新车/新板前,重新核对硬件和版本
warning
事实与推断

路径、哈希、参数、端口和“已完成”状态属于本次比赛的现场事实;“可能原因”“建议方案”和论文贡献属于分析或后续计划。复现时先以最终代码和验收结果为准,再参考时间线中的推断。

1 完赛最终代码与成果

check_circle
最终口径

2026年TI杯H题比赛已经结束。参赛者确认:最终MSPM0代码已经由本人手动烧录,上位机、MaixCAM、MSPM0和其余工程资料均保存在同一个最终文件夹中。因此本笔记是赛后归档,不再把工程描述成“等待继续烧录/等待下一轮调试”。

1. 唯一最终归档目录

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2026-8-1

目录包含:

  • 01_MSP主控:MSPM0G3507最终Keil工程、源代码、AXF、HEX和构建日志;
  • 02_MaixCAM视觉/当前部署:MaixCAM最终正式源代码和参数;
  • 03_机械结构:DCC、步进电机、接线和摇臂摆杆资料;
  • 04_设计文档与调参记录:调参轨迹、日志和版本过程;
  • 05_项目交接:交接手册和资料索引;
  • 06_RDKX5上位机:已展开的上位机/RDK X5比赛展示、录像和回放控制台源码;
  • 04_设计文档与调参记录/题目与设计报告:两份H题PDF及完善版设计资料;
  • 05_项目交接/交付压缩包:MaixCAM和RDK X5交付ZIP。

早期的备份-lora-before、7月30版、双车参考和2025压缩包已统一移到上一层90_历史归档,只用于追溯,不再代表完赛代码。

2. MSPM0最终代码

2.1 最终构建与烧录状态

  • Keil工程:01_MSP主控/project/Keil/EmbedBolt316.uvprojx;
  • 最终AXF时间:2026-08-01 17:13:14;
  • 最终HEX时间:2026-08-01 17:13:15;
  • 最终构建日志:0 Error(s), 0 Warning(s);
  • 最终固件已由参赛者在比赛阶段手动烧录。

最终文件哈希:

EmbedBolt316.axf
SHA-256 4D84FA04098BD6C4F8EAABA2D8402806C376DEF5424CFEF5CB2EC2E9200D1340

EmbedBolt316.hex
SHA-256 5B492D8C0C08CB97949B261415E53160EAF85EAC384765E63FE31388C062E671

2.2 最终车辆参数

Base2直线速度                  45 cm/s
Base2弯道速度                  35 cm/s
入弯前20 cm平滑降速

Base4物理目标                  150 cm
Base4编码器校正                ×1.25
Base4最终编码器目标            187.5 cm
Base4巡航速度                  28 cm/s
Base4起步/末速                 8 / 10 cm/s
Base4起步斜坡                  900 ms
Base4末段减速距离              20 cm

Task5/6巡航速度                27 cm/s
Task5/6起步斜坡                1500 ms
第二弯门控                     18 s后启用
入弯/接近出弯/出弯阈值         18 / 14 / 8 deg/s
A点宽黑阈值                    5路
里程兜底                       660 cm
640~660 cm末段速度            12→7.5 cm/s

最终代码还保留了完成/超时后向Maix发送CAR:CRUISE的兼容逻辑:车辆已经停止,但视觉闭环继续把球拉回保存目标,不因旧CAR:HOLD语义冻结。

GRAY_SENSOR_GANV_NO_MCU与GRAY_SENSOR_GANV_MCU两个旧灰度分支在最终MSP源码中均已不存在。

3. MaixCAM最终代码

正式入口:02_MaixCAM视觉/当前部署。

3.1 最终身份

app.yaml应用版本               1.5.0
config控制版本                 TASK6-CAPTURE10-SAFE-PARK-20260801
标定版本                       3PT-20260731-E
视觉中心                       230 px
DCC机械中位                    15241
相机配置                       480×480,80 FPS传感器模式

最终文件哈希:

main.py
SHA-256 B4B23B3776F785E460ABF0EE53CF141F1129D0E317A7896C2A6E641E9E0BE80F

config.json
SHA-256 A69ED24D9355461AA67543C184AB5B26A2FDDEA0EB74DCA291A02BB1BE0E3311

app.yaml
SHA-256 BF9AE7431A45E95F036CE078EF572ACD368ADD4EB0E573BE0818159F4A23DB8F

3.2 最终功能

  • TASK:1、TASK:2为图传任务;
  • TASK:3、TASK:4、TASK:5、TASK:6均进入滚球控制逻辑;
  • Task3完成O → +5 cm → -5 cm并在负端保持;
  • Task4在车辆A到B过程中保持O点;
  • Task5采用视觉位置/速度PD、MSP陀螺仪前馈、1500 ms缓启动和直线受限破静摩擦;
  • Task6启动后等待用户手动摆球稳定,捕获当前位置作为任意目标,再整圈保持;
  • Task6停车保持窗为约±0.65 cm且速度小于0.5 cm/s,停车恢复最大角度5°;
  • YOLO用于首次定位与重捕,局部高速跟踪用于连续控制;
  • DCC机械回中使用真实编码器相位15241,不依赖旧绝对0°。

六项任务分别使用RTSP端口5001~5006。Maix应用仍采用设备屏幕手动启动,不设置上电自启动,这是最终稳定性取舍,不是未完成功能。

4. 上位机/RDK X5最终代码

正式展开目录:06_RDKX5上位机。原始ZIP已移动到05_项目交接/交付压缩包/RDKX5存储回放功能_完赛归档.zip。

SHA-256 905FC6DA8745A1546850B8EB59EC577C6D5872FF55574E4B9D82A52ADE5AE7C9

正式控制台部署目标为:

/home/sunrise/rdk_evaluation

关键组成:

  • evaluation_console/evaluation_gui.py:1024×600触摸GUI;
  • evaluation_console/rdk_evaluation.sh:六任务显示、录像、状态和回放控制;
  • evaluation_console/config/tasks.conf:Task1~6与5001~5006端口映射;
  • rdk_media.sh:录像索引、容量保护、断流重连和历史回放;
  • desktop/:桌面入口;
  • tests/:静态检查、GUI、录像监督、自动巡检和触摸退出测试。

控制台最终能力:

  1. 用户点击“开始显示并录像”后,持续巡检5001~5006;
  2. 自动发现有效H.264 RTSP任务并全屏显示;
  3. 按任务和全局场次号保存录像;
  4. 每60秒分段,断流后安全保存并尝试重连;
  5. 剩余空间低于10 GB时阻止继续录像;
  6. 支持停止并保存、播放本题最新录像、打开录像目录;
  7. 退出控制台时先停止录像和画面,再安全退出;
  8. 多路同时在线时按固定端口优先级选择并提示。

上位机任务说明中Task6已经标为“循线一圈并任意位置稳定”,与最终Maix Task6捕获逻辑一致。

5. 完赛结果应如何表述

代码层面已经形成完整三端系统:

MSPM0 OLED与车辆状态机
        ↓ UART任务、车辆阶段、陀螺仪
MaixCAM视觉与步进滚球闭环
        ↓ 分题H.264 RTSP
RDK X5显示、录像、索引与回放控制台

调试期间保存的7.036 s、GYROFIX3 RMS 1.61 cm等数据是某些已记录测试轮次,不应自动冒充最终比赛成绩;最终赛场得分、用时和裁判结果若没有单独记录,就保持“未在本地资料中找到”,不再写成待完成事项。

6. 赛后结论

工程已完赛并封存。后续阅读这些资料的目的不是继续赶比赛版本,而是复盘:

  • 如何在高静摩擦、长执行延迟和不可靠GUI资源下完成可工作的闭环系统;
  • 为什么真实编码器相位和可重复初始条件比继续堆PID更重要;
  • 为什么Task5的FIX3能够靠量化对比收束,而第三题早期连续15版会越调越差;
  • 为什么本地源码、烧录固件、设备端应用和上位机必须分别建立版本身份;
  • 如何在比赛结束后用哈希和唯一目录保存可复现的最终成果。

2 技术方案与接口

1. 视觉坐标标定

设水管在图像中的两个端点为:

$$ P_0=(u_0,v_0),\qquad P_1=(u_1,v_1) $$

检测到的钢球中心为 \(P=(u,v)\),水管方向向量为:

$$ \mathbf d=P_1-P_0 $$

将球心投影到水管轴线上:

$$ \lambda=\frac{(P-P_0)\cdot\mathbf d}{\mathbf d\cdot\mathbf d} $$

再换算为以中心 O 为零点的实际位移:

$$ s=(\lambda-\lambda_O)L $$

其中 \(L=0.25\,\mathrm{m}\),\(s=0\) 为中心,\(s=+0.05\,\mathrm{m}\) 和 \(s=-0.05\,\mathrm{m}\) 分别对应 +5 cm 与 -5 cm。

Pasted image 20260729135312.png

2. 视觉检测方案

方案 算法耗时 结论
YOLO 41.2 ms 适合首次定位或低频纠正
局部模板匹配 67.8 ms 过慢,淘汰
80×60 自适应 LAB 色块 7.4 ms 当前实时控制主方案

LAB 方案包含取帧后的总周期约 18.9 ms,约 53 fps;静止中心抖动约 0.13 px。

3. 位置与速度控制

  • MaixCAM 输出球的位置 \(x\),连续帧差分并滤波得到速度 \(v\)。
  • 位置环根据剩余距离生成目标速度:
$$ v_{target}=\operatorname{sign}(e)\sqrt{2a|e|} $$
  • 速度环根据 \(v_{actual}-v_{target}\) 计算摆杆目标角度。
  • DCC 驱动器执行并保持步进电机角度。

第三题状态机

  1. CENTER:确认球在 O 点附近且速度足够低。
  2. TO_POSITIVE:使用一次受控强起动克服静摩擦,向 +5 cm 运动。
  3. TO_NEGATIVE:提前反向制动,向 -5 cm 运动;禁止负端二次强起动。
  4. HOLD_NEGATIVE:首次进入 -5 ± 0.8 cm 且 |v|≤2 cm/s 时立即调平。
  5. DONE:水平保持 180 ms 且仍在 -5 ± 1 cm 后完成;漂出则恢复普通控制。

第五题融合控制

  • 输入:视觉位置/速度 + MPU6050 角速度。
  • 启动车辆前,MaixCAM 先确认球在中心稳定,再回复 BALL:STARTED。
  • 保留约 1500 ms 缓启动。
  • 陀螺仪前馈不能固定方向硬叠加:与视觉纠偏同向时全量使用,反向时衰减或关闭。
  • 直线静摩擦短脉冲只允许在直线、低速且 |误差| > 0.8 cm 时触发;建议从 5.5°/260 ms 开始复测。

4. 电机、编码器与接口

  • 编码器相关寄存器范围:0x40~0x47;记录中重点读取地址 0x42,最终以拨码表确认。
  • 0x11:相对整角控制,当前可靠方案。
  • 首次修正最多 5° ≈ 228 脉冲,避免拆成无效微步。
  • 当前水平目标相位:15241;闭环实测 15244。
  • 回中流程:读取真实编码器相位 → 0x11 相对修正 → 再读取相位验收。
  • 不再使用存在问题的 0x13 或软重启探测。

5. 软件与启动安全

/etc/init.d/S06maixapp 曾在 comm method=none 时仍初始化 UART0,并向 /dev/serial0 写入 serial ready。UART0_TX 与 TB6612 的 PWMA 共用 A16,导致重启时可能出现电机脉冲。

已实施的保护:

  • comm method=none 时不再初始化 UART0,也不再写入 serial ready。
  • 原脚本备份:/etc/init.d/S06maixapp.pre-codex-20260730。
  • 相机使用单实例锁,生产模式关闭 RTSP 和屏幕显示。
  • YOLO 单缓冲;相机通道 buff_num=1。
  • 获取下一帧前显式释放当前 frame。
  • 控制器、模型和相机通道纳入统一清理。
  • 父进程异常时,电机子进程检测 EOF 并拉低输出。
Pasted image 20260730221554.png

3 系统交接与各任务结论

check_circle
赛后最终状态
  1. 比赛已经结束,唯一最终归档是C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2026-8-1。
  2. 参赛者确认最终MSPM0代码已在比赛阶段手动烧录;Base4最终为187.5编码器厘米/28 cm/s,不再沿用“板上仍为150/25”的调试期判断。
  3. 最终Maix正式目录为应用1.5.0,控制身份TASK6-CAPTURE10-SAFE-PARK-20260801,已经包含Task6任意位置捕获与停车保持逻辑。
  4. 上位机代码已展开到最终目录中的06_RDKX5上位机;交付ZIP保存在05_项目交接/交付压缩包,包含六任务RTSP显示、自动录像、断流保存、索引和回放控制台。
  5. 本文后续“未烧录、待验证、下一步”等句子是调试当时的历史状态,用于解释决策过程;最终版本以完赛最终代码与成果为准。

1. 记录范围与可信度

本笔记不是只保存“最后参数”,而是保存整个协作过程中影响判断的关键事实:用户现场现象、Codex读取到的XDS内存、Maix轨迹、代码变更、烧录结果、失败版本和回滚理由。

可信度顺序:

  1. 当前源码、构建日志、XDS内存和保存的JSON轨迹;
  2. 现场明确口述(例如“打到底”“停在3 cm”“只转左轮”);
  3. 根据运动现象作出的推断;
  4. 未完成实车复测的预计值。

所有“预计”“推断”均不得冒充比赛验收结果。

2. 硬件、接线与控制职责

2.1 设备

  • 主控:MSPM0G3507,Keil工程,XDS110下载/内存诊断。
  • 小车:左右两轮差速,8路灰度循迹,MPU6050/姿态数据用于弯道前馈和终点门控。
  • 视觉:MaixCAM Pro,摄像头侧装,识别钢球并计算沿25 cm管槽的一维位置。
  • 执行器:DCC-101步进驱动器、步进电机、摇臂连杆和PPR开槽摆杆。
  • 显示/操作:MSP OLED菜单;MaixCAM本机屏幕只负责手动启动应用与画面显示。
  • 网络:绿联网卡,历史设备IP 10.5.5.29;MaixVision曾正常连接。不要把电脑屏幕画面当摄像头数据源。
  • 调试:XDS110 Application/User UART曾枚举为COM7,Aux为COM8。

2.2 当前接线口径

接线文件:03_机械结构/步进电机和maixcam接线.txt。

Maix A18 -> 步进电机/DCC PWM
Maix A28 -> DCC RX
Maix A29 -> DCC TX
Maix A16 TX -> MSP RX
Maix A17 RX -> MSP TX

MSP与Maix是UART1/LwPKT字符串链路。旧MaixCamPro代码和旧接线曾被明确判定不可信,不能反向覆盖当前接线。

2.3 职责边界

  • MSP:OLED任务选择、小车循迹、里程、车辆启停、30秒/8秒计时、IMU、向Maix发送任务和车体状态。
  • Maix:球检测、位置/速度估计、滚球控制、DCC角度与保持、RTSP。
  • 摆杆由Maix控制,不由MSP直接输出步进角度。

3. OLED任务与题目映射

OLED 串口命令 代码任务 含义
Base 2 TASK:2 Requirement 2 整圈循迹并停A点
Base 3 TASK:3 Requirement 3 O→+5→-5并保持
Base 4 TASK:4 Requirement 4 A→B 150 cm并稳球
Play 1 TASK:5 Requirement 5 整圈并把球保持O点
Play 2 TASK:6 Requirement 6 用户先摆任意刻度,捕获该位置后整圈保持

重要纠正:不存在OLED“Base5”;第五题实际叫Play 1,第六题叫Play 2。XDS中枚举从Requirement 2=0开始,因此h_id=3是Task5,h_id=4才是Task6。

4. 总体时间线

4.1 工程接管、目录和版本辨认

  • 最初工程来自多个目录:备份-lora-before、队友交接包、ti-contest-2026-H、ti-contest-2026-8-1等。
  • 用户要求清理与当前目录无关的大量Git删除状态,随后要求按“MSP主控、Maix视觉、机械、设计/调参、项目交接”整理。
  • 曾对队友D版/Keil工程做差异检查,判断不能整包直接覆盖;主要迁移通信与任务功能到用户自己的MSP工程,保留用户循迹基线。
  • 最终整理后的正式目录为ti-contest-2026-8-1,顶层分为01_MSP主控、02_MaixCAM视觉、03_机械结构、04_设计文档与调参记录、05_项目交接。
  • 历史脚本和失败版本只用于追溯,正式入口是01_MSP主控和02_MaixCAM视觉/当前部署。

4.2 机械中位、堵转和编码器

  • 机械结构是步进电机摇臂带动摆杆。用户强调机械摆放本来就在中位,小球触底/落中能直接反映杆姿态。
  • 初期绝对零点错误导致电机上电打到最底并堵转。根因不是用户没摆中,而是软件绝对0°与真实机械中位不一致。
  • 旧记录中位先后出现15624、15337,均被后续标定淘汰。
  • 最终验证的水平目标相位为15241,读回15244,相差3脉冲,约0.066°。
  • DCC可靠命令是0x11相对整角;细分微步在该机构上多次实测“完全不动”,不能继续依赖微步。
  • 首次修正最多5°(约228脉冲),必须保留机械硬限,禁止直接下发未经反馈校验的绝对大角度。
  • 用户曾质疑电机未使能,因为轻推连杆即可移动;后续“始终使能”测试确认有明显保持力。因此应用启动后应使能保持,但不应盲目执行旧绝对回中。
  • 自动回中正确做法:读取真实编码器相位→0x11相对修正→再次读相位验收。不要用旧0x13或软重启探测。

4.3 MaixCAM启动、黑屏、死机与网络

  • MaixCAM经历多次黑屏、应用崩溃、设置界面卡死、网络不可达和重装系统。
  • 主要稳定性根因不是简单“内存不够”,而是相机MMF/VI/VPSS缓冲未释放、RTSP/显示/识别多实例争用以及底部刻度硬件叠加层在客户端重连时触发SIGSEGV(11)。
  • 做过的稳定性处理:单实例锁、相机buff_num=1、每帧显式释放、统一清理、生产模式减少显示/RTSP争用、父进程异常时电机子进程EOF安全处理。
  • 左上角退出触摸曾被怀疑误触,后来用户明确要求触摸改回;最终采用长按1.5秒以减少虚报,但不能把所有黑屏都归因于误触。
  • 开机自启动曾把系统和网络搞死。最终纪律:GUI只从Maix屏幕手动点开,禁止上电自启动,禁止用SSH启动GUI,禁止崩溃后循环强杀/重启。
  • /etc/init.d/S06maixapp曾在comm method=none时仍初始化UART0并发送serial ready。A16又与TB6612/PWMA链路冲突,造成重启瞬间电机抽动。修复方向是method=none时不初始化/不写UART0。
  • 用户后来明确选择手动点应用,宁可多一步,也不要自启风险。
  • 历史账号hwy / 4399ssjj在系统重装/恢复后不再被10.5.5.29 SSH接受;MaixVision仍可能连接,但不能据此假定SSH可用。

4.4 视觉方案从YOLO到混合跟踪

  • 用户重装系统后要求仍以YOLO为基础,但指出持续YOLO延迟大。
  • 讨论过香橙派3B+OV9281+OpenCV高帧率方案,但比赛时间不足,最终保留MaixCAM。
  • 最终视觉策略:YOLO用于首次定位/丢失重捕;局部LAB色块/轮廓用于连续控制。
  • 实测:YOLO约41.2 ms;模板匹配67.8 ms淘汰;80×60自适应LAB约7.4 ms;含取帧总周期约18.9 ms,约53 fps;静止抖动约0.13 px。
  • 曾期望60~80 fps,实际基线约53 fps。不能在报告里写成稳定300 fps。
  • 摄像头移动后必须重新标定。历史出现画面蓝线/中心与真实小球相差约2 cm,12次球心像素中位数240 px但物理约+2 cm。
  • 256×192 RTSP底部刻度仅为辅助:-12.5→31、-5→85、0→121、+5→157、+12.5→211。相机位置变化后该静态刻度立即失效。

5. 第三题(Base3)完整调参结论

5.1 目标与控制结构

  • 从O点出发,先到+5 cm,再到-5 cm,最终必须稳定在-5±1 cm;不能任务完成后自动回正,只有OLED返回/STOP才回中。
  • 视觉提供位置和滤波速度;位置环生成目标速度,速度环/PD生成杆角;DCC执行并保持。
  • 静摩擦是核心难题:小角度/微步完全带不动,过大脉冲又会造成二次越界。

5.2 大量实测现象

  • 早期出现:电机动但球原地不动;微步没有任何效果;改用绝对/整角才有明显动作。
  • 多轮中出现停在3 cm、幅度越来越小、到点继续输出打到底、负方向根本没走、重复打两次浪费时间等问题。
  • 用户多次指出“没动就应该持续加”“需要安全限幅”“小角度打不动”,这些都被实测证实。
  • 多次连续启动/回中/按键用于建立轨迹;用户要求每轮记录时间点,后期因记录中断导致参数偏离此前较好方向。
  • 曾经第十轮看似最好,后来连续复测发现可能有运气成分,说明单轮成功不能作为模型。
  • 一轮理想捕获:2.96 s到-4.96 cm,速度-0.10 cm/s。失败是控制器没有在这一刻调平,继续PID并误触发反向强起动。

5.3 已确认规则

  • +5阶段允许一次受控强起动;-5阶段禁止二次强起动。
  • 首次进入-5±0.8 cm且速度≤2 cm/s立即调平。
  • 水平保持180 ms且仍在-5±1 cm后才DONE;漂出才恢复普通控制。
  • “到位置继续追”比“略慢一点”更危险,曾直接把球打到端点。
  • 最好完整结果:正向峰值+5.46 cm,最终约-4.9 cm,长期保持稳定,但用时7.036 s,未达到5 s。
  • 第一次刹车停在-1.87 cm导致第二次追赶,浪费约3 s;下一步应优化一次捕获,不应继续增加强起动幅度。

6. Base2整圈循迹

  • Base2是Requirement 2,Maix只提供图传,车辆循迹由MSP完成。
  • 用户多次指出“线还在中间却提前停止”,否定了“宽黑误识别”的草率判断。
  • 起终点宽黑阈值最终坚持5路,用户明确拒绝降到4路,因为弯道可能误识别。当前H_STOP_MARK_MIN_CHANNELS=5,确认样本数1。
  • Base2与Task5/6速度不同,同样的物理赛道不能盲目共用同一个编码器里程常数;速度、打滑和负载会改变里程比例。
  • 最后一次对话中Base2出现“只转左轮”。此前Base4新固件所有烧录均在DAP连接阶段失败,没有写入,因此不能认为是新距离参数破坏Base2。
  • 排查优先级:右电机驱动/供电/使能/接线;右PWM和方向脚;灰度是否持续给出极限偏差;电机映射;板上固件状态。不要先改循迹PID掩盖硬件问题。

7. 第四题(Base4)

7.1 题意与调试期原实现

  • A→B直线循迹,物理距离150 cm,同时保持小球在O点,时间≤8 s。
  • Codex远程调试阶段板上最后可读版本曾是:H_TASK4_TRACK_DISTANCE_CM=150、巡航25 cm/s、前20 cm降速、终点10 cm/s。
  • 代码给car_move_cm下发150+1 cm阈值补偿,理论在编码器150 cm处完成。

7.2 实测偏短、远程烧录失败与赛后最终状态

  • 用户实测只跑约120 cm赛道距离。
  • 按比例150/120=1.25,本地源码加入Task4独立里程校正:编码器目标187.5 cm;不修改公共轮径,避免影响已收束的整圈任务。
  • 若仍用25 cm/s,按速度曲线估算约8.23 s,可能违反8秒。故本地源码把Base4巡航改为28 cm/s,预计约7.45 s。
  • 该版Keil全量编译0错误0警告,日志task4_distance1875_build.log。
  • UniFlash和Keil CMSIS-DAP都在连接阶段失败:DAP Connection Error / E310 Debug access failed。XDS110固件切DFU并恢复Standard只恢复了探针本体,目标仍拒绝连接。
  • 因此187.5/28在该次远程操作中没有烧入。比赛阶段参赛者随后手动烧录了最终固件;完赛目录中的最终AXF/HEX和源码均采用187.5/28。

8. 第五题(Play1 / Task5)

8.1 视觉+速度位置环+陀螺仪前馈

  • 不是单纯视觉位置环:Maix根据球位置和速度输出角度;MSP持续发送车体Z轴角速度CAR:YAW:n,Maix做前馈门控。
  • 起步前回中握手+1500 ms缓启动有效,起步峰值约从+2.98降至+1.56 cm。
  • 固定方向前馈不可用:第一弯可能抵消视觉,第二弯却可能有帮助。最终前馈只有与视觉纠偏方向一致时才全量使用,冲突时关闭/衰减。

8.2 版本结果

版本 位置范围 RMS 超±1 cm 结论
初始前馈 -2.54~+4.60 1.83 70.6% 极端振荡减小,第一弯不足
符号翻转 -6.02~+2.20 3.02 78.1% 明显变差,翻转错误
GYROFIX2 -3.11~+3.69 1.97 62.5% 恢复方向+受限脉冲
GYROFIX3 -3.14~+2.59 1.61 59.0% 综合最好,正式回退点

GYROFIX3保留:前馈门控、KD=0.9、直线5.5°/260 ms短脉冲、700 ms冷却、回中握手、缓启动。弯道脉冲FIX4综合变差,已删除并回退。

8.3 停车和整圈终点

  • Task5/6实际物理一圈约614.16 cm,但不同速度/负载下编码器里程不等价。
  • 早期619 cm左右兜底导致第二弯未完成就停车。XDS读到h_result=2、elapsed=24690、stop_mark=0,证明是里程误停,不是A点识别。
  • 去掉兜底后有一轮30.003 s超时且stop_mark=0,说明真实A点也可能漏检。
  • 最终加入第二弯门控:18 s后看到|yaw rate|≥18°/s标记入第二弯;回落≤14开始终点接近;≤8视为出弯完成。只有完成第二弯才允许A点和里程兜底。
  • A点仍优先使用5路宽黑+低转向率;兜底从614提高到660 cm。
  • Task5实测28.717 s完成、车辆停车位置合适、stop_mark=0,由660兜底;停车小震荡使球漂到-6 cm以上。
  • 为降低硬刹,在640~660 cm将车速从12线性降到7.5 cm/s。该版编译并成功烧录。

9. 第六题(Play2 / Task6)

9.1 需求理解的演变

  • 早期误把Task6设计成OLED选择Sign±与Position 1/2/3,担心动作队列上限。
  • 重新读题后确认不需要复杂菜单:用户先自动回中,再手动把球摆到任意实体刻度;启动Play2时Maix捕获当前球位置作为目标;车辆整圈过程中保持这个目标。
  • 因此删除Sign/Position菜单思路。Task6目标不是固定0点,也不能照搬Task5。
  • APP_TASK_CAPACITY=13并不是这一方案的瓶颈;最终任务表只有5个正式任务项。

9.2 关键故障

  • 一开始Task6直接报Prepare Ball First、摄像头卡死、自动回中偶发只使能不动作;分别涉及READY门控、相机资源、UART/DCC状态,不能混成一个问题。
  • Task6曾表现为手动摆到+2后开始有动作,随后一直向0点平衡,证明错误继承Task5固定O目标。
  • 修正为捕获启动时当前位置后,转弯偏差改善;但停车仍会打端点,A点还会多走接近半段直线。
  • 直接把宽黑阈值5路降到4路被用户否决并回退。
  • 只靠614/619里程可能少半圈;只靠真实宽黑又可能30秒超时。最终采用第二弯确认+A点优先+660兜底。

9.3 停车后必须继续回目标

  • Task6一轮XDS:h_id=4、h_result=2、elapsed=28166、stop_mark=0,车辆已由660兜底完成。
  • 用户指出“小球没有停回设定刻度”,并明确要求即使30秒超时也不能把球丢着不管。
  • 根因:旧Maix在CAR:HOLD后把停车视为终态,做一次350 ms制动后冻结,视觉环不再追目标。
  • 本地Maix正式源码已改:取消HOLD冻结;停车恢复角限制5°;进入目标±0.65 cm且速度<0.5 cm/s时调平。该本地修改因SSH认证失败未确认部署。
  • 为兼容设备旧程序,MSP结束逻辑已从CAR:HOLD改为冗余发送两次CAR:CRUISE。车辆已经由car_stop_and_reset()停止,CRUISE只让Maix继续纯视觉闭环,不会让车继续行驶,也不会像STOP一样清除Task6任意目标。
  • 该MSP兼容修正构建0错误0警告并成功烧录,是板上最后确认成功烧录的功能版本。

10. 通信与命令语义

命令 方向 语义/注意事项
CENTER MSP→Maix 机械回正+球回O,完成后BALL:CENTERED
TASK:2..6 MSP→Maix 同时选择对应视觉/RTSP任务
CAR:ACCEL MSP→Maix 起步前馈状态
CAR:CRUISE MSP→Maix 无纵向制动前馈;现在也用于停车后继续追目标
CAR:BRAKE MSP→Maix 固定负向制动前馈,不能过早持续发送,否则会把任意目标向负端推
CAR:HOLD MSP→Maix 旧设备版本会冻结Task6,不再作为Task5/6结束命令
STOP MSP→Maix 操作员退出任务,允许回中/清理;Task3/6任务内不能误发
CAR:YAW:n MSP→Maix 0.1 deg/s单位的角速度

11. XDS110诊断、地址与烧录

11.1 已验证工具链

  • Keil:C:\Users\12706\AppData\Local\Keil_v5\UV4\UV4.exe。
  • 工程:01_MSP主控/project/Keil/EmbedBolt316.uvprojx。
  • UniFlash:C:\ti\uniflash_9.5.0\dslite.bat。
  • XDS配置:C:\ti\codex_tools\xds110_mspm0g3507.ccxml。
  • 烧录脚本:C:\ti\codex_tools\flash_mspm0g3507.bat。
  • Task5/6诊断:01_MSP主控/tools/read_task6_post_xds110.js。

11.2 诊断脚本注意

脚本曾使用固定RAM地址。每次全量链接后地址可能变化,必须先从最新EmbedBolt316.map核对dbg_h_task_id、dbg_h_task_result、历史数组等符号,不能直接相信旧脚本。

11.3 探针与目标故障

  • XDS110曾出现UniFlash Error -260。xdsdfu -e显示3.0.0.41、NOserial、Runtime、Standard。
  • 通过xdsdfu -m进入DFU,再-c 1 -r恢复Standard,探针可恢复。
  • 2026-08-01后期目标出现CS_DAP_0 DAP Connection Error。重置XDS本体无效,Keil也报E310 Debug access failed。
  • 这说明问题在目标MCU DAP/低功耗/复位状态,不是AXF编译失败。需要按住板上RESET做connect-under-reset,或电源/BSL/安全恢复后再烧。
  • 失败发生在“Connecting”阶段,故不会半写入Base4 187.5版本。
  • 上述结论只对应当时那次连接。比赛阶段参赛者后来手动烧录最终固件,DAP故障不再是完赛状态。

12. 完赛参数快照

MSP

Task4 physical target       150.0 cm
Task4 final odo target      187.5 cm (1.25校正,已手动烧录)
Task4 final speed           28.0 cm/s(已手动烧录)
Task4 start/end             8.0 / 10.0 cm/s
Task4 ramp/brake            900 ms / 20 cm

Task5/6 cruise              27.0 cm/s
Task5/6 start ramp          1500 ms
Finish approach             12.0 cm/s
Fallback soft brake         640~660 cm, 12→7.5 cm/s
Fallback stop               660.0 cm
Second curve arm            18000 ms
Curve seen/approach/done    18 / 14 / 8 deg/s
A wide-black channels       5

Maix本地正式源码

Task5 dynamic KP/KD         0.7 / 0.9
Task5 yaw gain/max/deadband 0.035 / 2.5° / 4°/s
Task5 breakaway             5.5° / 260 ms / 700 ms

Task6 dynamic KP/KD         1.05 / 0.85
Task6 yaw gain/max/deadband 0.08 / 3.0° / 3°/s
Task6 moving max angle      10°
Task6 stopped recovery max  5°
Task6 settle window         ±0.65 cm, |v|<0.5 cm/s
Task6 hold breakaway        5° / 220 ms / 600 ms

13. 失败方案与禁止重复事项

  • 禁止用旧绝对0°命令找平,曾导致直接触底堵转。
  • 禁止把微步当主要执行方式;实测多次完全无动作。
  • 禁止应用/GUI上电自启动和崩溃循环重启;曾导致网络和相机资源一起失效。
  • 禁止多实例占用相机、RTSP、UART和显示。
  • 禁止把A点宽黑阈值从5降到4,弯道误识别风险已由用户明确否决。
  • 禁止只看单轮好运结果;第三题第十轮和多个“最好版”后来复测均暴露偶然性。
  • 禁止同时改视觉、强起动、刹车、保持环和赛道里程,否则无法归因。
  • 禁止用CAR:HOLD结束当前设备上的Task6;旧版本会冻结视觉环。
  • 禁止任务超时后放弃球控制;超时只停小车,Maix必须继续追保存目标。
  • 禁止把本地编译成功直接写成已烧录。Base4 187.5/28在远程调试时就是典型反例;赛后之所以改记为已烧录,是因为参赛者明确确认后来完成了手动烧录。

14. 若未来复现该系统的建议顺序

  1. 若再遇到“只转左轮”,应先分别验证左右电机、驱动与灰度输入,不要先改循迹PID。
  2. 若DAP再次拒绝连接,应优先使用connect-under-reset,并把“连接成功”和“烧录Verify成功”分别记录。
  3. Base4的1.25校正来自约120→150 cm实测比例;若重做车辆或更换轮胎,应重新做多轮直线标定。
  4. Task5/6的660兜底、640~660平滑降速和结束后CAR:CRUISE继续收敛是完赛代码的重要经验,不应随意删除。
  5. Maix重新部署时应先从设备屏幕退出应用、备份、语法检查、上传和哈希核对;仍不应通过SSH拉起GUI。
  6. 若重新测试Task6,应记录捕获目标、两弯峰值、停车位置/速度和重新进入±1 cm所需时间。
  7. 每次全量构建后应更新XDS诊断脚本地址,并保存map、构建日志、烧录输出和轨迹。

15. 对话中反复强调的现场原则

  • 用户能快速手摆球并立即开始,不要每轮都强制走完整软件重启流程。
  • 自动回中应由系统可靠完成,但任务运行后未经返回命令不能擅自回中。
  • 小角度不等于平滑;静摩擦区需要受控破静摩擦,运动后必须立即撤力。
  • 停车平滑要结合车辆速度曲线,而不是永远固定一个杆角。
  • 视觉问题的主因曾被纠正为延迟而非静态误差;因此需要高帧率局部跟踪和速度估计。
  • 用户要求持续读取和逐轮记录,不能在调参后半程停止看历史记录;每次改动都应回看已保存轨迹和失败经验。

4 实验复盘:从 7 月 30 日到完赛

本章按时间顺序记录架构确定、通信打通、机械回中、视觉切换、控制调参和最终对账。每个时间段都遵循“现象 → 判断 → 修改 → 结果”的叙事,便于复盘而不是重复阅读日志。

info
这份文档怎么读
  • 起点按现场指定,取 2026-07-30(星期四)21:44,中国标准时间。本机 Codex 主会话创建于 21:40:22,21:44:24 正式给出第三题架构方案。
  • 时间来自本机原始 JSONL 会话,不是凭记忆补写。
  • 正文不是逐句聊天记录,而是按“现象 → 操作 → 结果 → 判断 → 教训”整理。重复的“启动了/按了/读取”合并为测试轮次,改变技术判断的反馈保留准确时间。
  • “编译成功”“已部署”“已烧录”“实车验证”是四种不同状态,本文严格区分。
check_circle
赛后补记

比赛现已结束。参赛者确认最终MSPM0固件后来由本人手动烧录,MaixCAM最终应用和RDK X5上位机代码也都保存在ti-contest-2026-8-1。因此正文中“当时尚未烧入/仍需下一轮”的描述只表示对应时间点的历史事实,不代表工程现在未完赛。最终版本见完赛最终代码与成果。

0. 一页看懂整个过程

这几天最核心的矛盾不是某一个 PID 参数,而是五类问题交织:

  1. 机械基准不确定:软件角度 0° 一度不等于真实水平,导致上电打到底、轮间继承残余角、同一参数每轮初始条件不同。
  2. 执行器有强静摩擦与协议歧义:微步常常完全不动;0x13 绝对角在该 DCC 固件上会选择跨圈逼近,造成直接触底;真正可靠的是读真实相位后用 0x11 相对整角修正。
  3. 视觉不是只有“准不准”,更关键是延迟:持续 YOLO 约 25 fps,不足以处理高速滚球;换成“YOLO 首次定位 + 局部 LAB 跟踪”后达到约 53 fps,静态抖动 0.13 px。
  4. 状态机比单个增益更重要:到点后继续输出、换向仍受普通限步、停车进入 HOLD 后冻结或继续追打,都会让看似正确的局部控制变成全局失败。
  5. 实验管理失控会吞噬有效进展:7 月 31 日凌晨连续约 15 版叠加修改,后半程没有持续回看调参记录,导致偏离第 9~10 轮较好基线。之后以轨迹、版本标签、单变量修改和明确回退点收束,Task5 才稳定得到 GYROFIX3。

最终形成的可靠工程原则:

  • 每轮任务开始前都以编码器真实相位重建机械水平,不继承软件累计角。
  • 破静摩擦使用受控整角短脉冲;一旦检测到球运动,立即撤力或转入速度制动。
  • 到点时先处理执行器残余角和球速,再判定位置;“位置到了”不等于“可以 DONE”。
  • 车辆停车与滚球闭环分开:车可因完成或超时停止,球仍应继续回保存目标。
  • 一次只改一个机制;每轮必须保存配置哈希、轨迹、板上固件状态和现场观察。

1. 2026-07-30(星期四):架构确定、通信打通、相机与应用稳定性

21:44—21:52:第三题架构和 MSP 入口

21:44:24,方案正式定稿。 第三题要求被明确为 O → +5 cm → -5 cm并稳定、总时长不超过 5 s、两端误差不超过 1 cm。控制职责划分为:

  • MaixCAM Pro:视觉测位、位置/速度闭环、直接控制 DCC 步进机构;
  • MSPM0:OLED、按键、任务计时、发送 TASK:3、接收完成/失败;
  • MSP 不进入高速滚球内环,避免 20 ms 调度和串口延迟干扰视觉控制。

21:46:18,现场给出最高权限并纠正硬件事实。 XDS110 已接入;新接线文件是唯一可信依据,原 MaixCAM Pro 代码不可直接相信。这一条非常重要:后续代码必须从真实引脚和实测链路反推,不能从旧工程反推接线。

21:47:28,确认 UART1 接线。 MSP 侧 PB3/RX ← Maix A16/TX,PB2/TX → Maix A17/RX,115200 8N1,共地。A16/A17 在 Maix 侧实际是 UART0,系统启动日志和系统通信服务可能抢占。

21:50:43—21:52:37,MSP 第三题入口完成并烧录。 加入 Base3/Task3、完成/失败回包、5 s 超时与停车保护;Keil 全量构建 0 错误 0 警告,XDS110 烧录校验成功。此时只完成任务框架,没有允许电机自动动作。

经验: 先把“谁负责什么、命令从哪来、失败谁收口”定清楚,再调控制器。否则 Maix、MSP、底盘三个状态机会互相覆盖。

21:53—22:21:网络恢复、双向 UART 实测、摄像头底层故障

21:53:46—21:54:53,绿联网卡恢复。 Windows 中 UGREEN 网卡此前只是 Disconnected,不是设备消失;重新连接 hwy 后定位 MaixCAM IP 为 10.5.5.29。

21:57—22:02,第一次部署即遇到 SIGSEGV。 新程序按 A16/A17/A18 接线重写,关闭系统自带 UART 协议,并先保持 PWM 禁用。相机/模型连续崩溃,最初怀疑 INT8 模型兼容和 VPSS 残留;继续分段测试后发现连官方相机程序也崩溃,因此不能把问题归罪于新算法。

22:06:50,职责再次由现场确认:杆子由 MaixCAM Pro 控制。 这排除了“让 MSP 直接输出步进角度”的岔路。

22:10—22:15,通信链路用无动作方法验收。 先只发 PONG 和 M0:BOOT:

  • Maix A16 → MSP PB3 收包成功;
  • MSP PB2 → Maix A17 收包成功;
  • A18 未使能,测试不涉及机械风险。

22:21:19,当时的结论是相机 MIPI 0 FPS。 GC4653 能枚举但无图,最高概率是排线/运行环境。这个判断后来由重装系统使相机恢复,说明“工程代码不是唯一变量”是正确的。

经验: 串口、PWM、模型、相机必须拆开测。若一上来运行整套应用,SIGSEGV 会把四条链路混成一个“code 1”。

22:26—23:35:解释 code 1、重装恢复 YOLO、正确处理显示

22:26:28,澄清 code 1。 它只是进程异常退出,不是 Maix 相机硬件错误码。当时已见的直接原因包括路径错误和 CPython 不支持 time.sleep_ms();真正的相机问题另行诊断。

23:17:41,现场要求重装后仍用 YOLO。 重装后相机恢复 30 FPS;23:21:47 模型加载成功;23:22:17 完成 20 帧实拍,20/20 检到球,平均推理 39.5 ms,约 25 fps。

23:22:56,首次画面标定发现旧中心 240 px 明显偏右。 当时按画面有效杆长估算先改为 0 / 212.5 / 425 px,但这个只是初始视觉标定,摄像头后来移动后必须重新做。

23:23—23:28,完整程序卡在显示和内存。 YOLO 单测能跑,整套程序却 OOM/卡在 display.Display()。根因不是“Maix 一定不能显示”,而是后台脚本和 Launcher 同时抢显示与多媒体资源。

23:28:13,现场追问“一定不能显示吗”;23:28:21 给出正确答案:可以显示,但要按正式应用方式独占。 随后打包为 Launcher 应用,而不是 nohup 后台抢屏。

23:34:33—23:35:31,正式应用成功。 应用由 Launcher 启动,相机持续 60 FPS,屏幕显示 YOLO 框、O 点、±5 cm、位置与角度;只有收到 Task3 才允许 A18 动作。

经验: 嵌入式 Linux 上“算法可运行”和“完整 GUI 应用可稳定运行”是两件事。资源所有权必须唯一:相机、显示、RTSP、UART 都不能多实例争用。

23:42—23:59:首轮机构测试暴露死区与使能问题

23:44:40,应用驻留 60 s 通过。 帧计数从 667 增至 5319,无自行退出。

23:45:15,小幅 A18 测试: 中心 → +0.5° → -0.5° → 中心,程序正常退出,但肉眼动作不明显。

23:50—23:52,XDS 恢复后加入 SRAM 调试邮箱。 因 XDS 虚拟串口不是当前工程控制台通道,改为 XDS 写 RAM 值 3,仍走正式 Task3 路径。该固件编译并烧录成功。

23:53—23:58,机构与 DCC 通信逐项验证。 图纸估算表明 ±5° 属安全区;+2°、+5°短测球几乎不动。A28/A29 与 DCC 115200 UART 回包正常,因此接线、供电和通信在线。怀疑驱动器处于休眠/释放力矩,准备唤醒时又遇到相机内存资源未释放。

经验: “驱动器回 ACK”只证明命令被接收,不证明机械产生了足够角度和力;必须同时记录编码器相位、连杆动作和球位移。


2. 2026-07-31 凌晨:从触底堵转到可靠机械中位

00:06—01:27:第一次严重机械事故与系统恢复

00:06:07,现场报告步进电机堵转并直接打到最底。 这是整个调试最重要的安全事件。随后现场反复强调:轨道原本就在机械中位,小球能在中间本身就是证据,不能把错误归为“用户没摆中”。

问题链如下:

  1. 软件把当时保存的绝对原点当成真实机械水平;
  2. DCC 绝对位置/逼近方向的语义未经该固件实测;
  3. 任务失败或进程退出时,休眠命令可能只发一次且未校验;
  4. 电机仍保持上一次底部目标,人工轻推与软件状态不一致。

00:16:28,现场质疑是否真的使能。 轻轻发力能带动连杆说明保持力不足或电机处于 Sleep。后来“始终使能”试验出现明显保持力,证明使能状态必须由 DCC ACK 和实际保持力共同确认。

00:21:25,MaixVision 对 10.5.5.29 连接成功。 网络恢复不代表相机应用、SSH和GUI状态都正常;它只证明这一条连接链路可用。

00:37—01:27,系统进入资源占用/卡死循环。 出现黑屏、设置卡死、Type-C、断电重启、桌面能起但进入设置卡死、恢复备份、插拔相机等。01:26:43 现场判断“是不是上电自启动”,随后决定手动点应用。

最终纪律:

  • 禁止 GUI 应用开机自启动;
  • 禁止崩溃后循环拉起;
  • 禁止用 SSH 后台启动需要显示的 GUI;
  • 比赛时由 Launcher 手动启动,宁可多一步,不把系统拖死。

01:32—01:59:释放力矩、重设原点、绝对角模式暴露歧义

01:36:28,电机仍自动回到底且人工无法回正。 01:37:53 通过 DCC 0x19 收到 Motor Sleep 后才允许手动回中;正式代码随即改成最多重试三次并必须收到 Sleep ACK。

01:46—01:58,Base3 入口和方向测试。 Base3 确认对应 Task3。+0.5°/-0.5°测试却都可能让球走向画面左端,暴露 0x13 绝对角的“逼近方向/跨圈”语义不可信。

01:55:12,早期保存原点 15624。 这是阶段性值,后续已被 15337 和最终 15241 淘汰,不能再使用。

01:57:29,决定放弃 0x13 双向闭环。 改用 0x11 相对控制,避免绝对角绕行。

教训: 对闭环步进驱动器,命令名“绝对角”并不保证机械会走最短路径。未验证跨 0/环形编码器行为前,绝对目标可能比相对控制更危险。


3. 2026-07-31 02:00—05:54:第三题高强度迭代与失控复盘

02:00—02:30:微步完全无效,转向整角和静摩擦建模

02:01—02:30 连续多轮“启动—按键—观察”得到一致现场证据:

  • 02:14:13:没动时闭环应该继续加,但必须有安全限幅;
  • 02:16:21、02:18:50、02:20:31:微步完全没反应,明显不如整角;
  • 02:24:42:要求始终使能;02:25:41 实测出现明显保持力;
  • 02:29 左右仍无动作,说明“已使能”不等于“已克服静摩擦”。

02:16:39,识别出微步失败机制。 将 3°拆成六次 0.5°/23 脉冲时,每次命令都落在机构死区内,物理位置没有累积,软件却以为目标在累积。改为一次下发完整整角,同时保留累计硬限。

02:29:29,编码器证明位置环在动。 电机从 15272 走到 15135,正好 -137 脉冲约 -3°,但球仍未动;说明主要矛盾是机械静摩擦,而非视觉或串口。

02:30—03:18:方向、角度阈值和起动脉冲

02:30:54,首次观察到球明显运动但方向相反。 球从 +0.47 cm 到 -1.20 cm,方向看门狗及时触发 Sleep。

02:44:54,绝对角直接跨圈打到底。 DCC 目标跳到 31963,确认 0x13 不可安全用于该机构的双向闭环;02:45:31 立即锁定应用、禁止校准状态下使能。

02:46:29,切到 0x11 相对整角。 当时重设中位 15555,首次最多 5°≈228 脉冲,累计硬限 ±5°。

02:47—02:56,逐级识别静摩擦阈值。 +5°、+8°仍不足;总偏角约 +20°开始快速向左;负向约 -22°可让球快速向右。由此得到:有效破静摩擦阈值约在 10°~22°之间,并且正负方向不对称。

02:56:25,形成“起动脉冲”思想。 不让 PID 长时间顶在 -22°,而是短时打破静摩擦,检测到位移/速度后立即回到正常控制角。

02:58—03:18,连续调持续时间、检测阈值和再起动。 180 ms 太短;600 ms可起动但有冲端风险;随后加入位移 0.25 cm 或速度 1 cm/s 立即释放、再起动冷却、反向制动门控。03:16 仍出现当前机构位置下 550 ms不足。

经验: 小角度不等于平滑。对有静摩擦的机构,正确的“平滑”是有上限的破静摩擦脉冲 + 运动检测释放 + 速度制动,而不是永远只发 1°~3°。

03:18—03:44:恢复正式第三题,第一次接近成功

03:18:48,现场明确拒绝延长时间。 不能通过把控制超时改成 15 s 绕开题目。03:19:30 要求直接做完整 +5/-5。

03:22:00,正式双阶段版本部署。 正反向均加入破静摩擦起动和对称制动,Maix 控制超时回到 4.9 s。

03:33:43,现场再次指出“电机动了但没克服静摩擦”。 这证明控制必须记录球是否真正动,而不能只看电机 ACK。

03:34:25,首次明确要求建立调参记录。 后续每轮本应记录时间、参数和结果。

03:42:09,摄像头被移动,必须重新标定。 这是后面物理刻度与画面中心偏差的重要来源。

03:44:08,一轮已经在 2.96 s 到达 -4.96 cm,速度 -0.10 cm/s。 这是理想捕获时刻;失败原因不是到不了,而是控制器没切到水平保持,继续 PID 并误触发反向强起动。随后确立:

  • -5 阶段禁止二次强起动;
  • 首次进入 -5±0.8 cm 且速度≤2 cm/s立即调平;
  • 水平保持 180 ms 且仍在 -5±1 cm才 DONE。

03:45—05:05:到点、残余角、启动抽动与记录中断

03:48:20,退出键退不出去;03:50:23,应用启动时电机会抽一下。 后来追到系统启动脚本在 comm method=none 时仍初始化 UART0/A16 并输出 serial ready,与其他电机链路复用产生瞬时扰动。结论是应用启动应保持使能,但不能盲目执行旧原点;系统 UART 服务也必须真正关闭。

04:25—04:29,应用启动使能要求被再次确认。 不使能时机构容易受外界扰动;使能只负责保持,不应自动往旧角度抽动。

04:33:39,一轮正向峰值降到 +6.60 cm,但负向过冲到约 -9.39 cm。 说明正向起动改善,负向阻尼不足。

04:44:58,发现 HOLD 进入时执行器仍残留 -10°。 位置已到 -4.99 cm、速度也不大,但杆未归平,随后继续加速。由此引入 UNWIND:先卸掉实际残余角,再进入 HOLD。

04:58:30,现场询问当前是不是视觉闭环和位置/速度环。 答案是肯定的:视觉位置和估计速度决定杆角;问题不在“有没有闭环”,而在执行延迟、静摩擦和状态切换。

05:05:01—05:05:08,流程性失败被现场直接指出:4 点以后没有继续记录,也没有回看《调参记录.md》。 这是随后越调越差的主要管理原因。

05:05—05:54:连续叠加修改导致退化,明确回退

05:10:11,回退后主轨迹恢复。 球到 +8.18 cm,负向最低 -6.26 cm,并在 2.78 s 到 -5.46 cm、+0.90 cm/s、实际角 0°。剩余问题是机械延迟导致 HOLD 后速度继续增长。

之后继续叠加入口脉冲、自适应 UNWIND、扩大窗口等机制。局部都有理由,但组合后不可预测。

现场连续反馈:

  • 05:14:17、05:16:42:最终都卡在 3 cm;
  • 05:19:19:越来越差,偏离原来较好方向;
  • 05:29:13:无法继续接受这种退化;
  • 05:29:48:已改约 15 版但没有实质进展。

05:19:40,正式承认偏离并回到第 10 轮简单基线。 删除叠加的 UNWIND、自适应释放、宽捕获和入口脉冲,只保留主轨迹、硬保护和日志。

05:37—05:45,尝试用多轮轨迹辨识运动模型。 五轮完整任务拟合得到总等效延迟约 260~400 ms,正向峰值速度 12.2~15.2 cm/s、负向 21.5~29.9 cm/s;但因输入本身来自闭环,二阶拟合出现非物理“负阻尼”,残差约 9.5 cm/s² RMS。结论:闭环轨迹不能直接当开环模型,需要固定角度短脉冲数据。

05:48:44,现场先判断视觉误差大;05:49:26 随即纠正为视觉延迟大。 这个纠正改变了方案方向:不是继续做静态标定微调,而是提高控制帧率。

教训: 连续改很多版并不等于信息增益。若没有版本标签、单变量和回退点,更多试验只会让控制器更复杂、现场更疲惫。


4. 2026-07-31 05:50—10:52:视觉换轨、物理相位回中与第三题收束

05:50—06:03:从持续 YOLO 切到混合视觉

05:50—05:54,讨论传统视觉高帧率。 香橙派3B+OV9281 可作为 150~200 fps 路线,但比赛时间不足,最终保留 MaixCAM。

Maix 实测基准:

  • 480×480 纯取帧:60 fps;
  • 全局霍夫圆:1.78 fps,淘汰;
  • 100×70 局部霍夫:33.5 fps,且误抓车轮边缘;
  • 模板匹配:67.8 ms,淘汰;
  • YOLO 首次定位:41.2 ms;
  • 80×60 自适应 LAB:算法 7.4 ms;
  • 含取帧总周期 18.9 ms,约 53 fps;
  • 静止抖动 0.13 px,120 帧成功率 100%。

06:02:27—06:03:26,混合视觉接入正式 Task3。 YOLO 只负责首次定位/丢失重捕;局部 LAB 跟踪进入每帧控制;显示降到约 18 fps,与控制解耦;电机参数保持第 10 轮基线不动。

06:03—06:55:发现真正的机械基准问题

06:05:05,现场发现响应快但根本没往负方向控制。 说明换视觉只降低延迟,不能自动修复方向和状态机。

06:11:49—06:14:32,现场提出关键改进:不应每轮人工摆轨道,步进电机有编码器,应固定到约 15000 多的中位。 这是整个系统稳定性的转折点。

随后通过 DCC 真实编码器相位确认:

  • 早期软件累计角与物理相位不同步;
  • 任务结束可能还残留 7°;
  • 主程序声称 -28°,实际并没有稳定保持对应物理倾角。

06:43:35—06:44:57,最终机械中位确定。 用 0x12 读真实单圈相位,以 15241 为水平目标,按最短差值用 0x11 相对修正,再次读取验收;最终 15244,误差 3 脉冲,约 0.066°,并保持使能。

这套流程取代:

  • 旧原点 15624/15337;
  • 软件累计角回中;
  • 0x13 绝对角;
  • 软重启探测。

06:50:02,独立硬件验证。 实际相对到至少 -21°后,球从中部滚到 +13.76 cm端点,证明电机、连杆和方向正常;第三题此前失败主要是软件状态与真实相位不同步。

06:55—07:45:换向、提前制动与轮间继承

06:54:55,启动校正从环形相位 1124 自动回到 15238,误差 3 脉冲。 环形编码器跨零场景得到验证。

07:10—07:25,多轮现场观察指出三个错误:

  • 本可进入负向,却又多顶一次;
  • 距离只剩约 3 cm 时反而往错误方向打;
  • 到位置后仍继续输出,最终打到底。

07:16:40,确认问题不是积分继承。 阶段切换时积分已清零;真正错误是普通 8°渐变限幅让当前 -15.44°只能先走到 -7.44°,换向后第一拍仍在错误方向。任务中换向必须允许在安全范围内直接跨零,而启动/停止回中仍分段进行。

07:31:33,一轮首次进入 -4.96 cm,但速度仍 -13.91 cm/s。 负向制动晚一个 DCC 周期:等到过零后才 BRAKE,第二档制动建立时已经冲到 -6.95 cm,惯性最低到 -9.06 cm。

07:31—07:38,制动点在 +4.5、+1.5、-0.5 cm间来回试。 +4.5 太早,只到约 -1.2;后续幅度越来越小。07:38:23 现场要求回到前三轮中“除了刹不住,其余明显更强”的版本,随后精确回退。

07:41:17,发现轮间继承的铁证。 一轮 PRECHECK 起始软件角为 +8°,不是 0°。上一轮故障后物理轨道没有回中,下一次 Task3 只重新使能,导致起点不一致。由此确定:每次 Task3 开始必须重新读取真实相位 15241,验收后清零软件角。

07:41:58,现场进一步要求连小球也自动回中心。 于是状态机扩展为:机械相位回中 → 视觉低速把球收敛到 0 → 居中稳定后才接受/执行 Task3。成功后必须保持 -5;只有显式 STOP/返回才回中。

09:18—10:52:自动回中、Base3 响应和最终可用结果

上午恢复测试后,自动回中仍出现半天不停止、停在 3 cm、任务完成后擅自回正、Base3偶发无响应等问题。现场在 10:07:12 明确:没有按返回就不要回正;第三题最终必须停在 -5。

10:13:02,一轮视觉轨迹突然从约 +4 跳到 -3.53 cm,球心 Y=223.5,偏离正常轨道 Y≈191。 这说明局部跟踪可能抓到轨道外目标;必须约束 ROI/Y 带并在异常跳变时重触发 YOLO,不能让错误坐标直接驱动大角。

10:20:13,得到完整可用但超时结果:

  • 正向峰值 +5.46 cm;
  • 最终稳定约 -4.9 cm;
  • 第一次合格 DONE 时间 7.036 s;
  • 首次制动停在 -1.87 cm,随后二次追赶,浪费约 3 s;
  • UNWIND 有效,没有撞端。

这一轮说明“能完成运动并保持”已经实现,但题目 5 s 指标未达成。随后只把负向制动起点从 +0.5 cm 改到 -1.5 cm,试图取消二次追赶,并保留原始轨迹。

10:52:23,现场要求写交接并只打包当前最稳定版本。 第三题由持续调参转入交接收束。


5. 2026-07-31 下午:版本辨认、第四题和第五题

15:38—16:04:队友版本不能整包覆盖

读取队友交接包后,最终第三题基线被认定为 TUNE-3PT-20260731-A;D 为明确失败版,C 只有局部改善。现场要求把当前在用版单独剥离,并清理历史脚本、只保留调参记录。

对 MSP 工程的结论:队友 D 版不能整包直接替换用户 Keil 工程;应迁移通信和任务功能,保留用户已经验证的循迹基线。版本迁移应按模块和差异进行,不能按文件时间“新就覆盖旧”。

16:04—18:12:Base4 同时支持循迹与滚球

现场要求 MSP 按键命令发出后 Maix 所有任务都能响应,而不是只能运行单一第三题。Base4要求直线 150 cm,同时把球稳定在 O 点。

问题序列:

  • Base2一度线还在中间却不循迹,否定了简单“宽黑误停”解释;
  • 尝试让 Base4 自动拉起 Maix 应用,但造成软件死机;16:43现场决定取消,比赛时手动点应用,降低风险;
  • 相机移动后标定出现约 +2 cm偏差;回中一度落在 -2/+1物理刻度;
  • 车辆起步直接误停、滚球振荡到 6~7 cm;
  • 小角度打不动,停车和起步冲击尤其明显。

17:12:01,现场给出正确控制原则:应做平滑,而不是永远固定死一个参数。 后续起步/停车采用随车辆阶段变化的目标和限幅,而不是固定摆杆角。

17:49—18:10,Base4主要问题收束到启动与停车。 停车时球曾到 -4 cm;18:12现场决定先固定这一版,进入下一题。

18:12—20:23:Play1/Task5、陀螺仪前馈与 GYROFIX3

18:43:38,任务名纠正:第五题是 OLED Play 1,不是 Base5。

首轮发现第二弯只走一半,循迹距离过短;同时弯道滚球不稳。19:05现场要求把 MSP 陀螺仪数据用起来。

19:17:19,第一轮同步数据: 29.68 s、500帧、201个 UART 包;位置 -2.54~+4.60 cm,RMS 1.83 cm,70.6%帧超±1 cm。极端振荡已减少,但第一弯正偏明显。

19:31:09,错误判断:认为陀螺仪前馈符号应翻转。 同时保留了正确的两个观察:起步未稳定、直线 8°脉冲过大。

19:37:53,实测立刻否定符号翻转。 范围恶化到 -6.02~+2.20 cm,RMS 3.02 cm,超±1 cm 78.1%。但回中握手和 1500 ms缓启动将起步峰值从 +2.98 降到 +1.56 cm,应保留。

GYROFIX2: 恢复原前馈方向,加入受限直线脉冲。结果 -3.11~+3.69 cm,RMS 1.97 cm,超±1 cm 62.5%。

20:09—20:15,GYROFIX3:

  • 前馈若与视觉纠偏方向冲突则关闭;
  • KD 0.45→0.9;
  • 直线破静摩擦 5.5°/260 ms;
  • 触发阈值 0.8→0.65 cm;
  • 弯道禁止脉冲。

结果:29.69 s,范围 -3.14~+2.59 cm,RMS 1.61 cm,超±1 cm 59.0%,无超过±4 cm,停车后约 -0.19 cm。第一弯正峰从 +3.69 降到 +2.59 cm。

GYROFIX4 试加 5°/160 ms弯道脉冲后,峰值虽小但 RMS 1.68、超±1 cm 62.2%,综合变差。20:23:45完整回退到 GYROFIX3。 这是一次规范的“有指标对比、有明确回退”的成功调参流程。


6. 2026-08-01:Task6、终点门控、停车后继续回目标

04:21—05:04:读取队友 8-1 工程,纠正第六题需求

ti-contest-2026-8-1 被确认为队友承接后的最新基线:Maix应用1.4.1、Prepare Ball、任务RTSP端口、Task5 GYROFIX3、机械中位15241。正式后续只在该目录继续。

04:37:13,开始第六题并要求删除两条灰度分支。 GRAY_SENSOR_GANV_NO_MCU 和 GRAY_SENSOR_GANV_MCU 不再保留,避免并行分支让现场不知道实际编译哪条逻辑。

04:41—05:04,需求理解先复杂后简化。 最初设想 OLED 选择 Sign± 和 Position 1/2/3;重新读题和现场讨论后确认:用户自动回中,再手动把球摆到任意实体刻度,Play2启动时直接捕获当前位置为目标即可。无需复杂菜单,也不存在动作队列容量问题。

05:13—05:53:自动回中失败、Prepare Ball First 和 RTSP 崩溃

自动回中时常只使能不动作。读取证明两条 UART 均正常,编码器能回 15242;真正失败是视觉稳定判据过严:最后 -0.49 cm、0.55 cm/s,但要求±0.35 cm连续1000 ms,20 s后超时并主动 Sleep。

05:38:16,Task6直接报 Prepare Ball First,同时相机似乎卡死。 后续拆出两个独立原因:

  1. MSP READY锁存与Maix状态不同步,任务在 MSP 侧被提前拦截,甚至没发送 TASK:6;
  2. Maix 应用后来确实退出/崩溃,不能把一切归为误触或网络。

05:41:03,现场否定“误触退出”的轻率结论,并要求触摸恢复。 后续日志最终找到真正根因:

mmf_region_update_canvas failed → camera read timeout → SIGSEGV(11)

RTSP客户端重连时,底部刻度使用的 MMF 硬件 Region叠加层崩溃。解决方案是禁用硬件刻度叠加,保留视觉检测、控制和刻度数据,不再用该不稳定显示路径。

05:51—06:14:Task6不能照搬Task5,停车控制从一端走到另一端

05:51:35,现场发现手摆 +2 后程序像在往0点平衡。 数据表明目标没有真的被改为0,而是捕获为 +1.364 cm;但 Task6直接复用了Task5增益和静摩擦参数,小偏差时仅输出约 -1.2°,长期卡在0附近,造成“像照搬Task5”的现象。于是 Task6控制参数独立。

05:58—06:01,一轮任意目标数据: 捕获 +5.326 cm;第一弯偏差 -3.81 cm,第二弯 -4.01 cm;停车瞬间球在 +4.326 cm、速度 +2.57 cm/s,CAR:HOLD 立即回0撤掉制动角,丢球帧还残留 -2.25°,把球推向正端。改为加强Task6弯道前馈、停车保留速度制动、丢球立即回平。

06:08,停车后误差0.8 cm但PD只有0.87°,静摩擦不动。 随后加入 HOLD阶段 5°/220 ms恢复脉冲。

06:14:43,轨迹证明该方案过度:HOLD后闭环跑了97 s,球在 -9.59~+9.32 cm反复追打,角度多次饱和±10°。 于是立即撤销持续恢复脉冲,改为有限制速度制动后回平。这是典型的“两端错误”:完全冻结会漂,永久追目标会打端点;最后应是有安全限幅的持续视觉收敛。

06:13—06:48:A点漏检、错误614兜底、第二弯门控与660兜底

现场同时报告车辆没有在A点停,多走接近半段直线。题面和代码确认 Base2、Task5、Task6物理路线相同,约614.16 cm;区别是速度、负载和打滑,不是路线长度。

06:27:34,XDS铁证: Task6 h_result=3、30003 ms超时、stop_mark=0。第一次回A完全漏检,30秒后强停。

曾把宽黑从5路降到4路,06:28:54现场明确否决:弯道可能误识别,阈值不能降。最终保持5路。

错误尝试:614 cm左右里程兜底。 06:33现场发现第二个半圆没完成就停车;XDS显示24.69 s正常完成、stop_mark=0,100%由里程兜底提前触发。原因是速度/负载/打滑让编码器“614 cm”不等于物理一圈。

最终方案:

  • 用陀螺仪确认第二弯已经进入并完成;
  • 完成第二弯后才允许5路A点识别与里程兜底;
  • A点宽黑优先;
  • 兜底提高到660 cm。

06:36:51,该版本编译、烧录、校验成功。

06:41:21,一轮28.717 s正常完成,stop_mark=0,由660兜底。 车辆位置合适,但硬刹令球漂到 -6 cm以上。随后只改停车过渡:640~660 cm由12线性降到7.5 cm/s,不提前发送固定 CAR:BRAKE。06:48:07 编译烧录成功。

06:50—06:56:任务结束后不能把球丢下

06:50:43,一轮 Task6 28.166 s完成,但球未回设定刻度。 旧 Maix 对 CAR:HOLD 的处理是终态冻结,视觉不再追目标。

06:53:32—06:53:50,现场给出最终业务要求:即使30秒超时,也不能把球丢在那里,必须继续打回设定刻度。

因 Maix重装后 SSH认证暂时不可用,先在 MSP 做兼容:车辆已由 car_stop_and_reset()停止,任务完成/超时后不发冻结视觉的 CAR:HOLD,改为重复发 CAR:CRUISE,让旧Maix继续纯视觉闭环;不发 STOP,避免清除Task6任意目标。

06:56:07,该 MSP 修复0错误0警告并烧录成功。 这是板上最后确认成功写入的核心功能版本之一。


7. 2026-08-01 下午至 2026-08-05:Base4里程、DAP故障和当前状态对账

08-01 16:38—17:11:第四题实际只走120 cm

16:38:13,检查第四问循迹距离。 源码目标为150 cm;现场实测物理只有约120 cm。

按 150/120=1.25,本地源码将 Task4独立编码器目标改为187.5 cm,并把巡航25提高到28 cm/s,预计约7.45 s,满足8 s。全量编译0错误0警告。

当时XDS110和Keil均在连接阶段报DAP错误,所以在该次远程调试中没有烧入。比赛阶段参赛者后来手动烧录了最终固件;赛后归档中的Base4最终值为187.5编码器厘米/28 cm/s。此后出现的Base2只转左轮属于当时的阶段性故障,不能用来否定最终固件。

08-05:把“本地源码、板上固件、Maix设备”三层分开

调试会话结束时的对账曾区分本地源码、板上固件和Maix设备;赛后根据最终文件夹和参赛者确认,更新为:

  • 最终MSP: Base4为187.5/28;Task5/6包含第二弯门控、5路A点、660兜底、640~660平滑降速和结束后继续视觉收敛,已由参赛者手动烧录。
  • 最终Maix: 应用1.5.0,控制身份TASK6-CAPTURE10-SAFE-PARK-20260801,Task6任意位置捕获、停车恢复角限5°、稳定窗±0.65 cm且速度<0.5 cm/s均已进入正式源代码。
  • 最终上位机: RDK X5控制台支持Task1~6端口巡检、显示、录像、断流保存、索引与回放。

这也是后续交接最容易犯错的地方:文件最新不等于板上最新;板上MSP最新不等于Maix应用也同步。


8. 失败模式—根因—以后怎么做

失败现象 当时容易误判 最终根因/证据 以后固定做法
上电打到底、堵转 用户没摆中 软件绝对零点不是真实水平;0x13跨圈;Sleep未验收 读真实相位→0x11最短相对修正→复读验收;机械限位与故障Sleep
电机ACK但球不动 视觉/PID坏了 3°编码器确实到位,球仍不动;静摩擦阈值高 受控整角短脉冲,检测运动立即撤力
微步连续发仍不动 多发几次会累计 每个23脉冲都落在死区,物理不累计 禁用微步作为主控制手段
到-5后仍冲端点 制动力度不够 到点时杆仍残留角、球速未降;状态机继续PID 先UNWIND/速度制动,再HOLD;位置、速度、实际角三条件
同参数每轮结果不同 环境随机 上一轮残余+8°/-16°被下一轮继承 每轮以编码器15241重建初始条件
YOLO框稳定但控制差 静态坐标误差大 主要是约40 ms推理和总延迟 YOLO种子+局部LAB,控制与显示解耦
Maix黑屏/卡死 网络掉了/误触 多实例资源争用;RTSP硬件Region重连触发SIGSEGV Launcher单实例;禁自启;禁硬件刻度叠加;统一释放
Base3按了没反应 OLED坏了 READY门控、Maix进程退出、状态不同步 命令阶段和ACK分层记录;Maix真实状态决定是否启动
A点漏检后跑第二圈 路程设置长 stop_mark=0、30 s超时;5路线采样没命中 第二弯门控+A点优先+660兜底
614兜底少半圈 题面长度错 24.69 s、stop_mark=0,编码器提前到614 不用理论路程直接等同物理路线;结合IMU阶段
停车后球漂走 应立即回平 HOLD冻结视觉,静摩擦和余速仍存在 车停与球控分离;视觉继续追保存目标并带安全限幅
连改15版越来越差 还需继续微调 机制叠加、记录中断、初始条件不一致 单变量、版本标签、三轮复测、无改善立即回退

9. 最值得吸取的调试方法

9.1 每轮实验前必须冻结五件事

  1. 代码版本标签与文件哈希;
  2. MSP板上固件版本,不只看本地源码;
  3. Maix实际运行的应用版本与进程PID;
  4. 机械编码器相位和球初始位置;
  5. 本轮只允许改变的一个参数/一个机制。

9.2 每轮统一记录

时间:
任务与版本:
机械初相位/终相位:
球初始位置:
唯一改动:
正向峰值、负向峰值:
首次进入目标时间与当时速度:
最终位置与稳定时间:
是否触发强起动/几次:
结束原因:DONE / FAIL / TIMEOUT / STOP
车辆停止原因:A点 / 里程兜底 / 超时
现场异常:碰撞、风、手摆误差、摄像头移动、网络/进程异常
结论:保留 / 回退 / 还需同版本复测

9.3 决策纪律

  • 单轮好结果只算“候选”,至少连续三轮同初始条件才算改善。
  • 若结果恶化,先回退,不在恶化版本上继续补丁叠补丁。
  • 参数变化和状态机变化不能同轮进行。
  • 任何“没有反应”先分层:命令是否发出、ACK是否收到、编码器是否动、机构是否动、球是否动、视觉是否正确。
  • 任何“提前停止”必须读 h_result / elapsed / stop_mark / mileage,不能只凭画面猜宽黑。
  • 任何相机黑屏先看进程、MMF、RTSP、显示所有权和退出来源,不能先重启循环。
  • 编译成功后必须保存烧录结果;连接阶段失败等于没有改动板上程序。

10. 对这次协作本身的复盘

做得好的部分:

  • 通过 XDS 内存、DCC编码器相位和Maix轨迹,把多次“看起来像”转成了证据;
  • 发现持续YOLO延迟后,用基准测试选择局部LAB,而非继续凭感觉调PID;
  • Task5使用统一指标对比FIX2/FIX3/FIX4,并明确回退到FIX3;
  • 最终把设备、源码、板上固件三种版本状态分开。

需要避免重演的部分:

  • 早期对机械中位和DCC绝对角语义验证不足,造成一次严重触底;
  • 过早把相机退出归因于误触、把网络可用等同于应用可用,结论下得太快;
  • 7月31日4点以后没有持续维护调参记录,多项改动叠加,浪费大量现场轮次;
  • 让现场反复执行启动、回中、摆球,自动化和初始条件固定做得太晚;
  • 有时先改参数再读取完整轨迹,正确顺序应是先保存证据、再确定唯一改动;
  • 自动拉起Maix GUI、自启、后台运行等方案在资源脆弱的设备上风险过高,最终仍回到手动Launcher。
check_circle
最重要的经验

真正有效的进展通常不是“再调一个增益”,而是消除一个不可控变量:真实机械零点、统一初始条件、视觉延迟、状态机边界、版本口径、停止原因。先让实验可重复,再谈参数最优。

5 问题结论与实验记录模板

check_circle
赛后版本边界

比赛已经结束,参赛者确认最终MSPM0固件已手动烧录。完赛版包含Base4的187.5 cm/28 cm/s、Task5/6的660 cm兜底、640~660 cm平滑降速和完成/超时后继续追目标;Maix正式代码为1.5.0,并已包含Task6任意位置捕获。本文的故障条目是赛前复盘,不是当前待办。

已解决或已定位

编码器计数不符

  • 实际单位时间计数约为 300 多,并非按 MG310 的 1066 计算。
  • 寄存器范围为 0x40~0x47,需结合拨码表确认读取含义。
Pasted image 20260729212511.png

MPU6050、U8g2 与 CPU 阻塞

  • MPU6050 FIFO 冲击曾造成阻塞和反复异常。
  • 相机 MMF/VI 缓冲未释放是反复崩溃的主要原因,不是普通内存不足。
  • 已通过显式释放帧、单实例锁、单缓冲和完整清理链路修复。
Pasted image 20260729213850.png

上电异常脉冲

  • 根因:S06maixapp 无条件初始化 UART0 并向 A16/UART0_TX 写入数据。
  • 结果:重启时可能触发 TB6612 PWMA,导致电机突然动作。
  • 当前策略:method=none 时跳过 UART0;相机异常只安全退出,禁止循环强制重启。

机械原点与回中

  • 旧原点先后记录为 15624、15337,均已过时。
  • 最新水平目标相位为 15241,实测 15244,误差约 0.066°。
  • 回中状态机曾因成功门限和 4° 脉冲门限都设为 ±0.2 cm 而抖动,出现 READY → CENTER → READY 循环。
  • 正式方案改为真实编码器相位闭环,不依赖旧绝对零点。
Pasted image 20260731022208.png

第三题:定点往返

最接近成功的一轮

  • 球在 2.96 s 到达 -4.96 cm,速度仅 -0.10 cm/s。
  • 失败原因:未及时切换水平保持,PID 继续大幅修正,引发二次振荡和错误的反向强起动。

由此确定:

  • 只允许 +5 cm 阶段自动再次强起动。
  • -5 cm 阶段禁止二次强起动。
  • 首次进入 -5 ± 0.8 cm 且速度 ≤2 cm/s 时立即调平。
  • 水平保持 180 ms 且仍在 -5 ± 1 cm 才发送 DONE。

当前最好完整结果

指标 结果
正向峰值 +5.46 cm
最终位置 约 -4.9 cm
长期保持 稳定
完成时间 7.036 s

当前瓶颈:第一次制动停在 -1.87 cm,随后发生第二次追赶,浪费约 3 秒。下一步应优化提前制动和负端一次捕获,不应继续增强强起动。

第五题:行驶稳球

实验对比

版本 位置范围 RMS 超出 ±1 cm 结论
初始陀螺仪前馈 -2.54~+4.60 cm 1.83 cm 70.6% 极端振荡减少,但第一弯补偿不足
前馈符号翻转 -6.02~+2.20 cm 3.02 cm 78.1% 明显变差,翻转方向错误
GYROFIX2 -3.11~+3.69 cm 1.97 cm 62.5% 消除超过 ±4 cm 的情况
GYROFIX3 -3.14~+2.59 cm 1.61 cm 59.0% 完赛代码保留的综合最佳基线

已确认的控制结论

  • 回中握手和 1500 ms 缓启动有效:起步峰值由约 +2.98 cm 降至 +1.56 cm。
  • 完全取消静摩擦脉冲不可行;约 +1° 的持续小角度不足以让球脱离静摩擦。
  • 原 8° 脉冲过强,会在行驶振动中造成反冲。
  • 4.5° 短脉冲方向正确但偏弱,通常只移动约 0.1~0.3 cm。
  • 固定方向的纯前馈不可靠:第一弯可能抵消视觉纠偏,第二弯却可能有效。
  • 速度阻尼 KD=0.45 偏小,过中心后撤力较迟。

后续实际采用的方案

  • 恢复原前馈符号;
  • 前馈与视觉纠偏同向时全量启用,反向时关闭;
  • Task5的KD最终取0.9;
  • 直线短脉冲最终取5.5°/260 ms,只在直线、低速和明显偏差时触发;
  • GYROFIX4弯道固定脉冲综合指标变差,因此回退并保留GYROFIX3。

完赛时保留的局限与历史故障

  • 已保存的第三题完整轨迹中有一轮用时7.036 s;这是赛前测试数据,不等同于最终赛场成绩。
  • 自动回正、约3 cm位置静摩擦卡滞和机械滞回曾显著影响重复性。
  • 早期240 px中心与实体刻度偏约2 cm;完赛标定已更新为3PT-20260731-E / 230 px。
  • XDS110 COM7枚举和DAP拒绝连接曾中断远程烧录;最终固件后来由参赛者手动烧录。
  • Task5两段弯道对固定方向前馈的需求不同,最终通过“与视觉纠偏冲突时关闭前馈”收束。
  • Task6最终源代码包含任意目标捕获、停车恢复角5°和结束后继续回目标逻辑。
  • Base4最终采用1.25校正,即187.5编码器厘米和28 cm/s。
  • Base2只转左轮是最后阶段出现过的故障现象,作为硬件/输入排查经验保留,不再列为赛后待处理任务。

如果今后重做类似系统:实验记录模板

#### YYYY-MM-DD HH:mm|版本名

- 代码版本/提交:
- 机械零点与编码器相位:
- 参数改动:
- 总时长:
- 位置范围:
- RMS:
- 超出 ±1 cm:
- 第一弯峰值:
- 第二弯峰值:
- 最终位置/速度:
- 异常与安全事件:
- 结论:
- 下一步只改:

6 原始调试记录归档

info
归档说明

本章保留现场原文和失败尝试,不与第 1~5 章的最终结论混用。需要复现时先查主线;只有当主线无法解释某个现象时,再回到这里追溯上下文。

warning
原始记录说明

以下内容按当时现场状态原样保留,其中“当前”“下一版”“未烧入”等词只代表记录生成时刻,不覆盖本文第1章的完赛最终状态。

maxicampro装侧面,直接获取二维坐标(x,y) 假设水管在图像里的两个端点为:

P0=(u0,v0),P1=(u1,v1)P_0=(u_0,v_0),\qquad P_1=(u_1,v_1)P0​=(u0​,v0​),P1​=(u1​,v1​)

YOLO 检测框中心为:

P=(u,v)P=(u,v)P=(u,v)

水管方向向量:

d=P1−P0\mathbf d=P_1-P_0d=P1​−P0​

把钢球坐标投影到水管轴线上:

λ=(P−P0)⋅dd⋅d\lambda= \frac{(P-P_0)\cdot\mathbf d} {\mathbf d\cdot\mathbf d}λ=d⋅d(P−P0​)⋅d​

再转换成实际位置:

s=(λ−λO)Ls=(\lambda-\lambda_O)Ls=(λ−λO​)L

其中:

  • L=0.25 mL=0.25\text{ m}L=0.25 m
  • λO\lambda_OλO​ 是中心点 O 对应的位置
  • s=0s=0s=0 为中心
  • s=+0.05 ms=+0.05\text{ m}s=+0.05 m 为 +5 cm
  • s=−0.05 ms=-0.05\text{ m}s=−0.05 m 为 −5 cm
  • Pasted image 20260729135312.png
  • 计数错误,我去,不是mg310的1066啊,这个电机的编码器单位时间计数实际300多

寄存器地址0x42,嵌套计算0x40到0x47,拨码表确定。

Pasted image 20260729212511.png
cpu阻塞了,mpu6050和u8g2传输有问题
Pasted image 20260729213850.png
解决问题(时间点21:35) 解决成功了。还是MPU6050的FIFO冲击 卡了很久视觉和这个控制链路的问题,要费点时间。 牢死了,14:36 视觉事件闭环:20%只发单A脉冲,视觉检测到球已启动后停止同向加力;根据位置和速度预测提前反向回正,再完成O→+5→-5。

/etc/init.d/S06maixapp 无条件把 UART0 映射并向 /dev/serial0 写入“serial ready”,而 UART0_TX 正是 A16,也就是 TB6612 的 PWMA。method=none 只关闭 Maix Comm,并没有阻止这段开机 UART 写入,所以每次重启仍可能给电机一个脉冲。

之前反复崩溃不是普通内存不足,而是相机 MMF/VI 缓冲未释放,我又错误地用强制重启恢复,导致 A16 启动脉冲带动电机。

已经修复:

  • 新增单实例相机锁,禁止 Launcher、探针和任务程序重叠占用摄像头。
  • 关闭生产模式的 RTSP 和屏幕显示。
  • YOLO 改为单缓冲,摄像头通道 buff_num=1。
  • 每次读取下一帧前显式释放当前 frame。
  • 从控制器、模型到相机通道全部纳入完整清理流程。
  • 电机子进程会在相机父进程崩溃时检测 EOF,并自行拉低输出。
  • 修正开机脚本:当 comm method=none 时,不再初始化 UART0,也不会向 A16/UART0_TX 发送 serial ready。
  • 原启动脚本已备份为 /etc/init.d/S06maixapp.pre-codex-20260730。
  • 今后相机异常只安全退出并报错,禁止自动强杀、重复初始化和强制重启。

当前 PID 732 正持续保持:

A16=LOW
A22=LOW
A23=LOW

新版稳定运行时是 hybrid_visual_runtime_stable.py,已同步到 MaixCAM,但我没有再次启动。

下一次应先断开电机电源或保持从齿轮脱开,只验证相机能够稳定启动、运行、退出并完全释放;通过后再接回机构。不会再用重启解决相机问题。

Pasted image 20260730221554.png
刚刚堵转卡死过一次 事故最符合“物理中位正确,但旧绝对原点错误;绝对 0° 命令把摇臂打向下限”。 maxicampro出了三次毛病,一会内存问题一会网络问题真是力竭了, 开机自启,循环重启会导致linux网络链路卡死,完全无法连接到网络端口

新原点,机械中位已保存为 15624步进电机初始化中点位置

Pasted image 20260731022208.png
0x11 相对整角控制:首次最多 5°≈228脉冲 一次下发,不再拆成无效微步;软件累计硬限 ±5°

3点的时候有点改善了

  • 新原点:15337
  • 运动触发释放起动脉冲
  • 位移阈值:0.25 cm
  • 速度阈值:1 cm/s
  • 单次角度变化限制:8°
  • 反向制动上限:+3°
  • 最终状态:Motor Sleep

自动再起动现在按预期反复在 -22° ↔ 约-5.6° 之间工作,但每次 -22° 的最长保持只有 550 ms;在当前机构位置仍不足以破静摩擦。由于已经有“检测到运动立即释放”,可以安全地把最长等待延长到 1200 ms:没动就继续保持,一旦球开始动仍会立即撤销,不会固定推满 1.2 秒。

按第三题完整版做:从 O 点先到 +5 cm,再反向到 -5 cm,并在 -5 cm 附近稳定;整段仍限制 5 秒、最大偏差按 1 cm 判定,不延时。

我会把现有“单点 +5 cm”状态机改成双阶段轨迹,重点用一次强起动克服静摩擦、提前制动,并给正反向各自独立的安全角度限制。先让当前已启动程序安全停机,再改代码部署。

目前还差一部分

本轮轨迹其实已经非常接近成功:球在 2.96 s 到达 -4.96 cm,速度仅 -0.10 cm/s,这就是理想的捕获时刻。失败是因为控制器没有切换到“水平保持”,继续做大幅 PID 修正,造成二次振荡;最后又误触发了反向强起动,球滚到右端。电机已安全回到 0° 并休眠。

我现在按轨迹做确定性修改:

  • 只允许 +5 阶段自动再次强起动,-5 阶段禁止二次强起动。

  • 首次进入 -5±0.8 cm 且速度 ≤2 cm/s 时立即把杆调平。

  • 在水平状态连续保持 180 ms 且仍在 -5±1 cm 才发送 DONE。

  • 若漂出范围再恢复普通控制,不会打 -28° 强脉冲。

  • MaixCAM YOLO 检测球的像素坐标,标定成位置 x(cm)。

  • 连续帧的位置差经过滤波,估算球速 v(cm/s)。

  • 位置环根据球到 -5 cm 的剩余距离生成目标速度:
    v_target = sign(error) × sqrt(2a|error|)。

  • 速度环根据 v_actual - v_target 计算摆杆角度。

  • DCC 驱动器内部执行并保持步进电机角度。

重启后基准成功:自适应LAB色块明显胜出。

  • YOLO首次定位:41.2 ms,只需偶尔调用
  • 局部模板匹配:算法67.8 ms,太慢,淘汰
  • 80×60自适应LAB色块:算法7.4 ms
  • 加取帧后总周期约18.9 ms,约53 fps
  • 静止中心抖动仅0.13像素,比当前YOLO控制环快约3倍

已经恢复到刚才手工找平的位置,并完成真实编码器闭环验证:

  • 水平目标相位:15241
  • 最终实测相位:15244
  • 误差:3脉冲,约 0.066°
  • 电机当前持续使能保持
  • 正式启动代码已部署

开机回中现在使用:读取真实编码器相位 → 0x11相对修正 → 再读相位验收,不再使用有问题的 0x13 或软重启探测。

  • ED→MSP→UART→Maix 的链路正常,确实执行了第三题;
  • 正向最高 +4.10 cm;
  • 负向最低 -4.03 cm;
  • 4.9秒末停在 -3.90 cm / 0.18 cm/s,差约0.3cm进入完成容差;
  • MSP随后正确发送STOP并自动回中。

原因是负端低速时保持角只有约1.5°,克服不了静摩擦。

自动回正还有明显问题。

核心不是零点标错,而是回中状态机缺少滞回:

  • 成功门限和4°脉冲触发门限都是 ±0.2 cm。
  • 球刚在 -0.14 cm进入 READY 0,视觉抖动到约 -0.20 cm就会立刻重新触发4°脉冲。
  • 结果是已经回中的球又被主动推走,出现 READY → CENTER → READY反复切换。
  • 从远端回中时还可能因静摩擦突然释放,产生较大速度,恢复时间过长。

改为8°、最长700ms,并保留速度≥1cm/s立即反打;从-8°回到0°正好是一条安全步长。 3cm左右严重卡死

读取完成:

  • 正向峰值:+5.46 cm
  • 最终位置:约 −4.9 cm
  • 长期保持:稳定
  • 完成时间:7.036 s,超时

问题是第一次制动停在 −1.87 cm,随后发生第二次追赶,浪费约3秒

Status : OK Class : Ports FriendlyName : XDS110 Class Application/User UART (COM7) InstanceId : USB\VID_0451&PID_BEF3&MI_00\7&B69B450&0&0000 xds枚举问题

12 次球心在 239–241 px,中位数正好 240.0 px 这个位置在+2cm,还是不合适。

  • 时长:29.68 秒
  • 轨迹:500 帧
  • UART 接收:201 包,说明陀螺仪报文已送达
  • 小球范围:-2.54~+4.60 cm
  • 最大正偏发生在约 10.41 秒,应该是弯道阶段
  • RMS 误差:1.83 cm
  • 超出 ±1 cm:353/500 帧(70.6%)
  • 没再出现之前的 -8~+6 cm 极端摆动
  • 停车前约 -0.75 cm,随后保持在 +0.28 cm

结论:陀螺仪前馈确实起作用了,极端震荡明显减小,但弯道仍偏向 +4.6 cm,补偿量或方向还没调准。轨迹已保存到 task5_gyro_latest_trace.json。

19:17

本轮结果:

  • 位置范围:-3.25~+4.87 cm
  • RMS 误差:2.11 cm
  • 超过 ±1 cm:72.3%
  • 第一弯:角速度约 -23~-28°/s,球冲到 +4.87 cm
  • 第二弯:角速度同样约 -23~-28°/s,球主要停在 -2~-3 cm

同步数据已经明确暴露三个问题:

  1. 陀螺仪前馈符号反了
    第一弯视觉 PD 正在输出 +2.8~+3.9° 拉球回中,但陀螺仪前馈却叠加 -0.65~-0.85°,削弱了纠偏。应把增益从 +0.035 改为 -0.035。

  2. 起步前没有真正稳定
    进入弯道前小球已经到过 +2.98 cm。Task5 目前收到命令后车辆直接起步,应改成 MaixCAM 确认球稳定在中心后,再回复 BALL:STARTED 让 MSP 起步。

  3. 直线仍触发 8° 静摩擦脉冲
    车辆行驶时本身已有振动,不需要这种大脉冲。它造成第一弯后从 +4.8 反冲到 -2.35 cm。Task5 应禁止静态脉冲,始终使用平滑的小角度视觉 PD 加陀螺仪前馈。 19;39 这版整体变差,但原因已经很清楚:

  • 完成时间:29.69 s
  • 位置范围:-6.02~+2.20 cm
  • RMS:3.02 cm
  • 超过 ±1 cm:78.1%
  • 第一弯结束冲到约 -4.42 cm
  • 第二弯最低达到 -6.02 cm

同步数据说明:

  1. 前馈翻转错了
    弯道角速度约 -27°/s 时,前馈变成 +0.8°,随后球持续向负方向移动。应恢复原来的负前馈方向。

  2. 起步改善了
    起步峰值从上一版约 +2.98 cm 降到 +1.56 cm,回中握手和 1500 ms 缓启动有效,应保留。

  3. 完全取消静摩擦脉冲也不行
    直线上持续输出约 +1°,但球卡在 +1.5 cm,说明小角度无法克服静摩擦。不能恢复原来 8° 大脉冲,应改成约 4~5°、150~250 ms 的短脉冲,只在直线、低速且误差超过约 0.8 cm 时触发。

下一版应:恢复原前馈符号、保留回中握手和缓启动,并加入受限的小幅静摩擦脉冲。

GYROFIX2 确实比上一版好:

  • 完成时间:29.70 s
  • 位置范围:-3.11~+3.69 cm
  • RMS:1.97 cm
  • 超过 ±1 cm:62.5%
  • 已完全消除超过 ±4 cm 的情况
  • 第一弯:-2.12~+3.69 cm
  • 第二弯:-3.11~-1.36 cm

直线小脉冲共触发 9 次。4.5° 能产生正确方向的运动,但幅度偏弱,正侧通常只移动约 0.1~0.3 cm。

现在主要问题是:

  • 第一弯球在正侧时,负向陀螺仪前馈会抵消视觉纠偏。
  • 第二弯球在负侧时,同一个负向前馈又是正确的。
  • 说明不能使用固定方向的纯前馈,需要根据球当前位置限制:前馈若与视觉纠偏方向相反就衰减或关闭。
  • 当前速度阻尼 KD=0.45 也偏小,过中心后电机撤力较迟。

下一版建议:

  • 前馈只在与当前位置纠偏方向一致时全量生效。
  • 相反时降为 0,避免第一弯继续推偏。
  • Task5 的 KD 从 0.45 提到约 0.8~0.9。
  • 直线脉冲提高到约 5.5°/260 ms,仍远低于原来的 8° 大脉冲。

19:51


2026-08-01之后的完整记录入口

本文件保留早期逐段原始记录。Task4、Task5、Task6、660 cm兜底、停车恢复、XDS内存诊断、目标DAP锁死、Base4在当次远程操作中未烧入等过程细节,统一整理在:

赛后状态:比赛已经结束,参赛者后来手动烧录了最终MSPM0固件;最终MSP、MaixCAM和RDK X5上位机代码见完赛最终代码与成果。本页中的“当前”只代表原始记录当时的现场状态。 +

7 跨项目通用电赛技术与调试经验

本章把本题中可迁移的电源、编码器、循迹、IMU、工程迁移和烧录工具经验单独收束。它们不是本题最终代码的额外步骤;使用时必须根据新硬件重新验证。

info
本章定位

本章把跨项目可复用的传感器接线、循迹与姿态控制、工程迁移、烧录工具和故障索引并入同一篇主帖。前六章记录 2026 年 TI 杯 H 题的最终方案与现场复盘;两部分事实不互相覆盖,复现 H 题时以前六章的最终口径为准。

以下内容保留原始实验数据与代码路径,仅调整标题层级,使通用经验与 H 题主线在同一篇帖子中可检索。

7.1 硬件、电源与编码器

电赛物料清单

冯老师强烈建议三月底完成物料清单,根据以往题目整理所需物料。

物料分为两类:

  1. 根据今年题目方向推测可能需要的物料。
  2. 练习往年赛题时用到的物料。

ICM-42688二车调试

纠正记录:模块针脚与底板丝印反向适配
warning
Warning

以下连接关系是对前面“按底板丝印直接解释”的纠正,以本节为准。

代码已经按修正后的配置重新烧录,但仍然没有收到 ACK,下一步需要检查实际 42688 模块本身。

问题原因是:这个 42688 模块的 8 针顺序与底板原来的 GY-521 接口是反向适配的,不能直接按照底板丝印解释模块针脚。

实际连接关系

在电源和地对齐后,实际连接为:

模块针脚 底板标注 MCU 引脚 作用
CS AD0 PA30 片选;拉高进入 I²C 模式
AD0 INT PB1 地址选择;拉低后地址为 0x68
INT1 XCL PA31 普通 GPIO 输入
INT2 XDA PA28 普通 GPIO 输入
SCL SCL2 原软件 I²C SCL 时钟线
SDA SDA2 原软件 I²C SDA 数据线
软件配置纠正

之前代码把 PA30 拉低,相当于把模块 CS 拉低,强制进入 SPI 模式,因此 I²C 永远不会响应。

现在应改为:

  • PA30:普通 GPIO 输出并拉高,作为模块 CS,使 42688 进入 I²C 模式。
  • PB1:普通 GPIO 输出并拉低,作为模块 AD0,固定 I²C 地址为 0x68。
  • PA31:普通 GPIO 输入,接模块 INT1。
  • PA28:普通 GPIO 输入,接模块 INT2。
  • 软件 I²C 继续使用原来的两根 GPIO 作为 SCL/SDA。
配置排查命令
rg -n -S "GPIOA\.31|DL_GPIO_PIN_31|PA31|GPIOA\.28|DL_GPIO_PIN_28" \
  C:\Users\12706\Desktop\embedded
补充说明

底板丝印标注本身不影响软件控制,关键是按照模块的实际针脚顺序配置 GPIO。PA30、PB1、PA31 和 PA28 都按普通 GPIO 使用,不应仅根据底板上的同名丝印判断功能。

纠正记录:初始化时序
问题定位

官方寄存器值 0x0F 本身是正确的,真正的问题是初始化顺序。

根据 TDK ICM-42688-P 数据手册,陀螺仪从 OFF 切换到低噪声工作模式后,至少需要保持开启约 45 ms。旧代码只等待了约 20 ms,并且先配置其他寄存器,最后才给传感器上电,时序不满足要求。

当前修正后的顺序
  1. 复位并完成 I²C 通信确认。

  2. 先写入:

    PWR_MGMT0 = 0x0F
    

    使加速度计和陀螺仪进入工作状态。

  3. 等待 50 ms,给陀螺仪足够的启动时间。

  4. 再配置:

    GYRO_CONFIG0  = 0x06
    ACCEL_CONFIG0 = 0x26
    
  5. 再等待 10 ms,然后读取陀螺仪样本。

  6. 将 0x8000 作为无效样本哨兵值,不能把它当作真实零漂参与校准。

  7. 只有读取到有效样本后,才将姿态模块标记为 ready。

代码位置
C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-42688二车\ti-contest-2025-flash\custom_src\drivers\sensors\icm42688\attitude_algorithm.c

本次修改涉及初始化时序、启动等待和无效陀螺样本过滤。

保护效果

即使 42688 硬件异常或没有产生有效样本,姿态模块也不会被误标记为已就绪,Base1 不会因此被放行后无限原地打转。

最终测试结果
已确认的电气状态
VCC = 3.3 V
CS  = 3.3 V
WHO_AM_I = 0x47

这一步进一步排除了基础接线问题:

  • VCC = 3.3 V:供电输入正常。
  • CS = 3.3 V:模块已正确进入 I²C 模式。
  • SCL/SDA 为 3.3 V,WHO_AM_I = 0x47:I²C 数字通信正常。
当前只剩两项检查
  1. 测量模块稳压器输出端,也就是芯片侧 VDD。

    不能只测排针上的 VCC;芯片侧电压应稳定在约 1.71~3.6 V,开启陀螺仪时不能出现明显跌落。

  2. 确认 AD0 实测为 0 V。

最终判断

如果上述两项都正常,结合已经验证的以下条件:

  • 大端连续读取正常
  • 初始化时序正确
  • 未使用 PLL
  • 温度、加速度和陀螺数据同时冻结

基本可以判定问题位于 ICM42688 模块的模拟/MEMS 部分,而不是软件通信或基础接线。

最有效的确认方法是直接更换一块 ICM42688 模块。当前软件已经能够正确报告 IMU 初始化失败,不会再在初始化失败后继续运行基础路线。

初始配置(已被上方纠正覆盖)
原始记录:按底板丝印的初始理解
  • INT → PB1
  • AD0 → PB0,由 MCU 固定输出低电平,I²C 地址保持为 0x68
  • XCL/SCL → PA30
  • XDA/SDA → PA28
配置要点
  1. 上电初始化时,将 PB0 配置为输出并固定拉低。
  2. 使用 I²C 通信时,SCL 连接 PA30,SDA 连接 PA28。
  3. 中断信号接入 PB1。
  4. 由于 AD0 为低电平,设备地址使用 0x68。

编码器计数问题

初步判断

上拉已经把原来 ENCODER_3/4 的假中断压掉了,说明之前确实有浮空噪声。

先确认是否转动轮子,再进行 GPIO 轮询读取。

新结论
  • RESISTOR_NONE 不要上拉,但是悬空脚必须关闭。现在只使用左边两个轮子。
  • 这个期间还更换了端子线。
配置调整
  1. 编码器输入恢复成范本配置。

    ENCODER_1~8 都从内部上拉改回 DL_GPIO_RESISTOR_NONE,因为已经证明加上拉后 motor2/3 读不到有效脉冲。

  2. 正式驱动轮切到 motor2/motor3。

    配置目录:

    Desktop/embedded/ti-contest-2026版本/ti-contest-2025-flash/ti-contest-2025-flash/custom_src/application/control/
    

    drive_mapping.h 现在是:

更换电池与加重后的调参

第一次调参
Base1 完成状态
sm current=4/4
completed=1
直行
target_mileage=50.000
current_mileage=49.108
转后清零的直行航向
target_angle=0.000
current_yaw=0.566
straight_error=-0.566
straight_correction=0.918
直行目标速度
target_speed[2]=7.694

所以现在直行阶段是正向、航向也稳,0.566° 很小。

转角问题

转角没转好的原因在转向末端:

turn[31] yaw=79.117 err=10.883 out=5.000
turn[32] yaw=83.460 err=6.540  out=4.377
turn[33] yaw=87.749 err=2.251  out=0.522

它最后只到 87.75° 左右就进入后续流程了,差约 2.25°。之前为了防止过冲,把近目标速度压得太狠;换重电池后惯性小、负载大,末端推不够。

下一步建议只小调近目标,不动直行:

TURN_NEAR_MAX_SPEED: 5 -> 6.5
ANGLE_THRESHOLD_DEG: 1.5 -> 1.2

这样末端会再多推一点,目标从 87.7° 拉近到 89~90°,又不会回到之前冲过头的问题。

第二次调参
偶发多转 20°

多转 20° 更像是转向完成判定太薄,或者过目标后还没有立刻切断输出。

代码里已经没有主动给转向混平移速度,trans 是左右轮实际速度不完全对称造成的测量值。真正会导致“偶发多转 20°”的点是:过冲判定只接受 8° 内过零,过了 8° 反而不认完成,会继续纠正,实车就表现成多拧一截。

修改为:接近目标后发生过零,25° 内都立即完成并停车,同时保留防 IMU 大跳变。

第三次调参
  • 直线目标:50.000 cm
  • 实际里程:49.129 cm
  • 直线结束 yaw:-0.304°
  • 直线纠偏:-0.341,很小

下一步建议做很小的目标角补偿:将 Base1 的 90.0f 改成 91.5f,不要再动 PID。这样不影响直线纠偏,也不放大偶发过冲。

7.2 循迹、转向与运动控制

灰度(光电)循迹

第一次调试:排除通信与接线问题
读取结果
  • 总读取:933
  • 非 0xFF:933,说明通信没有断
  • 无效图案:730 次,约占 78%
  • 当前图案:0x01,被判有效
  • 当前纠偏方向已正确:左轮 0、右轮 40
结论

“动一下、停一下”不是接线问题,而是查表规则太死:只接受连续的 1~4 个黑线位;出现 0x09 这类非连续图案就立即停车。推车后碰巧变成 0x01,它又开始运动。

第二次调试:改用八路加权平均

循迹位置改成八路加权平均:

  • 只在 gray_byte == 0x00(完全没检测到黑线)时,报错停车。
  • 其他非零组合都计算位置。
新问题

巡线卡在直线转角,保护过大。

思路:三路或四路识别到黑线时,执行对应方向的转向角度;这个过程也可以封装成动作队列。

第三次调试:90° 角点过冲
现象

90° 转角有点过冲。本来应该到 90° 转角的地方,灰度传感器直接越过,车辆停在那里卡住。

最初认为是循迹惯性受小车重量影响。进一步纠正后,原因是进入角点前的纵向制动不足:当前仍以 20 cm/s 接近,四路识别还要求连续 5 帧;重车已经跨过角点,才进入全白保护。

修改
  • 单侧边缘 3 路:只触发预减速,速度降到 10 cm/s。
  • 完整四路:连续确认由 5 帧降到 3 帧,避免跨过角点。
  • 确认角点后:先停车制动约 120 ms。
  • 制动结束后:再以当时的 yaw 计算 ±90° 目标。
  • 短白区前探降为 6 cm/s、最多 160 ms。
  • 循迹 PID 修正量受当前基础速度限制,减速时不会突然反转车轮。
第四次调试:修正转角方向
  • 0x0F/0x1F:现在对应正角,执行左转。
  • 0xF0/0xF8:对应负角,执行右转。
  • 转后重新捕获线带的原地旋转方向也同步修正。
  • 不会再出现“转角方向改了,但找线仍沿旧方向旋转”的问题。
第五次调试:加入传感器前置距离

发现车辆旋转中心和灰度传感器之间存在安装距离,必须将其作为变量,否则车辆会提前停下。

已将 10 cm 前置距离做成动作参数:

car_add_track_with_corners(
    255.0f,  // 总循迹距离
    90.0f,   // 角点转角
    10.0f    // 灰度板到车辆旋转中心的距离
);
执行顺序
  1. 灰度确认角点:连续 3 帧识别到四路角点图案。

  2. 立即锁存角点方向,后续不会因灰度变化而改方向:

    • 0x0F/0x1F:左转,方向 +1
    • 0xF0/0xF8:右转,方向 -1
  3. 保持捕获瞬间的航向,直线闭环前进 sensor_offset_cm;Base3 当前为 10 cm。

  4. 停车制动约 120 ms。

  5. 根据锁存方向计算目标:

    target_yaw = current_yaw + direction * 90.0f;
    
  6. 完成转角后重新捕获线带,继续循迹。

第六次调试:放宽过冲完成判定

越过目标后,允许最多 25° 的过冲仍判定完成。

第七次调试:取消转后原地找线

原先 90° 转完后,程序还会沿原方向原地旋转找线,这会额外产生几度左偏。现在改为:

  • 完成 90° 转向后不再追加原地旋转。
  • 以 8 cm/s 低速向前循迹。
  • 根据实时灰度误差逐步回到线带中心。
  • 0x00 仍立即停车。

圆弧逻辑

第一轮调试
问题

圆弧控制速度太猛。半径 30 cm、轮距 24 cm 时,代码用 CIRCLE_SPEED=70 算出:

  • 外轮:98 cm/s
  • 内轮:42 cm/s

外轮几乎是直行上限的两倍。

修改

将圆弧速度按外轮不超过 50 cm/s 收束,同时增加圆弧读数变量,便于下一次读取累计角度和内外轮目标。

结果
圆弧动作完成:sm current=1/1 completed=1
  • 半径:30.000 cm
  • 目标角:360.000°
  • 累计角:360.746°
  • 过冲:约 0.746°
  • 顺时针:clockwise=1
  • 外轮目标:49.000 cm/s
  • 内轮目标:21.000 cm/s
  • 左右目标:左 49.000,右 21.000

转向方向与Yaw符号不一致

调试结论

编码器读取和速度 PID 目前正常,问题已经从“速度闭环”转移到:

转向方向与 yaw 符号不一致。

实测数据
速度闭环
目标速度:M2 -24 cm/s,M3 +24 cm/s
实际速度:M2 -22.634 cm/s,M3 +22.634 cm/s
PWM:M2 -525,M3 +553
平移分量:0.000
编码器更新:M2 76,525 次,M3 76,514 次

左右轮速度闭环非常对称,没有出现饱和或方向反转。因此编码器和速度 PID 暂时不需要修改。

姿态与转向
目标角:+90°
当前 yaw:-10.432°
误差:+100.432°
转向输出:持续 +24 cm/s
历史 yaw:-10.407°,并且持续变得更负
问题分析

小车执行正 90° 转向动作时,实际 yaw 朝负方向变化:

  1. 控制器目标是 +90°。
  2. 当前 yaw 从 -10.432° 继续向负方向变化。
  3. 误差越来越大,当前误差已经达到 +100.432°。
  4. 控制器因此持续输出 +24 cm/s,转向动作无法正常结束。

这说明左右轮的速度差方向已经产生了旋转,但该旋转方向与 yaw 的正负号定义相反。

里程现象

当前累计左右轮里程约为:

左轮:-1082 cm
右轮:+1082 cm

这进一步说明车辆确实在原地转向,而不是速度闭环失效;问题集中在转向轮速分配方向或 yaw 符号约定。

下一步修改

优先检查并修正以下其中一项:

  • 转向时左右轮目标速度的分配方向。
  • yaw 的正负号定义,或 yaw 误差的计算符号。

建议先确认“左轮负、右轮正”对应的实际旋转方向,再统一以下三者的约定:

  1. 左右轮速度差的正方向。
  2. yaw 增大的实际旋转方向。
  3. 转向动作中 direction 或目标角度的正负号。
priority_high
Important

当前不需要重新调整编码器方向、速度 PID 参数或速度闭环,只需修正转向方向与 yaw 符号之间的映射关系。

根因确认与修复
根因

后续确认问题来自右后轮的电机极性与编码器方向配置:

  • 后右轮正 PWM 实际让车辆后退。
  • 后右编码器又把这个后退速度当成正速度反馈。
  • 因此 Base1 两轮一起向后,Base2 则接近原地旋转,但 PID 读数看起来仍然“正常”。
已同步修改
  1. 翻转右后电机极性:

    C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\drivers\actuators\motor\motor_user.c:45
    
  2. 修正后右编码器方向:

    C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\config\car_params.c:22
    
    后右编码器方向:-1 → 1
    
编译与烧录结果
编译:0 Error / 0 Warning
烧录:已通过 XDS110 成功完成
下一步验证顺序
  1. 先测试直行,确认两轮都向前。
  2. 再测试 Base1。
  3. 最后重新验证 Base2 的原地转向和 yaw 方向。

PID调参:角度

Base1 初始参数
目标角度:90.0°
角度完成阈值:±1.2°
稳定样本数:10 次
转向最大速度:±40 cm/s
大误差最低转向速度:10 cm/s
最低速度启用误差:> 8°
角度 PID:Kp=1.1, Ki=0.0, Kd=0.15
现象与处理

yaw 转向到 90° 后,先慢速震荡,再逐渐减小震荡并停止。

进入 ±1.2° 角度窗口后,转向控制不再继续给小 PID 输出,而是直接把左右目标速度设为 0,只等待稳定计数完成。这样末端不会再靠小 PWM 来回蹭。

第二次调参

停车或复位时,将调试 PWM 一起清零,避免再读到上一拍残留值。

在 CAR_STATE_STOP 分支和 car_reset() 中都清零:

dbg_pwm_output_0..3 = 0;
结果

反而出现震荡:

yaw=106.307  err=-16.307
...
yaw=91.828   err=-1.828
yaw=89.327   err=0.673   out=0
yaw=87.003   err=2.997
yaw=84.925   err=5.075
...
yaw=79.554   err=10.446

这说明它冲过 90° 到 106°,又反打穿过 90° 到 79°,再回到目标。根因是转向速度和近目标输出太硬,惯性过冲大。

参数修改
TURN_MAX_SPEED: 40 -> 28
角度 PID Kp: 1.1 -> 0.9
角度 PID Kd: 0.15 -> 0.35
稳定样本: 10 -> 4
完成阈值: 1.2° -> 1.5°
20° 内输出限幅: ±14
8° 内输出限幅: ±7
最低转向速度: 10 -> 8
最低速度仅在误差 > 15° 时启用
第三次调参

现在不是震荡问题,而是“速度分段限幅 + 进入死区后断输出”带来的一点顿挫,尤其是:

err=2.122 out=1.169
err=1.373 out=0.000

可能是死区内斜率归零造成的:进入 ±1.5° 后,它还按斜率残留一点输出,等于在目标附近继续轻推。

应保留斜率限制,但死区内必须硬停车。

第四次调参
现象

超过 90° 一点后又回正。

原因

过冲后,程序主动反向纠偏了十多个周期。

完成判定
  • 只要误差符号跨过 0,并且过冲 ≤ 4°,就直接接受完成并立即停车。
  • 不再反向纠偏。
  • 需要根据车的具体情况判断这个策略是否过于保守。

PID调参:直行距离

第二次调参
现象

转向停止点太激进。它在 85.3° 时输出还有 17.5,后面靠惯性继续旋转;延时 250 ms 后清零时车还没稳,直行时 yaw 已经跑到 147°,所以走成 U 形。

做法
  • 扩大近目标限速区。
  • 增大降速斜率,让输出能从 20 附近快速降到 5,而不是慢慢下降。
  • Base1 的转后静止时间从 250 ms 增加到 500 ms。
第三次调参

直行时逻辑速度是正的,但实车后退,而且里程还能正向累计。

这说明 motor2/3 的“电机正方向”和“编码器正方向”都与物理前进方向相反。

7.3 IMU、Pitch与坡道控制

Pitch倾角调节

第一次调试:发现坐标轴偏差
实测数据
当前 pitch:-30.889°
最低 pitch:-35.029°
最高 pitch: 11.115°
当前 roll: -26.513°
采样数:3480

40° 坡道没有完全落在 pitch 轴上,而是分摊到了 pitch 和 roll。合成倾角约为:

acos(cos(pitch) × cos(roll)) ≈ 39.8°
初步判断

IMU 倾角基本正确,异常在于 IMU 坐标轴相对车身前进方向旋转了约 37°~45°。不能只用原始 pitch 判断坡道,需要把 pitch 和 roll 转换到车身前向坐标系,再做水平零点校准。

初步方案

把 IMU 的俯仰从传感器坐标转换到车身前向坐标:根据实测将车头前向轴设为传感器平面内约 -37°,用重力向量投影计算,随后再减去路线开始时的水平零点。这样 40° 坡道不会再被拆成 pitch=-31°、roll=-27°。

info
Note

这是第一轮根据 DMP pitch/roll 得出的判断。后续实测证明 DMP 欧拉俯仰不稳定,最终方案已改为 FIFO 加速度和陀螺仪互补滤波。

第二次调试:最终架构
MPU6050 DMP yaw         → 平地直行、转向、转圈
加速度 + X 轴陀螺 pitch → 上坡、下坡检测

两者共用一个 IMU 周期任务,但算法、零点和控制用途分开。

本轮完成的工作
  1. 确认 MPU6050 地址与身份:

    • 实际 WHO_AM_I = 0x70
    • 同时兼容 0x68/0x70
  2. 最初直接使用 DMP pitch/roll:

    • 水平静止时出现约 -37°~+21° 波动
    • 确认 DMP 俯仰不适合坡度判断
    • DMP 只保留给 yaw
  3. 改为读取 DMP FIFO 中已有的原始加速度:

    • 不增加额外 I²C 读取

    • 同时保留 FIFO 校准陀螺数据

    • 接口位于:

      C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\drivers\sensors\mpu6050\inv_mpu.c:3099
      
  4. 标定车身坐标轴:

    • 车头接近竖直时读到 accel=[194, 2045, -226]

    • 确认车头前向对应传感器 +Y

    • 俯仰旋转轴对应传感器 X

    • 配置位于:

      C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\app_template\app_template_config.h:57
      
  5. 加入互补滤波:

    98%:X 轴陀螺积分
     2%:加速度倾角校正
    周期:10 ms
    

    实现文件:

    C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\app_template\app_template_imu.c
    
静态结果
当前 pitch:+0.099°
最小值:-0.159°
最大值:+0.186°

因此静态误差约为 ±0.2°。

试错记录
1. 直接使用 DMP pitch

最初认为 DMP 已经输出欧拉角,可以直接用于坡道:

mpu_dmp_get_data(&pitch, &roll, &yaw);

实测 40° 坡道时:

pitch = -30.889°
roll  = -26.513°

将两者合成后倾角约 39.8°,因此最初误判为“IMU 斜装,坡度被分摊到 pitch 和 roll”。

2. 尝试将 DMP pitch/roll 转换到车身坐标

根据当时数据推算,车头轴相对 IMU 约为 -37°:

#define IMU_CHASSIS_FORWARD_AXIS_DEG (-37.0f)

使用重力向量投影计算车身 pitch。数学计算本身成立,但输入的 DMP pitch/roll 并不稳定,因此这个安装角推断也不可靠。

3. 第一次加入相对 pitch 零点

增加:

app_template_get_pitch();
app_template_zero_pitch();

路线开始时清零,并记录:

  • 当前 pitch
  • 最小 pitch
  • 最大 pitch
  • 采样次数

但采样最初放在 car_task() 中。手推小车时车控任务没有运行,读取结果是:

samples = 0
min = 0
max = 0

所以无法记录手推坡道。

4. 把 pitch 采样移到 IMU 周期任务

改为每次 mpu6050_dmp_update() 后持续采样,周期 10 ms。

这样即使没有启动路线,手推小车也能记录倾角极值。

5. 尝试启动自动清零

第一版启动标定:

丢弃:0.5 s
平均:1 s

但标定过早:

标定零点 = -4.798°
稍后车身原始角约 = -17.46°
相对 pitch = -12.659°

说明当前工程中的 DMP pitch/roll 仍在启动后继续变化。

6. 尝试稳定窗口标定

改成只有连续 1 s 波动小于 0.75° 才接受零点。

结果是 DMP 持续变化,或用户在测试时动车,稳定条件一直不满足,OLED 长时间停在 0°。

随后增加最长等待时间:

等待:0.3 s
每窗:0.5 s
最多:两窗
最长:约 1.3 s

这解决了无限等待,但没有解决 DMP 姿态本身的异常。

7. 发现 OLED 仍显示原始 DMP pitch

内部已经有相对车身 pitch,但 OLED 菜单仍绑定:

&pitch

后来改成:

&dbg_current_pitch

OLED 才真正显示转换和清零后的车身 pitch。

8. 确认 DMP pitch/roll 不适合当前坡度检测

小车全程水平静止时读取到:

DMP pitch 当前值:-14.478°
相对 pitch:-15.456°
运行极值:-37.391° ~ +20.933°

静止跨度接近 58°。因此确认问题不只是零点,而是当前驱动中的 DMP 欧拉俯仰输出不稳定。

9. 从 DMP 欧拉角切换到 FIFO 原始加速度

dmp_read_fifo() 本来已经返回:

short gyro[3];
short accel[3];
long quat[4];

旧代码只用四元数计算 yaw/pitch/roll,把 accel 和 gyro 丢掉。

增加:

mpu_dmp_get_latest_accel();
mpu_dmp_get_latest_gyro();

直接复用 FIFO 数据,不额外增加 I²C 读取。

10. 第一次 FIFO 加速度仍沿用错误的 -37° 轴

虽然数据源换成了稳定的加速度计,但车头轴仍配置为 -37°。

当车头接近竖直时:

accel = [194, 2045, -226]
显示 pitch 约 43°

数据明确说明重力集中在 +Y 轴,车头前向实际是传感器 +Y,不是 -37°。

11. 将车头轴修正为传感器 +Y

最终改为:

#define IMU_CHASSIS_FORWARD_AXIS_DEG 90.0f

此后水平静止实测:

当前 pitch:+0.099°
最小值:-0.159°
最大值:+0.186°

静态误差降到约 ±0.2°。

12. 发现纯加速度 pitch 受车辆加减速影响

用 Base1 爬坡时:

最小 pitch:-19.771°
最大 pitch:+33.975°

加速度计会把电机起步、刹车和重力混在一起,所以静态很准,运动中会出现假倾角。

13. 最终改成 FIFO 加速度 + FIFO 陀螺互补滤波

当前最终方案:

98%:X 轴陀螺积分
 2%:+Y 轴加速度倾角
周期:10 ms

数据用途:

DMP quaternion      → yaw
DMP FIFO accel/gyro → pitch
DMP Euler pitch/roll → 不再用于坡道控制

所以不是完全删除 DMP,而是保留 DMP 四元数用于 yaw,并使用 DMP FIFO 中的 accel/gyro 计算 pitch。

这就是从“直接使用 DMP pitch”,经过坐标投影、零点标定、稳定窗口,到最终改用 FIFO 加速度和陀螺互补滤波的完整过程。

当前符号约定
上坡、车头抬起:负角
下坡、车头朝下:正角
坡道动作
模块与接口

新增独立坡道模块:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\control\car_ramp.c:1

路线接口:

car_add_up_ramp(max_distance_cm, speed_cmps);
car_add_down_ramp(max_distance_cm, speed_cmps);

兼容旧接口:

car_add_ramp(...) == car_add_up_ramp(...)
判定参数
项目 参数
上坡进入 pitch <= -8°
上坡退出 pitch >= -3°
下坡进入 pitch >= +8°
下坡退出 pitch <= +3°
进入确认 5 个周期
退出确认 10 个周期
最短坡面时间 500 ms
动作超时 15 s
下坡最大速度 25 cm/s

状态机接入位置:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\control\car_state_machine.c:568
当前任务

原 Play 任务已替换为:

// Ramp Up
car_add_up_ramp(80.0f, 20.0f);

// Ramp Down
car_add_down_ramp(80.0f, 12.0f);

配置位置:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\app_template\app_template_actions.c:161

旧 Play 1 长路线和 Play 2 摄像头四路径已删除,路线文件缩减到约 221 行,固件减少约 892 bytes。最终工程编译结果为 0 Error / 0 Warning。

7.4 工程架构与快速迁移

架构问题

5. 堆栈问题

启动文件还是:

Stack_Size EQU 0x00001200   ; 4.5 KB
Heap_Size  EQU 0x00003000   ; 12 KB

工程没有实际使用动态分配;encoder.c 里的 malloc 只是注释。

当前链接器显示 RAM 30.19 KB,其中:

Heap  12 KB
Stack 4.5 KB

实际静态数据约只有 13.7 KB。建议把 Heap 改为:

Heap_Size EQU 0x00000400

保留 1 KB 足够保守。修改后 RAM 余量会从约 1.8 KB 增加到约 13 KB。

Stack 4.5 KB先保留,因为工程里存在 snprintf、浮点运算以及 DMP 局部数组。

小车工程快速迁移改进

当前结论
subject
Abstract

当前工程还没有达到“换车时只修改一个配置文件”的程度,快速迁移能力约为 6.5/10。

如果新车仍然采用以下配置,上层路线基本不需要修改:

  • 双驱差速结构
  • 编码器电机
  • 相同主控
  • MPU6050
可以直接复用的部分

以下上层功能基本不绑定具体小车:

  • 路线状态机
  • 直线、转弯、圆弧和循迹动作
  • 上坡、下坡动作
  • OLED 和串口任务入口
  • 超时与失败停车
  • 调试读取框架
换车时必须调整的部分
1. 底盘尺寸与编码器参数

配置文件:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\control\car_config.h:1

需要调整:

WHEEL_RADIUS_CM
PULSE_NUM_PER_CIRCLE
WHEEL_BASE_CM
ENCODER_PERIOD_MS
ENCODER_DIR_*
2. 实际驱动电机映射

配置文件:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\control\drive_mapping.h:8

当前配置:

DRIVE_LEFT_MOTOR  = MOTOR_BACK_LEFT
DRIVE_RIGHT_MOTOR = MOTOR_BACK_RIGHT
3. 电机 PWM 方向与底层引脚

需要检查:

  • motor_hardware.h
  • motor_user.c
  • SysConfig 引脚配置
4. IMU 安装方向

配置文件:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\app_template\app_template_config.h:57

当前配置:

IMU_CHASSIS_FORWARD_AXIS_DEG 90.0f

更换 IMU 安装方向后,必须重新标定:

  • 车头轴
  • 俯仰轴
  • 角度符号
5. 灰度板位置

需要调整:

sensor_offset_cm
TRACK_SENSOR_COUNT

同时需要重新确认角点左右掩码。

6. PID 参数

配置文件:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\app_template\app_template_pid_params.c:11

新车重量、轮胎、电机或电池发生变化后,至少需要重新调整:

  • 左右轮速度 PID
  • 里程 PID
  • 直行航向 PID
  • 转角 PID
  • 循迹 PID
7. 坡道阈值

配置文件:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\custom_src\application\control\car_ramp.c:8

需要调整:

RAMP_ENTER_PITCH_DEG
RAMP_EXIT_PITCH_DEG
RAMP_TIMEOUT_MS
迁移耗时判断
新车变化范围 预计迁移时间 主要工作
同主控、同传感器、同双驱结构 约 30~60 分钟 完成硬件映射,之后进行实车 PID 调试
同主控,但更换电机、轮径、轮距或 IMU 安装方式 约半天 完成基础迁移、参数修改和标定
更换主控、驱动板、编码器或循迹传感器 无法快速迁移 需要修改驱动层
当前最大不足

车辆参数目前分散在多个位置:

  • car_config.h
  • drive_mapping.h
  • app_template_config.h
  • PID 参数文件
  • 坡道模块
下一步改进

将分散的车辆参数收束到统一的车辆配置文件:

vehicle_profile.h
vehicle_profile.c

目标是更换小车时只需切换或修改一份车辆配置。

7.5 烧录与调试工具

自动切换烧录器

功能概述

工程已经实现 XDS110 与 J-Link 两种调试/烧录器之间的自动切换。入口位于:

C:\Users\12706\Desktop\embedded\ti-contest-2026版本\ti-contest-2025-flash\ti-contest-2025-flash\project\Keil\

切换流程为:

  1. 选择烧录器:XDS110 或 J-Link。
  2. 修改 Keil 的 .uvprojx 和 .uvoptx 配置。
  3. 可选:调用 Keil 编译当前工程。
  4. 调用对应烧录工具写入固件。
  5. 检查烧录结果并保留日志。
  6. 如果流程失败,自动恢复切换前的 Keil 配置。
文件组成
文件 作用
调试器切换.vbs 隐藏 PowerShell 窗口,启动图形选择器
debug_probe_selector.ps1 提供 XDS110/J-Link 选择界面,并决定是否编译、烧录和校验
switch_debug_probe.ps1 执行实际的 Keil 配置切换、编译和烧录
EmbedBolt316.uvprojx Keil 工程文件,脚本会修改其中的烧录 DLL
EmbedBolt316.uvoptx Keil 调试选项,脚本会修改调试器和目标选择
JLinkSettings.ini J-Link 的 Keil 配置文件
JLinkLog.txt J-Link 最近一次连接日志
图形界面用法
入口

双击:

project\Keil\调试器切换.vbs

界面中可以选择:

  • XDS110
  • J-Link
  • 是否在切换后自动执行“编译、烧录和校验”

默认勾选编译、烧录和校验。

warning
Warning

运行前必须关闭所有 Keil/uVision 窗口,否则脚本会拒绝执行,避免 Keil 使用缓存配置覆盖脚本修改。

命令行用法

在 project\Keil 目录执行:

切换到 XDS110
.\switch_debug_probe.ps1 -Probe xds110

切换并编译、烧录、校验:

.\switch_debug_probe.ps1 -Probe xds110 -Flash
.\switch_debug_probe.ps1 -Probe jlink

切换并编译、烧录、校验:

.\switch_debug_probe.ps1 -Probe jlink -Flash

如果 PowerShell 的执行策略阻止脚本,可以使用:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\switch_debug_probe.ps1 -Probe xds110 -Flash
XDS110 烧录流程

使用 XDS110 时,脚本会:

  1. 将 Keil 的 Flash2 设置为 BIN\UL2CM3.DLL。

  2. 将目标调试器选择为 XDS110 CMSIS-DAP。

  3. 使用 Keil 编译生成:

    project\Keil\Objects\EmbedBolt316.axf
    
  4. 在 C:\ti\uniflash_* 中寻找最新的 dslite.bat。

  5. 复制目标配置和 AXF 到临时目录。

  6. 将 XDS110 配置中的序列号替换为 NOserial,避免序列号绑定。

  7. 调用 TI UniFlash DSLite 烧录。

  8. 检查日志中的 IFlashDevice::HandleVerification(...) = true。

日志位置:

debug_flash_artifacts\build_logs\dslite_xds110_flash.log

成功后会将日志压缩为审计记录,包含:探针、接口、芯片型号、固件路径、固件大小、固件时间戳和完成时间。

使用 J-Link 时,脚本会:

  1. 将 Keil 的监视器 DLL 切换为 Segger\JL2CM3.dll。

  2. 将目标选择切换为 J-Link。

  3. 使用 Keil 编译生成:

    project\Keil\Objects\EmbedBolt316.hex
    
  4. 在以下位置寻找 J-Link Commander:

    C:\Program Files\SEGGER\JLink\JLink.exe
    C:\Program Files\SEGGER\JLink_V940\JLink.exe
    C:\Program Files\SEGGER\JLink_V796\JLink.exe
    
  5. 生成临时 J-Link 命令文件:

    device MSPM0G3507
    si SWD
    speed 1000
    connect
    r
    h
    loadfile EmbedBolt316.hex
    r
    g
    exit
    
  6. 检查退出码、错误信息和 O.K.。

日志位置:

debug_flash_artifacts\build_logs\jlink_flash.log
Keil 配置切换内容

脚本会修改两个 Keil 文件:

.uvprojx

修改 <Flash2>:

XDS110:BIN\UL2CM3.DLL
J-Link:BIN\UL2CM3.DLL

两者使用同一个 Keil Flash DLL,但实际调试器由 .uvoptx 中的配置决定。

.uvoptx

主要修改:

项目 XDS110 J-Link
nTsel 3 4
pMon BIN\CMSIS_AGDI.dll Segger\JL2CM3.dll
调试协议 SWD SWD
调试时钟 1000000 1000000
Keil Flash Sequence 启用 禁用,改由脚本调用 J-Link Commander

XDS110 和 J-Link 的目标芯片均为 MSPM0G3507。

编译与失败恢复

使用 -Flash 时,脚本会先调用:

C:\Users\12706\AppData\Local\Keil_v5\UV4\UV4.exe

并要求编译日志包含:

0 Error(s)

如果编译、烧录或校验失败:

  • 脚本抛出错误。
  • 自动写回切换前保存的 .uvprojx 和 .uvoptx。
  • 临时烧录目录只允许在系统临时目录下删除。
  • 失败日志保留在 debug_flash_artifacts\build_logs 中。
当前注意事项
  • 运行前关闭 Keil,避免配置缓存覆盖切换结果。
  • XDS110 需要安装 TI UniFlash,并且 C:\ti\uniflash_* 下存在 dslite.bat。
  • J-Link 需要安装 SEGGER J-Link Commander。
  • 两种方式都要求先生成对应的 AXF 或 HEX 文件。
  • 烧录接口为 SWD,目标芯片为 MSPM0G3507。
  • 切换脚本只负责当前工程的调试器配置,不会修改源代码。

JLinkLog.txt 中记录的最近一次 Keil 连接出现:

Failed to initialized DAP.
No PWR-AP detected.

这表示当时 J-Link 没有成功识别目标板的调试访问端口。优先检查:

  1. 目标板是否上电。
  2. SWDIO、SWCLK、GND 和复位线是否接好。
  3. J-Link 与目标板电平是否匹配。
  4. 是否有其他程序占用调试器。
  5. 是否需要在目标板复位时连接。

这属于硬件连接或目标供电问题,不等同于自动切换脚本本身失败。

7.6 问题现象—根因—解决索引

info
使用方式

这一章不是新的调参记录,而是把前五章中分散的问题压缩成可检索的故障表。遇到相似现象时先按“优先检查”排查,再进入链接章节查看原始数据和修改过程。

现象 已确认或高概率根因 处理结论 详细记录
ICM-42688 无有效数据,基础任务可能被错误放行并持续打转 模块8针顺序与底板原GY-521接口的理解相反;初始化状态又曾过早标记为就绪 按实物重新核对针脚映射;只有取得有效样本后才置姿态就绪,失败时禁止任务启动 42688针脚修正;初始化时序
编码器出现假计数、假中断,或加上拉后反而读不到脉冲 未使用输入悬空产生噪声;有效编码器输入不适合统一开启内部上拉 关闭悬空通道;有效输入恢复范本的DL_GPIO_RESISTOR_NONE;换线后重新逐轮确认 编码器计数问题
换重电池后90°转向末端差约2°,推不到目标 负载增大后近目标速度压得过低,末端输出不足 只微调近目标输出或目标角补偿,避免同时重调直行和整套PID 更换电池后的调参
转向偶发多转约20° 过零后的完成判定窗口过窄;超过窗口后控制器继续反向纠偏 用误差过零结合合理过冲范围结束动作,立即停车,不再连续反打 偶发多转记录;过零完成判定
灰度循迹“动一下、停一下”,但串口/传感器读取一直在线 查表只接受少量连续黑线组合,把0x09等非连续图案误判为失线 除0x00外均用八路加权平均求位置,避免对每种位图硬编码 灰度读取诊断;八路加权平均
90°角点处冲过黑线并卡死 车辆仍以较高速度接近,四路角点又要求连续5帧,重车跨过后才触发保护 边缘3路先减速;完整4路改3帧确认;锁存方向后制动约120 ms再转向 角点过冲
90°转完后仍多偏几度 转向完成后又沿原方向原地找线,额外累积角度 取消原地找线;改为低速前进并由实时灰度误差重新收线 取消转后找线
车辆在转,但yaw沿目标反方向变化,误差越来越大 转向轮速方向、yaw符号与目标角正负没有统一;实测还发现右后电机和编码器极性同时反了 先独立验证每个轮子的正PWM与编码器正负,再统一轮速差、yaw和目标角的符号约定 Yaw符号问题;电机极性修复
速度PID数据看起来对称,但整车后退或原地转圈 电机极性和编码器方向同时配置错误,闭环会在错误物理方向上表现为“正常” 判断闭环前必须观察车轮真实方向;本例翻转右后电机极性并修正右后编码器方向 根因与修复
角度控制冲过目标后左右反打、来回振荡 近目标输出过硬,且过冲后控制器继续反向纠偏多个周期 降低近目标强度;误差跨零且过冲可接受时直接完成并停车 角度 PID 记录
灰度检测到角点后车辆提前停车或转弯几何位置不对 灰度板位于车辆旋转中心之前,传感器触发点不等于车体应转弯的位置 将前置距离作为动作参数;触发后保持航向继续走该距离,再制动转向 传感器前置距离
坡道检测角度异常,自动清零等待或静止显示仍不可信 IMU安装轴与车身轴不一致;DMP欧拉角不适合当前安装与坡道判定 明确车身前向轴,最终使用FIFO原始加速度与陀螺仪互补滤波 Pitch 完整试错记录
只用加速度计算坡度时,加减速会被误认为上坡或下坡 车辆纵向加速度与重力分量混在一起 用陀螺仪短期动态配合加速度长期校正,并设置状态、阈值和持续时间判定 互补滤波方案
MSPM0 RAM几乎耗尽,但工程没有实际动态分配 启动文件预留了12 KB Heap,静态占用不高却挤压可用RAM Heap可缩到约1 KB;Stack暂保留4.5 KB以容纳snprintf、浮点和DMP局部数组 RAM 与堆栈
切换烧录器后编译/烧录失败,或J-Link提示No PWR-AP detected 可能是工程配置切换失败,也可能只是目标未上电、SWD接线或调试器占用 脚本失败时恢复.uvprojx/.uvoptx并保留日志;DAP类错误优先检查供电、SWD和占用 失败恢复;J-Link 日志判断

推荐排查顺序

  1. 先看物理现象:供电、接线、轮子真实方向、机构是否卡住,不能只相信闭环数值。
  2. 再看原始输入:编码器计数、灰度位图、IMU FIFO和串口帧是否持续更新。
  3. 统一坐标与符号:电机正方向、编码器正方向、yaw正方向和任务目标必须一致。
  4. 检查状态机结束条件:超时、过零、连续帧、失线和停车逻辑往往比PID参数更先决定成败。
  5. 最后才调参数:每轮只改变一类参数,记录起点、目标、峰值、终点、耗时和现场扰动。
priority_high
核心经验

“数值闭环正常”不等于“物理方向正确”;“动作能到目标”也不等于“结束条件正确”。调车时应把传感器可信度、符号映射、状态机和控制参数分层验证。