# controller arm_tracking Describes the entry `arm_tracking` at version `0.1.0`, registered by `mime.control._arm_tracking.ArmTracking`. ## At a glance - controller `arm_tracking`, version `0.1.0`, namespace `mime` - Derivative, by mode: inside the compiled step `piecewise_jump`, as a callback `cut` - Validity: none declared ## What it models, and when not to use it ### A magnet carried by an arm, following a body **What it models.** The controller of an arm that carries a magnet above a robot in a tube and keeps it there as the robot swims: `exp.set_controller(ArmTracking(description=asset("arm.urdf"), end_effector="link_6", home=rad(...), axis=X, up=Z, at=m(...)), reads={"body_pose": "umr.pose", "body_twist": "umr.twist", "joint_positions": "rpm.arm.joint_positions"}, writes={"joint_positions": "rpm.arm.joint_positions"}, mode="callback")`, or `mode="graph"` to run it inside the compiled step. Each control period it reads the body's pose, its twist and the arm's joint positions, sets a target for the arm's end effector at the standoff along `up` from where the body is along `axis`, that distance held within `reach`, and writes the joint positions that a few damped-least-squares steps reach from the arm's present ones: the command of a kinematic arm. The target moves a fraction `smoothing` of the way towards that point each tick, from the end effector's pose at `home`; with `orientation_feedback`, its orientation also turns so that the end effector's z follows the body's. It is the v0.2 line's controller of the AR4 rig, ported: the same target, filter and inverse kinematics, compared with the v0.2 controller on a recorded input sequence. It computes in single precision whatever the run's precision, as the v0.2 controller did, so a run in double precision with it is mixed. **When not to use it.** - When what the controller reads matters: it reads the body's pose and twist and the arm's joint positions straight from the physics state, which the build report lists as ground truth, not deployable (see MIME-ANO-104). No instrument gives a real controller the robot's pose inside a body. - For a dynamic arm: it writes joint positions, and the joint torques of the v0.2 line's computed-torque control wait for the dynamic arm. - When a derivative of the run is needed and it runs as a callback: between compiled chunks no derivative passes through its writes. Inside the compiled step one does, and it is `piecewise_jump`: the reach is a clip (a kink), and the inverse kinematics' half turn, the orientation feedback's hemisphere and its fallback where the body's z lies along the home pose's x are jumps. A tracking target never comes near a half turn of the end effector. - For a tube that is not straight through the origin along `axis`: it follows the body's distance along that one line. - When the target filter's time constant must stay fixed as the rate changes: `smoothing` is a fraction per tick, so the same value at another rate is another filter. **Versions.** `arm_tracking` 0.1.0 is its first: as a callback or inside the compiled step, writing a kinematic arm's joint positions. ## Settings | Setting | Type | Default | Dimension | Role | Default's kind | |---|---|---|---|---|---| | `acknowledged` | `tuple[tuple[str, str], ...]` | `[]` | none | design | convention | | `at` | `Annotated[Quantity, Length]` | `{"unit": "m", "value": [0.0, 0.0, 0.0]}` | length (m) | physical | convention | | `axis` | `tuple[float, float, float]` | `{"required": true}` | none | design | none | | `damping` | `float` | `0.05` | none | numerical | numerical | | `description` | `Asset` | `{"required": true}` | none | physical | none | | `end_effector` | `str` | `{"required": true}` | none | design | none | | `gain` | `float` | `1.0` | none | numerical | numerical | | `home` | `Annotated[Quantity, DIMENSIONLESS]` | `{"required": true}` | dimensionless | design | none | | `iterations` | `int` | `5` | none | numerical | numerical | | `lookahead` | `Annotated[Quantity, Time]` | `{"unit": "s", "value": 0.01}` | time (s) | numerical | numerical | | `orientation` | `tuple[float, float, float, float] \| None` | `null` | none | physical | convention | | `orientation_feedback` | `bool` | `false` | none | design | convention | | `reach` | `Annotated[Quantity, Length]` | `{"unit": "m", "value": [-0.05, 0.05]}` | length (m) | numerical | numerical | | `smoothing` | `float` | `0.005` | none | numerical | numerical | | `standoff` | `Annotated[Quantity, Length]` | `{"unit": "m", "value": 0.15}` | length (m) | numerical | numerical | | `up` | `tuple[float, float, float]` | `{"required": true}` | none | design | none | Each role and default's kind is the entry's `setting_roles` (`mime.registry.SettingRole`); a setting that declares none is required. Each default is a claim (`mime.registry.DefaultKind`): a determined default is resolved in every profile, a convention taken in every profile, a numerical default taken in exploratory only and stamped pending. Their rules and bases: - `acknowledged` (convention): the setting's written default; no element of the description is read past unless acknowledged with its reason - `at` (convention): the setting's written default; the arm's base sits at the world's origin unless placed - `damping` (numerical): the value the earlier line's controller ran with; the frozen reference of the arm's experiment was run with it; no study of it is recorded - `gain` (numerical): the value the earlier line's controller ran with; a design value of the controller (where and how it holds the arm), held as a numerical default so that A0's frozen declarations stand: the frozen reference of the arm's experiment was run with it, and no study of it is recorded - `iterations` (numerical): the value the earlier line's controller ran with; the frozen reference of the arm's experiment was run with it; no study of it is recorded - `lookahead` (numerical): the value the earlier line's controller ran with; a design value of the controller (where and how it holds the arm), held as a numerical default so that A0's frozen declarations stand: the frozen reference of the arm's experiment was run with it, and no study of it is recorded - `orientation` (convention): the setting's written default; the arm's base is not turned from the world unless turned - `orientation_feedback` (convention): the setting's written default; the target is not turned to follow the body unless asked - `reach` (numerical): the value the earlier line's controller ran with; a design value of the controller (where and how it holds the arm), held as a numerical default so that A0's frozen declarations stand: the frozen reference of the arm's experiment was run with it, and no study of it is recorded - `smoothing` (numerical): the value the earlier line's controller ran with; a design value of the controller (where and how it holds the arm), held as a numerical default so that A0's frozen declarations stand: the frozen reference of the arm's experiment was run with it, and no study of it is recorded - `standoff` (numerical): the value the earlier line's controller ran with; a design value of the controller (where and how it holds the arm), held as a numerical default so that A0's frozen declarations stand: the frozen reference of the arm's experiment was run with it, and no study of it is recorded ## Reads and writes `reads`: each label bound by the experiment to a thing's state or an observation's output: | Label | Units | Shape | |---|---|---| | `body_pose` | `m; 1` | `[7]` | | `body_twist` | `m/s; rad/s` | `[6]` | | `joint_positions` | `1` | `[-1]` | `writes`: each label bound by the experiment to an apparatus's command, whose drive it replaces: | Label | Units | Shape | |---|---|---| | `joint_positions` | `1` | `[-1]` | ## Nodes it builds Builds no node of its own. ## Gradient status The derivative depends on the mode the controller is placed in, and a saved experiment's lock records that mode's status: - Inside the compiled step: `piecewise_jump`, its law's own - As a callback: `cut`, the binding's whatever the law The derivative through this controller inside the compiled step is `piecewise_jump`: not the exact derivative of what it computes everywhere. Switches the law evaluates, each declared once by its owner: | Switch | Indicator | Orders, per output group | Route to exact | |---|---|---|---| | `arm_tracking.reach.low` | the body's distance along the axis less the reach's lower end, m | `joint_positions` 0 (a kink); `target_position` 0 (a kink) | none: a distance below the reach is held at its end, whose derivative in the distance is zero; a smooth envelope would be another controller | | `arm_tracking.reach.high` | the reach's upper end less the body's distance along the axis, m | `joint_positions` 0 (a kink); `target_position` 0 (a kink) | none: a distance above the reach is held at its end, as at the lower one | | `arm_tracking.half_turn` | minus the cosine of half the angle from the end effector's orientation to its target's (the w of the turn between them), dimensionless, positive past a half turn | `joint_positions` -1 (a jump) | none: past a half turn the shorter way round is the other way, and each inverse-kinematics step turns back; a target within a half turn of the end effector, as a tracking one is, never reaches it | | `arm_tracking.small_turn` | the series' threshold less the squared sine of half the angle from the end effector's orientation to its target's, dimensionless, positive where its rotation vector is taken from its series | `joint_positions` 1 (continuously differentiable) | none needed: the series meets the closed form to rounding in its value and its slope, so the derivative on either side is the one function's | | `arm_tracking.parallel` | the length of the home pose's x made perpendicular to the body's projected z, less the length below which the two are taken as parallel, dimensionless | `joint_positions` -1 (a jump); `target_orientation` -1 (a jump) | none: with the home pose's x along the body's z the target's x is undefined, and the home pose's y stands in for it | | `arm_tracking.hemisphere` | the length of q (q . held), q the turn's quaternion and held the target's orientation, dimensionless: zero at the edge of held's hemisphere | `joint_positions` -1 (a jump); `target_orientation` -1 (a jump) | none: at the edge q and -q are as near the held orientation, and the filter goes the other way round on either side | The derivative through this controller as a callback is `cut`: not the exact derivative of what it computes everywhere. - **Where**: the controller's writes: a compiled chunk takes them as fixed inputs, so a derivative of the run stops at them (it is zero through them) - **Why**: the run calls the controller's step on the host between compiled chunks, where no derivative passes through a call - **Route to exact**: place the controller inside the compiled step, where its status is its law's own, derived from the switches it declares ## Validity none declared ## References [@Sciavicco2000], [@deJongh2025NonlinDyn] ## Other declarations | Class variable | Type | Value | |---|---|---| | `live` | `ClassVar[Mapping[str, LiveInput]]` | `` too long to show; see `mime registry --json` `` | | `moving` | `ClassVar[bool]` | `false` |