64 lines
39 KiB
HTML
64 lines
39 KiB
HTML
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Motor_v2 专家评审 2026-10-19</title><style>body{font-family:-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;margin:0;background:#f5f6f8;color:#1f2328;line-height:1.6}
|
||
main{max-width:1200px;margin:0 auto;padding:24px}h1{font-size:26px;margin:0 0 4px}h2{border-left:4px solid #2f6feb;padding-left:10px;margin-top:36px}
|
||
.meta{color:#57606a;font-size:14px}.card{background:#fff;border-radius:8px;padding:16px 20px;box-shadow:0 1px 3px rgba(0,0,0,.08);margin:12px 0}
|
||
table{border-collapse:collapse;width:100%;background:#fff;font-size:13px}th,td{border:1px solid #d0d7de;padding:6px 8px;vertical-align:top;text-align:left}
|
||
th{background:#eef2f7;position:sticky;top:0}code{font-size:12px;background:#f0f2f5;padding:1px 4px;border-radius:3px;word-break:break-all}
|
||
.b{display:inline-block;padding:1px 8px;border-radius:10px;color:#fff;font-size:12px;white-space:nowrap}.b0{background:#b91c1c}.b1{background:#ea580c}.b2{background:#ca8a04}.b3{background:#2563eb}
|
||
tr.s0 td{background:#fef2f2}.kpi{display:flex;gap:12px;flex-wrap:wrap}.kpi div{flex:1;min-width:120px;background:#fff;border-radius:8px;padding:12px;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,.08)}
|
||
.kpi b{font-size:28px;display:block}pre{background:#0d1117;color:#e6edf3;padding:14px;border-radius:8px;overflow:auto;font-size:13px}</style></head><body><main>
|
||
<h1>Motor_v2 电机固件专家评审</h1>
|
||
<div class="meta">仓库 cbb/Motor_v2 · 分支 master · 评审基线提交 <code>86ca766</code>(2026-10-13 “feat(motor): 引入运行禁止机制重构电机控制安全系统”)· 评审日期 2026-10-19 · 静态评审,未实编/未上机</div>
|
||
<div class="kpi"><div><b style="color:#b91c1c">2</b>致命</div><div><b style="color:#ea580c">15</b>严重</div><div><b style="color:#ca8a04">22</b>一般</div><div><b style="color:#2563eb">11</b>建议</div><div><b>5.6</b>综合评分/10</div></div>
|
||
|
||
<h2>1. 项目概述与架构</h2>
|
||
<div class="card">GD32C103CB(Cortex-M4F,120 MHz)上的无刷电机 FOC 驱动固件,配 DRV8313 门驱动、KTH7823 磁编码器,CAN-FD(仲裁 1 Mbps / 数据 5 Mbps)与上位机通信;CMSIS-Toolbox csolution + AC6(microlib) 构建;应用位于 0x08012000,配合独立 BOOT 实现 OTA。最新提交引入了 <code>RunInhibit</code> 位图式“运行禁止”闸门,作为功率级唯一执行者。</div>
|
||
<pre>┌──────────────── GD32C103CB (Cortex-M4F @120MHz, APP @0x08012000) ────────────────┐
|
||
│ 中断层 │
|
||
│ ADC0 注入组(TIMER1 CH3 TRGO 触发) ─► 写三相电流 + 置 TASK_CURRENT_UPDATE [prio 0,0] │
|
||
│ TIMER2 50µs 节拍 ─► 置任务位(角度/速度/电机控制/1ms CAN …) [prio 0,1] │
|
||
│ TIMER1 中心对齐 PWM ≈20.0067kHz (ARR=2999) [prio 0,2] │
|
||
│ SysTick 1ms(delay/CPU 统计) [0] CAN RX0/TX/DMA0-CH4 [prio 1] │
|
||
├──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ 主循环 main.cpp:关中断取任务位快照 → do_task() │
|
||
│ ├ 高频:KTH7823 SPI 读角度 → α-β/卡尔曼 → run_inhibit.update() → FOC 模式分发 │
|
||
│ │ Clarke/Park → PID(电流/速度/位置/MIT) → 逆Park → SVPWM → Set_3Pwm │
|
||
│ ├ 低频:health(热模型/欠压/nFAULT) · power_manage · 喂狗 │
|
||
│ └ CAN:CAN-FD(仲裁1M/数据5M) 命令解析 · 周期反馈帧 · 参数持久化(flash.cpp EEPROM) │
|
||
├──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ 存储:EEPROM 模拟 0x0801F800-0x08020000(2×1KB) · 节点ID 乒乓双备份+CRC · OTA/备份区 │
|
||
└──────────────────────────────────────────────────────────────────────────────────┘</pre>
|
||
<table><tr><th>目录</th><th>主要文件</th><th>职责</th><th>规模(行)</th></tr><tr><td><code>Algorithm/</code></td><td>FOC.cpp/h、pid、Filter(α-β/卡尔曼)</td><td>坐标变换、SVPWM、多模式控制环、零位校准</td><td>~1.8k</td></tr><tr><td><code>device/</code></td><td>Can、ADC、Timer、ang_sensor、motor</td><td>CAN-FD 协议栈、注入采样、PWM、KTH7823 SPI、DRV8313 使能</td><td>~3.6k</td></tr><tr><td><code>sys/</code></td><td>flash、health、run_inhibit、initial、systick、config</td><td>参数持久化/OTA、保护、运行禁止闸门、初始化、时基</td><td>~3.0k</td></tr><tr><td><code>顶层</code></td><td>main.cpp、tasks.cpp</td><td>调度主循环、任务拆分</td><td>~0.4k</td></tr><tr><td><code>RTE/</code></td><td>GD32C10x 标准库、启动文件、链接脚本</td><td>厂商库(含本地修改)</td><td>—</td></tr></table>
|
||
|
||
<h2>2. 评审范围与方法</h2>
|
||
<div class="card"><ul>
|
||
<li>范围:Robot_V2/ 下全部应用源码(Algorithm、device、sys、main、tasks,约 9.6k 行)、RTE 启动文件/时钟/链接区域/中断文件、csolution/cproject/cbuild-pack/vcpkg、.gitignore、IDE 配置;厂商标准库仅比对与 .base 的差异。</li>
|
||
<li>专家组:①电机控制算法/FOC ②实时性/中断/DMA/MCU 架构 ③CAN-FD 通信协议 ④功能安全与故障保护 + Flash 存储 ⑤代码质量、构建与仓库卫生。各视角独立通读后交叉核对,关键结论由主评审逐行复核(M01、M02、M04、M07、M10、M19 等)。</li>
|
||
<li>既有记录:全仓搜索 .md/.html,仅根目录 <code>STATIC_ANALYSIS_NOTES.md</code>。逐条核对见第 4 节,修订版见 <a href="STATIC_ANALYSIS_NOTES_复核_2026-10-19.md">STATIC_ANALYSIS_NOTES_复核_2026-10-19.md</a>(原文件保留未改)。</li>
|
||
<li>严重度:<b>致命</b>=可能导致失控/烧毁/伤人;<b>严重</b>=保护失效、控制失效或数据丢失;<b>一般</b>=功能缺陷/可靠性隐患;<b>建议</b>=质量与可维护性。</li>
|
||
<li>局限:未编译、未上机、未示波;行号基于 86ca766。</li></ul></div>
|
||
|
||
<h2>3. 问题清单(50 条)</h2>
|
||
<table><tr><th>编号</th><th>严重度</th><th>领域</th><th>文件:行号</th><th>标题 / 现象</th><th>风险</th><th>修改建议</th></tr><tr class="s0"><td>M01</td><td><span class="b b0">致命</span></td><td>CAN/FOC</td><td><code>device/Can.cpp:1128-1132;Algorithm/pid.cpp:50,126</code></td><td><b>控制帧目标电流/速度直接作为 PID 限幅,未校验符号与有限性</b><br>目标电流 target_current、目标速度 target_velocity 被原样传入 set_limit();pid.cpp:50 _constrain(x,-limit,limit) 在 limit<0 时上下界颠倒;NaN/Inf 未过滤。</td><td>mode3/4/5 下发负值时 PID 输出被钉在 +|limit|,与误差方向无关,可导致反向满压、电机失控;电流环限幅(电压量纲)被设为电流值,单位错配。</td><td>协议层对所有浮点字段做 isfinite() 与范围夹紧;set_limit 内取 fabsf 并拒绝非正值;电流环输出限幅改用 limit_voltage/v_safe,与目标电流分离。</td></tr><tr class="s0"><td>M02</td><td><span class="b b0">致命</span></td><td>Flash/安全</td><td><code>sys/flash.cpp:304-321,393-404,887-899(format 后 save_system_config);sys/flash.cpp:77-84</code></td><td><b>EEPROM 页满重建时丢失电角度零位校准角</b><br>任一写入触发页满 → format() 擦除两页 → save_system_config(false) 只重写 6 个控制参数、CRC 与两个刷新周期,不重写 ADDR_CALIBRATION_ANGLE(0x07);若正是写校准角触发的页满,新校准角同样不会写入。上电回退默认 83°。</td><td>换向角错误 → 力矩方向/大小错误,速度环可能正反馈失控,且无任何告警(init 认为读取失败属正常首启)。</td><td>save_system_config 重建路径同时重写校准角(及将来所有独立记录);或改为双页轮转搬运最新有效记录;无有效校准角时置 INHIBIT 位禁止闭环并上报。</td></tr><tr class="s1"><td>M03</td><td><span class="b b1">严重</span></td><td>CAN/安全</td><td><code>sys/run_inhibit.h:19-29;device/Can.cpp(无命令超时逻辑)</code></td><td><b>无通信超时/心跳丢失停机</b><br>禁止来源仅 nFAULT/欠压/过热/采样失效/未使能,无 CAN 超时来源;收到最后一条控制帧后电机持续执行。</td><td>上位机死机、线缆脱落、总线 Bus-Off 时电机按最后目标一直运行(速度模式下持续转动)。</td><td>新增 INHIBIT_COMM_TIMEOUT:控制帧到达刷新时间戳,超时(可配置,如 100 ms)request(),收到新控制帧再 release;超时阈值可经 CAN 配置并持久化。</td></tr><tr class="s1"><td>M04</td><td><span class="b b1">严重</span></td><td>FOC/安全</td><td><code>Algorithm/FOC.cpp:59-63;sys/systick.cpp:164-168</code></td><td><b>零位校准在主循环阻塞约 1 s 且持续喂狗</b><br>calibrate_zero_position() 直接 motor_resume() + Set_3Pwm(ALIGN_PWM_DUTY) + delay_1ms(1000);delay_1ms 循环内 fwdgt_counter_reload()。</td><td>1 s 内欠压快检、nFAULT 轮询、热模型、CAN 处理全部停摆,看门狗也被持续喂而失去意义;对齐电流无限流、对齐后未判转子静止。</td><td>改为非阻塞状态机(每 50 µs 推进),期间 run_inhibit/保护任务照常运行;对齐时做电流闭环或占空比按 Vbus 归一;delay_1ms 内不得喂狗。</td></tr><tr class="s1"><td>M05</td><td><span class="b b1">严重</span></td><td>安全/FOC</td><td><code>Algorithm/FOC.cpp:426-431;sys/config.h(MAX_VCC 仅作读数有效判定)</code></td><td><b>无过压保护,Vbus>36 V 时 SVPWM 按旧电压归一</b><br>update_power 仅接受 8–36 V,超出时保留旧 Vdc;固件无过压禁止位,回馈泵升无处理。</td><td>制动/回馈时母线抬升,实际相电压被放大,叠加功率器件超压,存在击穿 MOSFET/驱动风险。</td><td>增加 INHIBIT_OVERVOLTAGE(带回差与防抖);超限时继续用实测 Vbus 归一;必要时短路制动或限制回馈电流。</td></tr><tr class="s1"><td>M06</td><td><span class="b b1">严重</span></td><td>实时性/安全</td><td><code>RTE/Device/GD32C103CB/startup_gd32c10x.s:178-181;gd32c10x_it.c 未编入工程</code></td><td><b>HardFault 等异常直接死循环,PWM 保持最后占空比</b><br>实际生效的是启动文件弱定义 `B .`,不关 PWM、不睡眠驱动,依赖约 50 ms 看门狗复位。gd32c10x_it.c 不在 cproject 编译列表且重复定义 SysTick_Handler。</td><td>异常期间三相按最后占空比输出最长约 50 ms(IRC40K 偏差下更不确定),可能过流/冲击。</td><td>实现强定义 HardFault/MemManage/BusFault/UsageFault/NMI:首先关 TIMER1 输出(CHxEN/POEN)并拉低 nSLEEP,再保存故障现场到 BKP/保留 RAM,最后复位。</td></tr><tr class="s1"><td>M07</td><td><span class="b b1">严重</span></td><td>安全</td><td><code>device/Can.cpp:1167-1168;device/motor.cpp:243-249;sys/run_inhibit.cpp:32-45</code></td><td><b>motReset 直接唤醒驱动,绕过运行禁止闸门的睡眠状态</b><br>RESET bit0 先 release(NFAULT) 再 motor_reset()(sleep→resume)。若其他禁止位仍在,闸门 stopped 已为 true,不会再次 motor_sleep(),驱动保持唤醒。</td><td>欠压/过热/未使能期间驱动被唤醒(PWM 虽被置 0,但违背单一执行者设计,DRV8313 故障锁存也被清除)。</td><td>motReset 仅 release(NFAULT),驱动睡眠/唤醒全部交给 RunInhibit::update();或在闸门中把 stopped 置 false 强制重新施加睡眠。</td></tr><tr class="s1"><td>M08</td><td><span class="b b1">严重</span></td><td>CAN/Flash</td><td><code>device/Can.cpp:940,1048,1175;sys/flash.cpp(writeNodeId/save_system_config/format)</code></td><td><b>运行中 Flash 擦写使控制环停摆</b><br>节点 ID、刷新周期、RESET bit2 存参等命令在主循环中直接擦写 Flash,不检查电机是否静止;FOC 控制环也在主循环(tasks.cpp:96-118)。</td><td>单 bank 擦页期间 CPU 取指停顿数十 ms,PWM 保持最后占空比,控制失效;可能触发看门狗复位。</td><td>仅在 control_mode==0 且被禁止/静止时允许持久化;否则延迟到停机或回 NACK;擦写前先 request(INHIBIT_FLASH_BUSY)。</td></tr><tr class="s1"><td>M09</td><td><span class="b b1">严重</span></td><td>CAN</td><td><code>device/Can.cpp:950,967-981</code></td><td><b>旧格式 SET_NODE_ID 未拦截广播</b><br>DLC 1/2 的旧格式允许 target_node==BROADCAST;扩展格式(约 1010 行)已拦截,旧格式没有。</td><td>一条广播帧可把总线上所有节点改为同一 ID,造成 ID 冲突、命令错发至多个电机。</td><td>旧格式同样拒绝广播目标,并要求魔数/二次确认;考虑废弃旧格式。</td></tr><tr class="s1"><td>M10</td><td><span class="b b1">严重</span></td><td>Flash</td><td><code>sys/flash.cpp:444,906;sys/health.cpp:195</code></td><td><b>一次欠压保存后本次上电所有存参被跳过</b><br>欠压路径 save_system_config(true) 将 system_config_saved 置 true 且永不清零,此后 444 行直接返回 OK。</td><td>欠压恢复后的参数修改、进 BOOT 前保存、format 后的重建全部被静默跳过 → 与问题 2 叠加,页满时整页参数丢失。</td><td>save 标志只用于“本次欠压已保存”,在欠压解除时清零;format 重建路径应强制保存(忽略该标志)。</td></tr><tr class="s1"><td>M11</td><td><span class="b b1">严重</span></td><td>Flash</td><td><code>sys/flash.cpp:413-430,161-181;main.cpp:107</code></td><td><b>EEPROM 无掉电原子性,CRC 错误时参数全部归零</b><br>format() 同时擦除两页,无备份页;format 可能在欠压保存路径中被触发;CRC 错误时全部参数回 0(含多圈角度 angle_set);main 不检查 flashEEP.init() 返回值,也不进 init_error_flags。</td><td>掉电/欠压期间擦写失败即全部配置丢失,上电以 0 参数运行;多圈位置丢失。</td><td>采用双页交替(有效页标记 + 搬运后再擦旧页);欠压时禁止 format;init 失败置初始化错误位并禁止运行。</td></tr><tr class="s1"><td>M12</td><td><span class="b b1">严重</span></td><td>安全</td><td><code>sys/health.cpp:98-103,212-213</code></td><td><b>过热/欠压解除后自动恢复运行</b><br>保护解除后不需上位机确认,电机直接按原模式、原目标继续动作;过热还会在 PROTECT_TIME 到期后按时间解除。</td><td>无人值守下电机突然重新转动,存在机械伤人/撞击风险。</td><td>故障解除后保留 INHIBIT_NOT_ENABLED(需上位机重新下发使能);过热解除改为温度回差而非定时。</td></tr><tr class="s1"><td>M13</td><td><span class="b b1">严重</span></td><td>实时性</td><td><code>device/Timer.cpp:75-107,139-141;sys/config.h:376,384</code></td><td><b>控制节拍与 PWM/采样不同步,频率不一致</b><br>TIMER2 产生 50 µs(20 kHz) 任务节拍;TIMER1 中心对齐 ARR=2999 → 周期 5998 clk ≈ 20.0067 kHz。两者约 150 ms 拍频一次。</td><td>采样到控制的延时在 0~50 µs 漂移,每秒约 6.7 帧被重复/丢弃,产生约 6.7 Hz 转矩纹波与噪声。</td><td>由 ADC 注入完成中断(或 TIMER1 更新事件)驱动 FOC 计算;修正 ARR 使周期整数匹配;去掉独立的 TIMER2 控制节拍。</td></tr><tr class="s1"><td>M14</td><td><span class="b b1">严重</span></td><td>实时性</td><td><code>device/Timer.cpp:329-342</code></td><td><b>PWM 比较值写入未开影子寄存器预装载</b><br>Set_3Pwm 直接写 CHxCV,全仓无 timer_channel_output_shadow_config 调用。</td><td>周期中途更新产生窄脉冲/不对称脉冲,扰动低边电流采样窗口并增加开关损耗。</td><td>对 CH0~CH2 开启 TIMER_OC_SHADOW_ENABLE,并开启 ARR 预装载。</td></tr><tr class="s1"><td>M15</td><td><span class="b b1">严重</span></td><td>实时性</td><td><code>main.cpp:67-80;device/Timer.cpp:139</code></td><td><b>无任务超时/丢拍检测</b><br>主循环一轮超过 50 µs 时任务位被静默合并,控制直接丢拍,无计数无上报。</td><td>高负载(CAN 突发、Flash 擦写)时控制周期失真不可见,调试困难,严重时失稳。</td><td>在 TIMER2 ISR 中检测上次置位未被取走则累加 overrun 计数,通过 CAN 状态帧上报;超阈值请求禁止。</td></tr><tr class="s1"><td>M16</td><td><span class="b b1">严重</span></td><td>FOC</td><td><code>Algorithm/pid.cpp:64-68;device/ang_sensor.h:103-111;Algorithm/FOC.cpp:509-510</code></td><td><b>NaN/Inf 目标值导致积分污染与死循环</b><br>NaN 进 PID 后斜坡 fminf/fmaxf 使输出按最大速率爬升且积分永久为 NaN;Inf 进 normalizeAngle360 的 while 循环永不结束(mode6 开环角度累加路径)。</td><td>控制输出失控或主循环卡死(之后仅靠看门狗复位,期间 PWM 保持)。</td><td>协议层过滤非有限值;normalizeAngle360 改用 fmodf 并对非有限输入返回 0;PID 入口检测 isfinite 并复位。</td></tr><tr class="s1"><td>M17</td><td><span class="b b1">严重</span></td><td>FOC/安全</td><td><code>device/ang_sensor.cpp:78,160-176</code></td><td><b>角度传感器读失败/断线未升级为禁止运行</b><br>读失败返回 FAULT_RECEIVE 并保留旧角度,无错误计数、不请求 inhibit;读回 0xFFFF(MISO 浮空)被当作有效;TRANS 标志等待无超时。</td><td>编码器断线时以陈旧/错误角度换向,力矩方向错误,可能飞车。</td><td>连续 N 次读失败或校验失败 → request(INHIBIT_ENCODER_FAULT);识别 0x0000/0xFFFF 等非法值;TRANS 等待加超时。</td></tr><tr class="s2"><td>M18</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:1117</code></td><td><b>控制帧不校验长度与模式范围</b><br>短帧缺失字段按 0 处理(P 增益等被清零);P_config、control_mode 无范围检查,tasks.cpp:113 对非法模式仅不执行。</td><td>错误/截断帧改变控制参数,行为不可预期。</td><td>按命令定义校验 DLC 与字段范围,不合法回 NACK 且不修改任何状态。</td></tr><tr class="s2"><td>M19</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:1187</code></td><td><b>RESET 深睡眠位判断运算符优先级错误</b><br>`eigth_byte & 0x80 >> 7` 等价于 `eigth_byte & 1`,实际判断 bit0 而非 bit7。</td><td>上位机置 bit0 时意外进入深睡眠,bit7 反而无效。</td><td>改为 `(eigth_byte >> 7) & 0x01` 或 `eigth_byte & 0x80`。</td></tr><tr class="s2"><td>M20</td><td><span class="b b2">一般</span></td><td>CAN/FOC</td><td><code>device/Can.cpp:1177-1181</code></td><td><b>运行中可清零角度</b><br>RESET bit3 不检查状态即 angle_set(0) 并重置卡尔曼。</td><td>位置模式下目标与实际角度瞬间跳变,电机猛冲。</td><td>仅在 mode0/禁止状态下允许,或同时把 target_angle 置为新零点。</td></tr><tr class="s2"><td>M21</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:1395,744</code></td><td><b>TX 路径主循环与 ISR 竞争邮箱、乱序</b><br>CANFD_AsyncSend 在主循环直接 can_message_transmit,同时 TX 中断从队列填邮箱。</td><td>选邮箱竞态、新帧绕过队列先发,反馈帧时序错乱。</td><td>统一经 TX 队列发送,仅在临界区内检测空邮箱并启动。</td></tr><tr class="s2"><td>M22</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:896-924</code></td><td><b>BOOT 帧无魔数/校验</b><br>任何基础 ID 0x0000 的帧都可触发进 BOOT 或节点静默。</td><td>误触发导致电机离线。</td><td>增加魔数 + 目标节点 + 停机状态校验。</td></tr><tr class="s2"><td>M23</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:575,630</code></td><td><b>远程帧未过滤</b><br>rx_ft 读出后未判断,远程帧被当作数据帧处理。</td><td>DLC 非 0 的远程帧携带随机数据进入命令解析。</td><td>丢弃 RTR 帧或单独处理。</td></tr><tr class="s2"><td>M24</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:359,413,1241-1251</code></td><td><b>总线错误/Bus-Off 不可见,FIFO 溢出分支不可达</b><br>未开错误中断,Bus-Off 不上报也不纳入保护;只开 RFNE,FIFO 满/溢出处理永远进不去。</td><td>总线故障时静默失联(与无通信超时叠加)。</td><td>开启 EWIE/BOIE/ERRNIE,记录 TEC/REC,Bus-Off 时请求禁止并按策略恢复。</td></tr><tr class="s2"><td>M25</td><td><span class="b b2">一般</span></td><td>CAN</td><td><code>device/Can.cpp:394-407;sys/config.h:341</code></td><td><b>硬件过滤器全收,默认 STATUS 1 kHz×48 B</b><br>所有帧靠软件过滤。</td><td>多节点组网时总线与中断负载偏高。</td><td>按节点 ID 配置硬件过滤器;默认刷新率下调。</td></tr><tr class="s2"><td>M26</td><td><span class="b b2">一般</span></td><td>CAN/Flash</td><td><code>device/Can.cpp:1034</code></td><td><b>反馈刷新周期无下限且每次设置都写 Flash</b><br>可配置为极小周期;频繁设置磨损 Flash。</td><td>总线饱和、Flash 寿命下降。</td><td>设定下限(如 1 ms);值未变不写(write 已跳过同值,但应在命令层限频)。</td></tr><tr class="s2"><td>M27</td><td><span class="b b2">一般</span></td><td>Flash</td><td><code>sys/flash.cpp:230-257</code></td><td><b>readFloat 无保留字节防护,可能误读 CRC 字</b><br>read() 在 206 行用保留字节排除 CRC 字,readFloat 没有同类防护,扫描至头部 CRC 区。</td><td>记录不存在时可能把低字节为 0x07 的 CRC 字当作校准角返回,calib==0 判定拦不住。</td><td>float 记录格式加入类型标记/校验位,或扫描时跳过 CRC 区。</td></tr><tr class="s2"><td>M28</td><td><span class="b b2">一般</span></td><td>Flash/BOOT</td><td><code>sys/flash.cpp:929-1016,1077-1098</code></td><td><b>OTA 标志与备份区搬运缺乏原子性</b><br>OTA_SET 擦页后未写回目标值;前序写失败后 FMC 已锁,后续擦除失败;move_app_data 擦除备份区前未先作废 BACKUP_AVAL,48 页擦除期间关中断且不喂狗;新 APP 首启即覆盖备份区,A/B 回滚失效;地址注释过时。</td><td>升级中途掉电变砖或回滚失效。</td><td>先作废标志再擦写、分段喂狗、确认新 APP 正常运行后再覆盖备份;更新注释。</td></tr><tr class="s2"><td>M29</td><td><span class="b b2">一般</span></td><td>安全</td><td><code>Algorithm/FOC.cpp:466-468;sys/config.h:407-408</code></td><td><b>欠压快检基准只升不降,阈值注释不一致</b><br>vbus_reference 峰值保持无衰减;回馈短时抬高后恢复正常即被判跌幅>5 V;高母线时恢复阈值(×0.9)低于触发阈值(−5 V)会抖动;注释默认 8 V,实际 12 V。</td><td>误跳闸、保护抖动。</td><td>基准改为慢速低通跟踪;触发/恢复阈值统一并带回差;更正注释。</td></tr><tr class="s2"><td>M30</td><td><span class="b b2">一般</span></td><td>FOC</td><td><code>Algorithm/FOC.h:47;Algorithm/FOC.cpp:429,413;device/Timer.cpp:329-342</code></td><td><b>v_safe 初值与运行值不一致,占空比无统一限幅</b><br>初值 Vdc/1.05,运行值 Vdc×1.1;首次 update_power 前 T0 可能<0 → 负占空比;Set_3Pwm 不限幅,负浮点转 u32 为未定义行为。</td><td>上电首拍产生异常占空比。</td><td>Set_3Pwm 内统一夹紧 [0, Dmax];v_safe 初值与运行公式一致。</td></tr><tr class="s2"><td>M31</td><td><span class="b b2">一般</span></td><td>FOC/实时性</td><td><code>sys/config.h:395-397;Algorithm/FOC.cpp:95-98</code></td><td><b>最高占空比约 95.5% 超出采样窗口要求(约 90%)</b><br>高调制时 W 相低边导通时间不足,电流读数趋零,经共模去除把误差分摊到 Iq;注释“每周期匹配两次”与实际单沿触发不符。</td><td>高速/高负载时转矩估计失真。</td><td>限制最大占空比或做两相动态选相采样;修正注释。</td></tr><tr class="s2"><td>M32</td><td><span class="b b2">一般</span></td><td>FOC</td><td><code>Algorithm/FOC.cpp:526,546,636-640,751</code></td><td><b>多模式积分饱和与热路径改写用户限幅</b><br>526 行在热路径直接改写 limit_voltage;mode1/4/5 由 SVPWM 或外部钳位限压,但 PID 内部 limit 仍为 Vdc,积分 windup。</td><td>大扰动后超调、恢复慢。</td><td>各环 PID 的 limit 与实际执行限幅一致;不在控制路径改写用户参数。</td></tr><tr class="s2"><td>M33</td><td><span class="b b2">一般</span></td><td>实时性</td><td><code>device/ADC.cpp:62-63,467-481</code></td><td><b>电流三相读取与清 ready 无原子保护(撕裂)</b><br>ISR 可能在读取三相中途写入新帧,三相来自不同帧,或丢失一帧 ready。</td><td>偶发电流尖峰/误判采样失效。</td><td>读取侧用 LDREX/STREX 或序号双读校验(seq 前后相同才接受),无需长临界区。</td></tr><tr class="s2"><td>M34</td><td><span class="b b2">一般</span></td><td>实时性</td><td><code>main.cpp:53-65;sys/initial.cpp:255</code></td><td><b>看门狗启用晚,复位原因未读取</b><br>看门狗在 OTA 初始化与磁检之后才开启,初始化失败循环中既无看门狗也不喂狗;未读 RCU_RSTSCK。</td><td>上电阶段卡死不自动复位;现场无法区分看门狗/软件/欠压复位。</td><td>尽早启用看门狗;启动时读取并上报复位原因后清除。</td></tr><tr class="s2"><td>M35</td><td><span class="b b2">一般</span></td><td>构建</td><td><code>startup_gd32c10x.s:42,53;regions_GD32C103CB.h:102-103</code></td><td><b>栈/堆大小两处定义不一致</b><br>启动文件 Stack 0x800/Heap 0x800;regions 头文件 0x200/0xC00。缺少最坏栈深分析。</td><td>实际生效值不明,存在栈溢出隐患。</td><td>以 map 确认生效值并统一一处;用 armlink --info=stack/--callgraph 核算最坏栈深;栈底放哨兵。</td></tr><tr class="s2"><td>M36</td><td><span class="b b2">一般</span></td><td>FOC</td><td><code>device/ang_sensor.cpp(SPI 初始化 prescale)</code></td><td><b>SPI 分频与注释不符</b><br>prescale=24 实际映射为 /16,约 7.5 MHz,注释写 5 MHz。</td><td>超出传感器/走线裕量时误码。</td><td>按手册确认 KTH7823 最高时钟并使用正确枚举。</td></tr><tr class="s2"><td>M37</td><td><span class="b b2">一般</span></td><td>安全</td><td><code>sys/health.cpp:150-153,252-256;Algorithm/FOC.cpp:89-91</code></td><td><b>若干故障只上报不禁止</b><br>温度传感器失效(255)被置 0,温度保护静默失效;编码器 MagStatus 只上报;单相电流失效时用陈旧 Kalman 值继续闭环;无软件瞬时过流与堵转保护。</td><td>保护覆盖不全。</td><td>传感器失效 → 禁止运行;增加软件过流(相电流阈值+计数)与堵转(Iq 大且速度≈0 持续 T)保护。</td></tr><tr class="s2"><td>M38</td><td><span class="b b2">一般</span></td><td>构建</td><td><code>Robot_V2/Robot_V2.cproject.yml:misc;Robot_V2.csolution.yml:build-types</code></td><td><b>编译选项与 build-types 冲突,链接诊断大面积抑制</b><br>cproject 对所有构建强制 -O3,覆盖 Debug optimize:none / Release balanced;Link 抑制约 70 个 L62xx/L63xx 诊断(含 L6329 重复)。</td><td>Debug 不可调;真实链接问题被掩盖。</td><td>移除 cproject 中 -O3,由 build-types 控制;只保留确认无害的个别抑制。</td></tr><tr class="s2"><td>M39</td><td><span class="b b2">一般</span></td><td>构建</td><td><code>Robot_V2/RTE/Device/GD32C103CB/*.c 与 *.base@1.0.0</code></td><td><b>厂商库文件被本地修改(与 .base 差异)</b><br>25 个 RTE 设备文件与 base 不同(如 system_gd32c10x.c 改了 VECT_TAB_OFFSET=APP_BASE 0x08012000 并加注释,timer/can/adc 库也有改动)。</td><td>pack 更新时修改被覆盖或合并困难,应用基址隐藏在厂商文件中。</td><td>将应用基址等改动移到用户代码(如 SystemInit 后设置 SCB->VTOR)或文档化;尽量恢复厂商库原样。</td></tr><tr class="s3"><td>M40</td><td><span class="b b3">建议</span></td><td>构建</td><td><code>仓库根目录</code></td><td><b>缺少 README / 协议文档 / 构建说明 / CI</b><br>无 README,CAN 命令集只存在于代码注释。</td><td>新人与上位机开发者难以对接,回归无保障。</td><td>补 README(硬件、构建、烧录、地址布局)与 CAN 协议表;增加 cbuild CI 与主机端 PID/SVPWM 单元测试。</td></tr><tr class="s3"><td>M41</td><td><span class="b b3">建议</span></td><td>代码质量</td><td><code>Robot_V2/Algorithm/thermal.cpp,.h</code></td><td><b>空壳文件未加入工程</b><br>thermal.cpp 仅有文件头注释,未在 cproject 中。</td><td>死代码干扰阅读。</td><td>删除或实现后加入工程(热模型目前在 sys/health.cpp)。</td></tr><tr class="s3"><td>M42</td><td><span class="b b3">建议</span></td><td>代码质量</td><td><code>RTE/Device/GD32C103CB/gd32c10x_it.c</code></td><td><b>死文件且重复定义 SysTick_Handler</b><br>不在编译列表。</td><td>误加入工程会产生重复定义。</td><td>删除,或改为强定义异常处理的唯一来源。</td></tr><tr class="s3"><td>M43</td><td><span class="b b3">建议</span></td><td>构建</td><td><code>.continue/、.vscode/c_cpp_properties.json</code></td><td><b>IDE 配置入库</b><br>Continue MCP 模板(无敏感信息)与 windows-gcc-x86 IntelliSense 配置(与 AC6 目标不符)。</td><td>噪声、误导。</td><td>加入 .gitignore 或改为 arm 交叉配置。</td></tr><tr class="s3"><td>M44</td><td><span class="b b3">建议</span></td><td>代码质量</td><td><code>sys/initial.h:10-176,42;sys/initial.cpp:7,15</code></td><td><b>头文件与 C/C++ 混用问题</b><br>头文件中定义 static 变量;C++ 类包在 extern "C" 中;initial.h 被重复包含;fputc 使用 throw()。</td><td>每个编译单元各有副本、链接语义混乱。</td><td>static 变量移入 .cpp;extern "C" 只包 C 接口;去掉 throw()。</td></tr><tr class="s3"><td>M45</td><td><span class="b b3">建议</span></td><td>代码质量</td><td><code>sys/flash.h:59 等</code></td><td><b>魔数与命名不一致</b><br>ADDR_VEL_LIMIT 实际存 target_current;多处魔数。</td><td>维护误用。</td><td>重命名、集中常量。</td></tr><tr class="s3"><td>M46</td><td><span class="b b3">建议</span></td><td>FOC</td><td><code>Algorithm/FOC.cpp(Ud 恒 0);Algorithm/Filter.cpp(CurrentKalman Q=10,R=5e-5)</code></td><td><b>Id 未闭环;电流卡尔曼增益≈1 等于不滤波</b><br>Ud 恒为 0;Kalman 参数使滤波失效。</td><td>高速/弱磁场景能力受限;无效计算消耗 CPU。</td><td>增加 Id 电流环(目标 0);重新整定或移除该滤波器。</td></tr><tr class="s3"><td>M47</td><td><span class="b b3">建议</span></td><td>FOC</td><td><code>Algorithm/FOC.cpp(SVPWM)</code></td><td><b>SVPWM 量纲约定未注明</b><br>Uq 量纲为相幅值的 √3 倍(数值验证扇区与角度误差约 1e-13,变换正确)。</td><td>参数整定易误解。</td><td>在注释中写明电压归一化约定。</td></tr><tr class="s3"><td>M48</td><td><span class="b b3">建议</span></td><td>CAN</td><td><code>device/Can.cpp:366-390</code></td><td><b>CAN-FD 位时序裕量待确认</b><br>数据段 5 Mbps 仅 6 tq、SJW=1,TDCO=4 单位需对照手册。</td><td>长线/多节点时误码。</td><td>按手册核算采样点(70–80%),示波验证 TDC。</td></tr><tr class="s3"><td>M49</td><td><span class="b b3">建议</span></td><td>实时性</td><td><code>system_gd32c10x.c:857</code></td><td><b>未开启时钟监控 CKM</b><br>晶振起振失败卡在 while(1)。</td><td>板级故障时无安全输出。</td><td>开启 CKM,NMI 中安全停机。</td></tr><tr class="s3"><td>M50</td><td><span class="b b3">建议</span></td><td>CAN</td><td><code>device/Can.cpp:331,1292;Can.h:226-228</code></td><td><b>CAN RX DMA 收益有限、残留死代码</b><br>M2M 按帧启动 DMA;通道 5 中断为空;旧 rx_message_buffer 队列无人调用。</td><td>额外中断与维护负担。</td><td>评估直接读邮箱;删除死代码。</td></tr></table>
|
||
|
||
<h2>4. 既有评审(STATIC_ANALYSIS_NOTES.md)结论跟踪</h2>
|
||
<table><tr><th>条目</th><th>原结论</th><th>当前状态</th><th>证据 / 说明</th></tr><tr><td>SUP-GLB-001 编译期常量(ADC/health/config constexpr)</td><td>误报/偏离</td><td><b>仍成立</b></td><td>ADC.cpp:22-47;health.cpp:107;config.h 全部为 constexpr</td></tr><tr><td>硬件单例 FOC / sampler / CAN0_transmiter / KTH7823 / thermalController / Version</td><td>接受单例</td><td><b>仍成立</b></td><td>FOC.cpp:28;ADC.cpp:60;Can.cpp:86;ang_sensor.cpp:39;health.cpp:104;initial.cpp:218</td></tr><tr><td>TaskScheduler scheduler 短临界区</td><td>已保护,接受</td><td><b>仍成立</b></td><td>main.cpp:68-72 关中断取快照并清位;taskFlags volatile(initial.h:75);两写入方同抢占级</td></tr><tr><td>CAN RX/TX 队列临界区与 PRIMASK 恢复</td><td>已保护,接受</td><td><b>基本成立,但有遗漏</b></td><td>Can.cpp:644-731、480;遗漏:主循环 CANFD_AsyncSend 直接写邮箱与 TX ISR 竞争(Can.cpp:1395/744,见M21)</td></tr><tr><td>ADC injected 电流缓存不加临界区</td><td>保留现状</td><td><b>风险仍在</b></td><td>ADC.cpp:62-63、467-481 存在三相撕裂/丢 ready(M33);建议序号双读而非 PRIMASK</td></tr><tr><td>“加 PRIMASK 后实机连续重启”归因于时序</td><td>归因时序</td><td><b>归因不成立</b></td><td>10 周期级临界区不足以致复位;更可能是 update_current 提前返回分支(ADC.cpp:468)未恢复中断 + 与 systick 改动捆绑未隔离 + 50 ms 看门狗(IRC40K)余量小;无复位原因记录无法定位</td></tr><tr><td>systick DWT CPU 占用统计</td><td>允许小误差</td><td><b>仍成立</b></td><td>systick.cpp:75-79、193-206;仅诊断值</td></tr><tr><td>ang_sensor SPI DMA 完成标志</td><td>接受 volatile 标志</td><td><b>已过时</b></td><td>已改为寄存器轮询 spi_rw_half_word_polling(ang_sensor.cpp:57-82),标志不存在;新问题:TRANS 等待无超时(:78)</td></tr><tr><td>Timer total_us100_counter</td><td>仅 ISR 使用,接受</td><td><b>仍成立</b></td><td>Timer.cpp:19、149-153;可改为 ISR 内 static</td></tr><tr><td>health voltage_state / last_vbus_slow</td><td>模块私有,接受</td><td><b>仍成立(有残留)</b></td><td>health.cpp:112-113 文件内 static;last_vbus_slow 只写不读</td></tr><tr><td>SUP-CMP-001 normalizeAngle360 浮点比较</td><td>无需容差</td><td><b>比较结论成立,但有新缺陷</b></td><td>ang_sensor.h:103-111 输入 Inf/NaN 时死循环(M16)</td></tr><tr><td>printf:FOC 齿槽校准打印位于 FOC_DEBUG</td><td>正式版不编译</td><td><b>已过时</b></td><td>9fbd9c3 已移除齿槽补偿;FOC_DEBUG 仅剩 motor.cpp:245 一处</td></tr><tr><td>versionToString 去 snprintf</td><td>已改</td><td><b>仍成立</b></td><td>initial.cpp:302-346</td></tr><tr><td>AUTO-MET-001 calibrate_cogging_tick 拆分</td><td>已拆分</td><td><b>已过时</b></td><td>函数随齿槽功能删除;Can.cpp:1186 bit4 改为保留位</td></tr><tr><td>AUTO-MET-001 communication_check 拆分</td><td>已拆分</td><td><b>已落实</b></td><td>Can.cpp:841-1115</td></tr><tr><td>AUTO-MET-001 do_task 拆分</td><td>已拆分</td><td><b>已落实</b></td><td>tasks.cpp:296-399</td></tr><tr><td>“实编仍需在 Keil/CMSIS/cbuild 环境验证”</td><td>待验证</td><td><b>仍未验证</b></td><td>本次评审为静态评审,未实编;cproject -O3 覆盖 build-types(M38)需一并处理</td></tr></table>
|
||
|
||
<h2>5. 亮点</h2>
|
||
<div class="card"><ul>
|
||
<li><code>RunInhibit</code> 位图闸门:功率级动作收拢为单一执行者,上电默认 INHIBIT_NOT_ENABLED,闸门位于模式分发之前,非法模式也能封住输出。</li>
|
||
<li>节点 ID 双页乒乓 + 魔数 + 计数器 + CRC,写后校验重试,写成功才回 ACK。</li>
|
||
<li>ADC0 只跑注入组、VBUS/温度移到 ADC1 连续模式,消除规则/注入组互扰;电流零点配平带独立错误位上报。</li>
|
||
<li>Clarke/Park/逆 Park 与 SVPWM 数值正确;PID Tustin 积分 + 测量值微分低通 + 条件积分抗饱和设计合理。</li>
|
||
<li>CAN DLC 合法化(CANFD_NormalizeLength)、DWT 按任务统计负载、Doxygen 注释完整。</li></ul></div>
|
||
|
||
<h2>6. 优先级修复路线图</h2><div class="card"><ol>
|
||
<li><b>P0(上电机前必须修,1–2 天)</b>:M01 控制帧输入校验与 PID 限幅取正;M02/M10 EEPROM 重建重写校准角并修正 system_config_saved;M03 通信超时禁止;M06 HardFault 先关 PWM;M07 motReset 交给闸门;M16 NaN/Inf 过滤。</li>
|
||
<li><b>P1(一周内)</b>:M04 零位校准非阻塞化、delay 不喂狗;M05 过压保护;M08 运行中禁止 Flash 擦写;M09 广播改 ID 防护;M12 故障解除需重新使能;M17 编码器故障禁止;M19 深睡眠位优先级 bug。</li>
|
||
<li><b>P2(迭代内)</b>:M13/M14/M15 由 ADC 中断驱动 FOC、影子寄存器、丢拍计数;M11 EEPROM 双页原子化;M33 电流序号双读;Bus-Off/错误中断;看门狗前移与复位原因上报;补软件过流/堵转。</li>
|
||
<li><b>P3(持续改进)</b>:构建选项清理(-O3、诊断抑制)、厂商库还原、README/协议文档/CI/主机单元测试、死代码清理、Id 闭环。</li></ol></div>
|
||
|
||
<h2>7. 总结评分</h2>
|
||
<table><tr><th>维度</th><th>评分</th><th>说明</th></tr><tr><td>功能安全与故障保护</td><td><b>5.0</b> / 10</td><td>有统一运行禁止闸门与上电默认禁止,但缺通信超时/过压/过流/编码器故障禁止,故障后自动恢复</td></tr><tr><td>电机控制算法</td><td><b>6.5</b> / 10</td><td>变换与离散化正确;限幅符号、NaN、windup、采样窗口问题</td></tr><tr><td>实时性与中断</td><td><b>6.0</b> / 10</td><td>执行模型清晰、临界区合理;节拍与 PWM 不同步、无丢拍检测、异常不关 PWM</td></tr><tr><td>CAN 通信协议</td><td><b>5.5</b> / 10</td><td>DLC 合法化、ACK 回写较好;输入校验、Bus-Off、广播防护不足</td></tr><tr><td>参数存储/Flash</td><td><b>4.5</b> / 10</td><td>节点 ID 存储设计好;EEPROM 重建丢校准角、无原子性、标志位逻辑缺陷</td></tr><tr><td>代码质量与可维护性</td><td><b>6.5</b> / 10</td><td>注释充分(Doxygen)、函数已拆分;头文件/死代码/魔数</td></tr><tr><td>构建与仓库卫生</td><td><b>5.0</b> / 10</td><td>csolution 规范;-O3 覆盖、诊断抑制、厂商库改动、无 README/CI</td></tr><tr><td><b>综合</b></td><td><b>5.6</b> / 10</td><td>架构方向正确、近期在安全闸门上投入明显;但输入校验、通信超时、参数存储一致性三处缺陷可直接导致失控,修完 P0 前不建议无人值守或带负载运行。</td></tr></table>
|
||
<p class="meta">本报告由专家评审生成,仅新增 docs/ 文件,未改动任何源码。</p>
|
||
</main></body></html> |