MovementDefinition详解

来自MDCS wiki2
跳到导航 跳到搜索


概述 / Overview

`MovementDefinition` 是 Clumsy 中描述 一个运动行为的抽象基类。每个 Movement 是一段 可迭代的控制循环,由 `DriveTask` 宿主按 DriveTaskInterval(默认 20 ms)周期推进。它是 Clumsy 自动驾驶的 基本运动语汇

`MovementDefinition` is the Clumsy abstract base that describes a single motion behaviour. Each Movement is an iterable control loop, ticked by `DriveTask` at `DriveTaskInterval` (default 20 ms). It's Clumsy's basic motion vocabulary.

定义:`D:\src\Clumsy\ClumsyCore\Pilot\MovementDefinition.cs:41-48`。 Definition: `D:\src\Clumsy\ClumsyCore\Pilot\MovementDefinition.cs:41-48`.

契约 / Contract

public abstract class MovementDefinition
{
    public abstract IEnumerable<bool> Get();
}

public abstract class MovementDefinition<T> : MovementDefinition
{
    public new abstract IEnumerable<T> Get();
}

`Get()` 返回一个 可迭代;框架每次调 `MoveNext()` 推一格。

`Get()` returns an enumerable; the framework calls `MoveNext()` once per tick.

  • 每次 `yield return true` = "继续 tick" / "keep ticking"
  • 迭代器耗尽(不再 `yield`) = "Movement 完成" / "Movement done"

生命周期 / Lifecycle

 你的代码 / Your code:
   DriveTask.WaitDriveTask(new MyMovement{...}.Get());
      │
      ▼
 DriveTask 内部 / Inside DriveTask (D:\src\Clumsy\ClumsyCore\DriveTask.cs:104-144):
   new Thread + AboveNormal priority
      │
      ▼ 循环 / loop:
   enumerator.MoveNext()
   Thread.Sleep(Configuration.conf.DriveTaskInterval) // 20 ms
   ...
      │
      ▼ 完成 / done (MoveNext returns false):
   stopped_reason = 0
   Monitor.PulseAll(wait)
      │
      ▼ 异常 / on exception:
   stopped_reason = -1
   capture & rethrow on Wait()

调用方 `WaitDriveTask` 在 `Monitor.Wait(wait)` 上阻塞直到完成。 Caller `WaitDriveTask` blocks on `Monitor.Wait(wait)` until completion.

最小 Movement / Minimal Movement

using ClumsyCore.Pilot;
using ClumsyCore.DrivePlans;
using System.Collections.Generic;

public class MyStraight : MovementDefinition
{
    public float dx, dy;          // displacement
    public float basespeed = 600; // mm/s

    public override IEnumerable<bool> Get()
    {
        var sx = currentX;        // pose at start
        var sy = currentY;
        var driver = new SteeringLineFollowing
        {
            srcX = sx, srcY = sy,
            dstX = sx + dx, dstY = sy + dy,
            basespeed = basespeed
        };
        foreach (var t in driver.Follow()) yield return t;
    }
}

[MovementTest] 属性 / Attribute

目标 / Target: 类 / class

效果 / Effect: 在 Clumsy 测试 UI 注册一个 测试按钮;按下立即在车上跑这个 Movement(车应静止 / 在仿真)。 Registers a test button in the Clumsy UI; pressing it runs the Movement on a stationary (or simulated) vehicle.

[MovementTest(name = "测试我的直线 / Test my line")]
public class MyStraight : MovementDefinition { ... }

可用原语 / Primitive library

Movement 通常 组合下层原语而不是从零写控制律。下面是仓库里现成的: Movements usually compose lower-level primitives rather than implement control law from scratch.

ClumsyCore/Legacy/(几何跟踪基础)

Primitive 用途 / Use
`SteeringLineFollowing` 阿克曼正向纯跟踪
`SteeringLineFollowingReverse` 阿克曼反向跟踪
`OmniWheelTracking` 全向轮速跟踪
`LeftRightWheelTracking` 差速轮速跟踪
`OmniBezierTracking` / `LeftRightBezierTracking` Bezier 曲线跟踪
`TrajectoryReplanningObstacleAvoidance` 动态避障

ClumsyDance/Movements/(行为聚合)

Movement 用途 / Use
`SlotFollowing` 槽位跟踪(叉车叉孔 / KIVA 腿对)
`ReflexDock` 反射式对接
`KivaFollowShelfOfManyLegs` 多腿料架导航
`TopHatFollowing` 礼帽形特征跟踪
`BezierMPC` Bezier 参考的 MPC
`LRWheelOdometryCalculator` 编码器积分助手

ClumsyDance/Detectors/(感知)

Detector 用途
`LidarDetectTray` 通用托盘检测(2 腿 / 3 腿可调)
`Lidar2dDetect2LegTray`, `Lidar2dDetect3LegTray`, `Lidar3dDetectTray` 特化变体
`Cam3dDetectTray` 3D 相机检测

MovementDefinition<T> 泛型变体 / Generic variant

当 Movement 需要 返回一个对象(如已配置好的子 Movement): When a Movement needs to return a typed handle (e.g. a pre-configured sub-Movement):

public class BuildFollower : MovementDefinition<SteeringLineFollowing>
{
    public float srcX, srcY, dstX, dstY;
    public override IEnumerable<SteeringLineFollowing> Get()
    {
        yield return new SteeringLineFollowing
        {
            srcX = srcX, srcY = srcY,
            dstX = dstX, dstY = dstY,
            basespeed = 800
        };
    }
}

异常 + 中止 / Exceptions & abort

  • `Get()` 内抛异常 → `DriveTask.Wait()` 返回时 重新抛出
  • `PilotBase.SecurityCheck()` 在没收到控制信号超时(默认 300 ms)时调 `DriveStop()`;当前 Movement 的 enumerator 不会再被推进,但 资源清理责任在 Movement 作者(在 yield 前后包 try/finally)。
  • 调用者按 `WaitDriveTask(...)` 同步等;要异步用 `DriveTask.WaitDriveTask` 包在另一线程或 `SimpleAgvInterface.Queue`。

Exceptions from `Get()` re-throw when `WaitDriveTask` returns. The watchdog (`SecurityCheck`, default 300 ms timeout) calls `DriveStop()`; the enumerator stops being advanced but Movement-owned cleanup is on the author (wrap in try/finally).

与 Pilot 的关系 / Relation to Pilot

  • `PilotBase` 是 Movement 的 宿主进程上下文(提供 pose、IDriveWriter 等)。
  • `PilotDefinition<T>` 加 typed config。
  • 95% 的车型定制不用碰 PilotBase —— 写 Movement 就够。

`PilotBase` is Movement's host context (pose, drive writers); `PilotDefinition<T>` adds typed config. 95% of customisation only needs Movements.

常见错误 / Gotchas

  • 在 `Get()` 用 `Thread.Sleep` → 阻塞 DriveTask 线程,定时器漂移。正确方式:`yield return` 多次。
 Don't `Thread.Sleep` inside `Get()`; just `yield return` multiple times.
  • Movement 跨实例共享状态 → Movement 应是 可重入的;不要用 static 字段保存进度。
 Movements should be reentrant; don't use static fields for progress.
  • 忘了在不需要继续时 `yield break` → 看起来在原地踏步。
  • 构造时读 pose → 应在 `Get()` 第一帧读,避免构造与执行间车移动了。
 Read pose inside `Get()`, not in the ctor — otherwise the car may have moved.

相关页面 / See also