# `Monty.Simulators.Synthetic.Kinematics`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/simulators/synthetic/kinematics.ex#L1)

Where the stand-in world puts the agent after an action.

Monty's world is MuJoCo, and its actuator defines the conventions this module
follows, because a policy that steers one must steer the other:

  * `MoveForward` moves along the agent's local **−Z** — towards whatever the
    sensor is looking at — and a negative distance moves it backwards;
  * `TurnLeft` / `TurnRight` yaw the *agent* about its local Y, positive to the
    left;
  * `LookUp` / `LookDown` pitch the *sensors* about their local X, and the
    agent stays where it points;
  * `MoveTangentially` moves along the action's `direction`, which is already a
    world vector: the surface policies rotate their heading by the agent's
    rotation before they answer, so it must not be rotated again;
  * `OrientVertical` / `OrientHorizontal` orbit the agent about its own axes:
    a side step, then a pitch or yaw, then a step along the agent's −Z. Monty's
    actuator "ASSUMES" the distances were calculated to orbit the object, and
    takes the surface into account not at all — so neither does this.

What is not honoured: the actions that set a pose outright (`SetAgentPose`,
`SetSensorPose`, `SetYaw`, …). Asking for one is an error rather than a silent
no-op — a world that quietly ignores an action makes an agent look broken in a
way that the log cannot explain.

# `pose`

```elixir
@type pose() :: %{
  position: Monty.Geometry.Rotation.vector(),
  rotation: Monty.Geometry.Rotation.t()
}
```

Where one body sits: a position in the world, and a rotation from its own
frame to the world's.

# `t`

```elixir
@type t() :: %{
  agent: pose(),
  sensors: %{optional(Monty.Simulators.Environment.sensor_id()) =&gt; pose()}
}
```

# `new`

```elixir
@spec new([Monty.Simulators.Environment.sensor_id()], keyword()) :: t()
```

A world with the agent where the experiment starts it.

Options: `:agent`, the agent's start pose, and `:sensors`, a map of sensor id
to that sensor's pose relative to it — both as an experiment's simulator is
configured. A sensor the options do not place rides a decimetre back from the
agent and a centimetre apart from the others, which is the stand-in's own
geometry: a world with no scene description still needs two cameras to see two
places.

# `sensor_poses`

```elixir
@spec sensor_poses(t()) :: %{
  optional(Monty.Simulators.Environment.sensor_id()) =&gt; pose()
}
```

Each sensor's own pose, relative to the agent, as proprioception reports it.

# `sensor_positions`

```elixir
@spec sensor_positions(t()) :: %{
  optional(Monty.Simulators.Environment.sensor_id()) =&gt;
    Monty.Geometry.Rotation.vector()
}
```

Where each sensor is, as the environment reports it.

# `sensor_world_poses`

```elixir
@spec sensor_world_poses(t()) :: %{
  optional(Monty.Simulators.Environment.sensor_id()) =&gt; pose()
}
```

Where each sensor is and which way it faces, in the world.

The sensor's own frame is the agent's turned by however the sensor is mounted
on it, so raising the agent's yaw carries its cameras round too.

# `step`

```elixir
@spec step(t(), [Monty.Frameworks.Actions.Action.t()]) :: t()
```

The world after every action of one step, in the order they were given.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
