结论:`MultiWheelC\Fleet` 当前的数学链路作为“第一版组件”是成立的,现有 Fleet 数学测试共47个场景全部通过,没有发现明显的坐标系或符号错误。但它还不是可以直接上双车实车的完整工程方案,`FleetStateEstimator` 的等权平均尤其需要增加鲁棒性。 ## 正负平均为0是不是问题 不一定,很多时候这正是期望结果。 假设两辆车反算出的车队中心分别为: ```text 车辆1候选中心:+20mm 车辆2候选中心:-20mm ``` 平均得到: ```text 车队中心:0mm ``` 但代码随后还会计算每辆车相对于这个中心的布局误差: ```text 车辆1相对误差:+20mm 车辆2相对误差:-20mm ``` 所以并不是误差消失了,而是被分成了: ```text 公共分量:车队整体中心运动 相对分量:成员之间的队形变形 ``` 这正好符合当前架构: - 公共分量交给 `FleetController` 控制车队中心; - 相对分量交给 `FleetCoordinator` 减速,以及 `FleetMemberCommandCorrector` 小幅纠偏。 如果两辆车反算结果都是 `+20mm`: ```text 平均中心:+20mm 成员相对误差:接近0 ``` 这表示整个车队共同移动了20mm,而队形没有变,也符合物理意义。 ## 当前不是“无条件直接平均” 当前 [FleetStateEstimator.cs](/D:/Users/Desktop/入职培训/停车机器人/MyParking/MultiWheelC/Fleet/FleetStateEstimator.cs:277) 实际执行的是: 1. 将成员状态对齐到统一时刻。 2. 每辆车根据固定布局反算候选车队中心。 3. 检查任意两辆车反算的中心位置差和航向差。 4. 差异超过阈值,直接返回状态不可用。 5. 只有所有候选结果基本一致,才平均: - X/Y算术平均; - Yaw圆周平均。 6. 根据融合后的中心重新计算每辆车的布局误差。 所以如果: ```text 车辆1:+200mm 车辆2:-200mm ``` 两者相差400mm,只要超过 `_maximumPositionDisagreementMeters`,代码会在平均前拒绝本周期状态,不会把它们平均成0后继续运行。 Yaw也不是普通平均。例如: ```text +179°和-179° ``` 圆周平均会得到接近 `±180°`,而不是错误地得到 `0°`。 ## 当前平均真正的问题 它隐含了一个假设: > 所有成员定位精度相同、可信度相同,而且没有异常值。 对你目前的 Detour 情况,这个假设不够可靠。 假如真实车队中心是0,而: ```text 车辆1定位正常:0mm 车辆2发生小范围跳变:+60mm ``` 当前等权平均会得到: ```text 估计中心:+30mm ``` 如果60mm还没有超过候选差异阈值,这个错误会进入车队中心控制。 当前估计器存在三个主要限制: - 所有成员等权,没有利用定位质量、数据年龄和历史稳定性; - 单个异常成员超过阈值时,会使整个车队状态不可用,不能隔离坏成员; - 它是单帧估计,没有利用上一周期车队中心和轮速预测判断哪辆车更可信。 而且你目前主要是双车。两辆车互相矛盾时不存在“多数票”: ```text 车1说中心在A 车2说中心在B ``` 只看这两个当前观测,无法判断谁正确。必须引入: - 上一周期车队状态; - 轮速运动预测; - 每辆车定位健康状态; - 数据新鲜度; - 连续多帧确认。 ## 更成熟、适合你的方案 我建议采用: > 带时间预测和异常门控的鲁棒加权 SE(2) 融合。 不需要现在就上复杂的因子图或完整EKF。 ### 第一步:保留当前反算方法 继续让每辆车计算: ```text 候选车队位姿 = 成员实际世界位姿 × 成员固定布局位姿的逆 ``` 这部分当前是正确的,不需要推翻。 ### 第二步:增加上一状态预测 根据上一周期车队中心和轮组速度预测: ```text PredictedFleetPose ``` 然后每辆车的候选中心分别与预测值比较: ```text 位置创新 航向创新 ``` 这样双车出现分歧时,可以判断: ```text 车1与预测连续 车2突然跳变 → 暂时拒绝车2,而不是两车平均或全队立即失败 ``` ### 第三步:成员单独门控 每个成员分别检查: - 状态是否可用; - 数据是否过期; - 相对预测的位置创新是否合理; - 相对预测的航向创新是否合理; - 是否连续多帧异常; - 后续是否连续多帧恢复。 不要只做当前这种“成员之间两两比较”。 ### 第四步:对通过门控的候选加权融合 权重可以先用简单工程等级: ```text 健康且新鲜 → 权重1.0 轻度退化或较旧 → 较低权重 正在异常确认 → 权重0 ``` 以后 Detour 如果能提供协方差,再改成按协方差加权。 对残差使用 Huber 一类鲁棒损失会让正常小误差仍按最小二乘处理,而异常大残差的影响被压低;这是成熟估计库采用的标准做法,[GTSAM官方文档](https://gtsam.org/doxygen/a04439.html)也明确给出了 Huber 在迭代重加权最小二乘中的权重形式。 ### 第五步:输出融合健康等级 建议以后区分: ```text Healthy 所有有效成员一致,正常融合 Degraded 有一个成员被拒绝,暂时依赖剩余成员和预测,整队限速 Unavailable 所有成员都不可信,或双车分歧且无法判断谁正确 短时保持轮速预测,随后停车 ``` ## 更严格的数学形式 成熟版本可以直接求一个最符合全部刚体观测的车队位姿: \[ T_f^*= \arg\min_{T_f} \left[ w_p\|\log(T_{pred}^{-1}T_f)\|^2+ \sum_i w_i\,\rho\left( \|\log((T_fT_{layout,i})^{-1}T_{actual,i})\|^2 \right) \right] \] 它表示: - `T_f`:要求解的车队实际中心; - `T_layout,i`:第i辆车的固定布局; - `T_actual,i`:第i辆车当前测量位姿; - `T_pred`:根据历史和轮速得到的预测中心; - `w_i`:成员可靠性; - `ρ`:Huber等鲁棒损失。 这是“鲁棒刚体拟合”,比单纯平均更完整,同时利用位置和航向。普通最小二乘刚体配准本身就是成熟方法;鲁棒损失是在异常观测下限制单个数据影响的标准扩展。[刚体点集最小二乘原始问题说明](https://ntrl.ntis.gov/NTRL/dashboard/searchResults/titleDetail/PB91135772.xhtml) 如果将来各车能提供可信协方差,但不同SLAM估计之间的相关性未知,可以考虑 Covariance Intersection;它专门处理未知交叉相关性的估计融合。[Julier与Uhlmann的SLAM融合论文](https://www.sciencedirect.com/science/article/abs/pii/S0921889006001436) 但你目前拿不到Detour协方差,所以现在直接上CI没有足够输入。 ## 对当前项目的建议 当前先不要推翻 `FleetStateEstimator`。合理的推进顺序是: 1. 保留现有布局反算、时间对齐、圆周Yaw平均和成员误差计算。 2. 增加“上一车队状态+轮速”的中心预测。 3. 把两两一致性检查改成“每成员相对预测的独立门控”。 4. 对通过门控的成员做加权圆周平均或小规模Huber融合。 5. 允许单个成员短时降级,不要一帧异常就让整个估计不可用。 6. 双车无法判断谁正确时,短时间用预测,仍无法恢复再停车。 7. 最后再考虑完整的SE(2)鲁棒最小二乘,不需要一开始引入GTSAM或QP。 对整个 `MultiWheelC\Fleet` 的当前评价是: - 布局定义、刚体速度分解、中心控制、相对误差纠偏、最差成员统一减速和全员Ready屏障,结构上没有明显问题; - 当前最大算法缺口就是车队中心融合还不够鲁棒; - 最大工程缺口仍是运行入口、通信、成员超时看门狗、报警汇总和零速命令的实际下发; - `FleetMemberAgent` 目前还缺少独立自动化测试; - 各种阈值目前只在测试构造中出现,尚未通过双车实车数据标定。 所以,平均本身不是错误;“只做等权平均,并在冲突时整队不可用”才是需要下一阶段改进的部分。 将 FleetMemberReport 转成 FleetMemberStateSample 和 FleetMemberSafetyStatus; 将准备命令映射到 FleetMemberAgent; 将成员速度命令映射成 FleetCommand; ShouldStop 时广播 Stop 并让主车本地停车; 命令序号、报告序号和任务编号检查; M层报警和底盘执行失败映射到 FailureCode; 无线端口初始化; 报文序列化、分包、校验和接收循环; 主车本地单调时钟和报文接收时间记录。