# `BB.Motion.Tracker`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/motion/tracker.ex#L5)

Behaviour for continuous position tracking with IK.

Trackers maintain an ongoing IK solution loop, continuously solving for
updated targets and sending actuator commands. This is useful for:

- Following a moving target (visual tracking)
- Smooth trajectory interpolation
- Real-time position control from external sources

## Implementing a Tracker

Trackers are typically GenServers that:
1. Run a periodic solve loop at a configurable rate
2. Accept target updates via `update_target/2`
3. Send actuator commands on each successful solve
4. Report status including tracking error and solve statistics

## Callbacks

- `start_tracking/5` - Begin tracking a target link
- `update_target/2` - Update the current target position
- `status/1` - Get current tracking status
- `stop_tracking/2` - Stop tracking and optionally return final positions

## Example Implementation

See `BB.IK.FABRIK.Tracker` for a reference implementation.

## Usage Pattern

    # Start tracking
    {:ok, tracker} = BB.IK.FABRIK.Tracker.start_link(
      robot: MyRobot,
      target_link: :gripper,
      initial_target: {0.3, 0.2, 0.1},
      update_rate: 30
    )

    # Update target from vision callback
    BB.IK.FABRIK.Tracker.update_target(tracker, new_target)

    # Check status
    %{residual: 0.001, tracking: true} = BB.IK.FABRIK.Tracker.status(tracker)

    # Stop and get final positions
    {:ok, positions} = BB.IK.FABRIK.Tracker.stop(tracker)

# `positions`

```elixir
@type positions() :: BB.IK.Solver.positions()
```

# `status`

```elixir
@type status() :: %{
  tracking: boolean(),
  target: target() | nil,
  residual: float() | nil,
  iterations: non_neg_integer(),
  update_rate: pos_integer(),
  last_update: DateTime.t() | nil
}
```

# `target`

```elixir
@type target() :: BB.IK.Solver.target()
```

# `tracker_state`

```elixir
@type tracker_state() :: term()
```

# `start_tracking`

```elixir
@callback start_tracking(
  robot :: module() | BB.Robot.t(),
  robot_state :: BB.Robot.State.t(),
  target_link :: atom(),
  initial_target :: target(),
  opts :: keyword()
) :: {:ok, tracker_state()} | {:error, term()}
```

Start tracking a target link.

## Options

Required:
- `:robot` - Robot module or struct
- `:target_link` - Name of the link to track
- `:initial_target` - Starting target position

Optional:
- `:update_rate` - Solve frequency in Hz (default: 20)
- `:delivery` - Actuator command delivery mode (default: `:direct`)
- `:max_iterations` - Maximum solver iterations per update
- `:tolerance` - Convergence tolerance

## Returns

- `{:ok, state}` - Tracking started
- `{:error, reason}` - Failed to start

# `status`

```elixir
@callback status(state :: tracker_state()) :: status()
```

Get current tracking status.

## Returns

Status map containing:
- `tracking` - Whether actively tracking
- `target` - Current target position
- `residual` - Distance from end-effector to target
- `iterations` - Solver iterations on last update
- `update_rate` - Current update frequency
- `last_update` - Timestamp of last successful solve

# `stop_tracking`

```elixir
@callback stop_tracking(state :: tracker_state(), opts :: keyword()) ::
  {:ok, positions()} | {:error, term()}
```

Stop tracking.

## Options

- `:hold` - Whether to send hold commands to actuators (default: false)

## Returns

- `{:ok, positions}` - Final joint positions
- `{:error, reason}` - Stop failed

# `update_target`

```elixir
@callback update_target(state :: tracker_state(), target :: target()) ::
  {:ok, tracker_state()} | {:error, term()}
```

Update the current target position.

The tracker will solve for the new target on its next update cycle.

## Returns

- `{:ok, state}` - Target updated
- `{:error, reason}` - Update failed (e.g., tracker stopped)

---

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