docs: correct EM controller handoff guide
This commit is contained in:
@@ -284,9 +284,12 @@ public static class EmPlannerFullPipelineDemo
|
||||
double signedSpeedMetersPerSecond = point.SignedLongitudinalVelocity;
|
||||
double velocityXMetersPerSecond = point.VelocityX;
|
||||
double velocityYMetersPerSecond = point.VelocityY;
|
||||
double longitudinalAccelerationMetersPerSecondSquared = point.LongitudinalAcceleration;
|
||||
double longitudinalJerkMetersPerSecondCubed = point.LongitudinalJerk;
|
||||
double yawRateRadiansPerSecond = point.YawRate;
|
||||
// 控制适配的公开映射只消费 X/Y/Yaw、VehicleCurvature 和
|
||||
// SignedLongitudinalVelocity;其他公开派生量在此仅作输出诊断观察。
|
||||
// LongitudinalAcceleration 和 LongitudinalJerk 是规划程序集内部诊断,
|
||||
// 外部控制器集成代码不得复制对它们的成员访问。如自有构建需要该证据,
|
||||
// 应先由该构建定义并暴露经评审的公开诊断契约,再通过契约读取。
|
||||
_ = timeFromStartSeconds;
|
||||
_ = worldXMeters;
|
||||
_ = worldYMeters;
|
||||
@@ -296,8 +299,6 @@ public static class EmPlannerFullPipelineDemo
|
||||
_ = signedSpeedMetersPerSecond;
|
||||
_ = velocityXMetersPerSecond;
|
||||
_ = velocityYMetersPerSecond;
|
||||
_ = longitudinalAccelerationMetersPerSecondSquared;
|
||||
_ = longitudinalJerkMetersPerSecondCubed;
|
||||
_ = yawRateRadiansPerSecond;
|
||||
}
|
||||
|
||||
|
||||
+39
-9
@@ -41,6 +41,7 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
// 并在返回前完成 Success/SuccessWithFallback、非空轨迹和可控点的发布闸门检查。
|
||||
private static void WrapperReplacement(
|
||||
ControllerStateProvider stateProvider,
|
||||
bool gearSwitchHandoffIsSafetyReviewedAndAuthorized,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
FixedOfflinePlanningOutput planningOutput =
|
||||
@@ -56,7 +57,9 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
"固定离线 EM 包装器未返回完整控制轨迹。");
|
||||
}
|
||||
|
||||
RejectGearSwitchForSingleTrackingMovement(emTrajectory);
|
||||
EnsureSingleDirectionTrackingIsAuthorized(
|
||||
emTrajectory,
|
||||
gearSwitchHandoffIsSafetyReviewedAndAuthorized);
|
||||
|
||||
// 原测试已有的状态源继续交给动作;仅 trajectory 的来源由人工数据改为 EM 输出。
|
||||
var movement = new TrajectoryTrackingMovement
|
||||
@@ -68,6 +71,10 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
// 若原测试使用组合动作计划,则使用同一条已适配的控制轨迹创建段。
|
||||
var segment = new TrackMotionPlanSegment(trajectory);
|
||||
|
||||
// 若本段以 GearSwitch 结束,授权只允许跟踪当前方向段到精确停车。
|
||||
// 动作完成后,外部状态机必须确认已停稳和下一方向,再单独请求/执行下一段。
|
||||
// 绝不把两个方向的点串接起来,也不自动跨越换向边界。
|
||||
|
||||
_ = movement;
|
||||
_ = segment;
|
||||
}
|
||||
@@ -78,6 +85,7 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
private static void DirectServiceReplacement(
|
||||
EmPlanningRequest request,
|
||||
ControllerStateProvider stateProvider,
|
||||
bool gearSwitchHandoffIsSafetyReviewedAndAuthorized,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var planningService = new EmPlanningService(new OsqpNativeSolver());
|
||||
@@ -93,7 +101,9 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
}
|
||||
|
||||
EmTrajectory emTrajectory = result.Trajectory;
|
||||
RejectGearSwitchForSingleTrackingMovement(emTrajectory);
|
||||
EnsureSingleDirectionTrackingIsAuthorized(
|
||||
emTrajectory,
|
||||
gearSwitchHandoffIsSafetyReviewedAndAuthorized);
|
||||
|
||||
// 适配器使用 EM 的世界位姿、车辆曲率和有符号纵向速度,重建 Trajectory2D 的弧长。
|
||||
Trajectory2D trajectory =
|
||||
@@ -110,15 +120,20 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
// SegmentStarted/TrackingCycleObserver 等观测器以及状态与安全检查仍保持测试所有权。
|
||||
var segment = new TrackMotionPlanSegment(trajectory);
|
||||
|
||||
// GearSwitch 授权不是跨边界授权:只跟踪本次返回的完整方向段到精确停车。
|
||||
// 停稳后由外部状态机确认方向,然后发起一次新请求并创建新的单段动作。
|
||||
// 不得拼接方向点,不得在当前 movement/segment 中自动继续到下一段。
|
||||
|
||||
_ = movement;
|
||||
_ = segment;
|
||||
}
|
||||
|
||||
// 单个 TrajectoryTrackingMovement 只能消费一个方向段。
|
||||
// GearSwitch 表示该段末端必须先停车;除非外层已实现并经安全评审的“停车—方向确认—请求下一段”序列,
|
||||
// 否则拒绝它,绝不能把前进/倒车点拼接进同一个 Trajectory2D 后连续跟踪。
|
||||
private static void RejectGearSwitchForSingleTrackingMovement(
|
||||
EmTrajectory emTrajectory)
|
||||
// 完整方向轨迹以 GearSwitch 结束并不代表轨迹本身跨界:它可安全跟踪到边界精确停车。
|
||||
// 但调用方必须显式传入已通过安全评审的换向交接授权;未授权时在跟踪前拒绝。
|
||||
private static void EnsureSingleDirectionTrackingIsAuthorized(
|
||||
EmTrajectory emTrajectory,
|
||||
bool gearSwitchHandoffIsSafetyReviewedAndAuthorized)
|
||||
{
|
||||
if (emTrajectory == null)
|
||||
{
|
||||
@@ -126,12 +141,27 @@ public static class EmPlannerTrajectoryReplacementExample
|
||||
"EM 规划输出为空,不能创建 TrajectoryTrackingMovement。");
|
||||
}
|
||||
|
||||
if (emTrajectory.Metadata.TerminalType == EmTerminalType.GearSwitch)
|
||||
if (emTrajectory.Metadata.TerminalType != EmTerminalType.GearSwitch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (emTrajectory.Metadata.PlanningScope != EmPlanningScope.FullDirectionSegment ||
|
||||
emTrajectory.Metadata.LongitudinalMode != EmLongitudinalMode.ExactStopAtBoundary)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"EmTerminalType.GearSwitch 必须由已安全评审的停车、方向确认和下一段规划序列处理;" +
|
||||
"单个 TrajectoryTrackingMovement 不得跨越换向边界。");
|
||||
"GearSwitch 只能作为 FullDirectionSegment 的 ExactStopAtBoundary 末端交给单段跟踪。");
|
||||
}
|
||||
|
||||
if (!gearSwitchHandoffIsSafetyReviewedAndAuthorized)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"GearSwitch 方向段需要调用方显式提供已通过安全评审的换向交接授权;" +
|
||||
"未授权时不得创建跟踪动作。");
|
||||
}
|
||||
|
||||
// 通过检查只授权当前段执行到精确停车。外部状态机仍必须在停稳后
|
||||
// 确认下一方向并另行请求/执行下一段;不得拼接点或自动跨越边界。
|
||||
}
|
||||
|
||||
// CruiseSpeedMetersPerSecond、AccelerationMetersPerSecondSquared、
|
||||
|
||||
@@ -21,7 +21,7 @@ PlanningGridMap → PlanningRequest → HybridAStarPlanner.Plan
|
||||
→ EmControlTrajectoryAdapter.Create → Trajectory2D → TrajectoryTrackingMovement
|
||||
```
|
||||
|
||||
路径、地图和车辆参数使用 SI:世界位置为 m,航向为 rad,曲率为 1/m。`PlanningGridMap` 是冻结地图快照;不要把 Clumsy 绘图用的 mm 坐标直接交给规划器。
|
||||
路径、位姿、车辆和轨迹值使用 SI:世界位置为 m,航向为 rad,曲率为 1/m。地图输入是明确的例外:`PlanningMapRequest` / `MapBoundsMm` 的边界、`ResolutionMm` 和障碍物几何均使用世界 mm,如完整示例步骤 3 所示。`PlanningGridMap` 是该 mm 地图请求生成的冻结快照。只能在明确拥有的边界做 m/mm 转换并保留单位标注:不得把 Clumsy 的 mm 坐标直接交给 `Pose2D`,也不得把 m 值未转换就交给 mm 地图契约;单位不明时立即停止,不根据数值大小猜测。
|
||||
|
||||
## 步骤 1–15
|
||||
|
||||
@@ -49,7 +49,7 @@ PlanningGridMap → PlanningRequest → HybridAStarPlanner.Plan
|
||||
|
||||
[对应完整示例:步骤 3](EmPlannerFullPipelineDemo.cs#L84)
|
||||
|
||||
**本步输入**:固定障碍物、地图边界与栅格分辨率。
|
||||
**本步输入**:固定障碍物、`MapBoundsMm` 地图边界与 `ResolutionMm` 栅格分辨率;这些地图几何值均为世界 mm。
|
||||
**调用**:建立并冻结 `PlanningGridMap`。
|
||||
**本步输出**:同一 `SnapshotId` 的可读地图。
|
||||
**失败处理**:地图未就绪、起点或终点越界/碰撞时拒绝,不生成替代移动命令。
|
||||
@@ -214,13 +214,13 @@ Trajectory2D controllerTrajectory =
|
||||
| `VelocityX`, `VelocityY` | 世界 m/s | 由有符号速度和航向导出的预测分量。`VelocityX` 和 `VelocityY` 是世界坐标系中的预测速度分量,不是底盘纵向/横向命令;闭环测试不得把它们直接发送给车辆。 |
|
||||
| `YawRate` | rad/s | `SignedLongitudinalVelocity * VehicleCurvature` 导出的预测偏航角速度。 |
|
||||
| `SegmentIndex`, `SegmentLocalS`, `PathS`, `Direction`, `BoundaryType` | 索引、m、m、枚举、枚举 | 段归属、局部/全路径弧长、方向及目标/换向边界;用于保证不跨方向消费。 |
|
||||
| `LongitudinalAcceleration`, `LongitudinalJerk` | m/s²、m/s³ | 沿车体前向轴的规划量;当前为程序集内部验证/执行成员,不是 `Trajectory2D` 输入。 |
|
||||
| `LongitudinalAcceleration`, `LongitudinalJerk` | m/s²、m/s³ | 沿车体前向轴的规划器内部诊断量,不是 `Trajectory2D` 输入。外部维护者不得复制对内部成员的访问;如自有构建确需该证据,必须先通过其显式公开契约暴露后再读取。 |
|
||||
|
||||
VelocityX 和 VelocityY 是世界坐标系中的预测速度分量,不是底盘纵向/横向命令;闭环测试不得把它们直接发送给车辆。
|
||||
|
||||
| 概念 | 正确解释 | 常见错误 |
|
||||
| --- | --- | --- |
|
||||
| m / mm | 规划与 `Trajectory2D` 均为 m;外部测试仅绘图时将 m 乘 1000。 | 将 Clumsy `Vector2` 的 mm 直接用于 `Pose2D`。 |
|
||||
| m / mm | 路径、`Pose2D`、车辆和 `EmTrajectory` / `Trajectory2D` 值为 SI m;`PlanningMapRequest` / `MapBoundsMm` 边界、`ResolutionMm` 和障碍几何为世界 mm。仅在明确的接口边界转换。 | 将 Clumsy `Vector2` 的 mm 直接用于 `Pose2D`,或将 m 障碍几何直接填入 mm 地图契约。 |
|
||||
| rad / deg | 所有 `Yaw`、转角、容差为 rad;`AngleMath.DegreesToRadians` 只用于初始化控制器角度。 | 将 3 或 45 当作 rad。 |
|
||||
| 1/m、m/s、m/s²、m/s³、rad/s | 分别是曲率、速度、加速度、jerk、偏航角速度。 | 用曲率替代转向角,或把加速度当速度。 |
|
||||
| 世界/车体 | `X/Y/Yaw`、`VelocityX/Y` 在世界系;有符号纵向速度、加速度、jerk 沿车体前向轴。 | 把世界 XY 分量作为车体纵/横向命令。 |
|
||||
@@ -302,11 +302,11 @@ VelocityX 和 VelocityY 是世界坐标系中的预测速度分量,不是底
|
||||
| 方向不匹配 | `StateDirectionMismatch`,或 Metadata/点方向不同 | 只用当前活动段;先停稳再重新确认。 |
|
||||
| 适配器拒绝 | 少于两个不同位置点,`Create` 抛出 | 不修改原始轨迹硬凑点;修复上游输出。 |
|
||||
|
||||
本 demo 是**单方向**。换向需要在当前段的精确边界完整停车,并在安全审查后的换向状态机确认下一方向后,单独请求并执行下一段规划。不得把正、负方向段串接成一个 `Trajectory2D`,也不得跨换向边界插值。
|
||||
本 demo 是**单方向**。有效的 `FullDirectionSegment` 轨迹即使以 `EmTerminalType.GearSwitch` 结束,仍只是一个可跟踪到精确停车的安全方向段;但调用方只能在显式提供已通过安全评审的换向交接授权时执行该段。授权仅覆盖“执行当前段并在换向边界精确停车”;到站后必须由外部状态机确认已停稳和下一方向,然后单独请求并执行下一段。授权绝不允许把正、负方向点串接成一个 `Trajectory2D`、自动跨越边界或跨边界插值。
|
||||
|
||||
## 外部闭环测试中的替换点
|
||||
|
||||
当前应替换的人工来源是外部 `NewControllerTrackingTests.cs` 中的 `TestTrajectoryFactory.CreateStraight4Meters`(在直线 4 m 测试中构造 `trajectory`)。下游汇是 `TrajectoryTrackingMovement.Trajectory`。合并时只把该变量的来源替换为本指南的服务调用和适配器输出;`StateProvider`、Stanley/PID/GCP 调参、完成/偏离保护、超时与记录器均由控制器测试继续拥有。不要改写它们来迁就规划器。
|
||||
当前应替换的人工来源是外部 `NewControllerTrackingTests.cs` 中的 `TestTrajectoryFactory.CreateStraight4Meters`(在直线 4 m 测试中构造 `trajectory`)。下游汇是 `TrajectoryTrackingMovement.Trajectory`。合并时只把该变量的来源替换为本指南的服务调用和适配器输出;`StateProvider`、Stanley/PID/GCP 调参、完成/偏离保护、超时与记录器均由控制器测试继续拥有。包装器和直接服务替换路径都要求调用方显式传入换向交接是否已通过安全评审和授权;未授权的 `GearSwitch` 轨迹必须在创建跟踪动作前拒绝。不要改写原有安全所有权来迁就规划器。
|
||||
|
||||
## 建议阅读顺序
|
||||
|
||||
|
||||
Reference in New Issue
Block a user