Monty.Frameworks.Models.MotorPolicies.SurfacePolicy (ExTbpMonty.Core v0.1.0)

Copy Markdown View Source

A policy that walks the surface of an object, feeling along it.

Mirrors tbp/monty/frameworks/models/motor_policies.py::SurfacePolicy, the policy a surface agent runs: it keeps the sensor a fixed distance from the surface and moves along it, so the evidence it collects comes from a band that crosses the object rather than from wherever a random walk lands.

The cycle

Four actions, in order, forever:

MoveForward -> OrientHorizontal -> OrientVertical -> MoveTangentially

Moving forward puts the sensor at desired_object_distance from what it sees; orienting horizontally turns the sensor to face the surface normal's sideways lean, orienting vertically its up-and-down lean, and moving tangentially takes a step along the surface in a direction that is mostly the last one and partly new (alpha, Monty's "0 is the same as before, 1 is a random walk"). Only the vertical orienting step is worth showing the learning modules — the other three are bookkeeping to stay on the object — which is what motor_only_step says.

When it cannot see the object

object_coverage below a tenth, or a search already under way, means the sensor has left the object; then the policy searches: back off to a circle four times the desired distance in front of it and step round it thirty degrees at a time, horizontally for a full turn, then vertically for a second, then at random angles until something comes into view.

What it shares with InformedPolicy

Monty's SurfacePolicy is an InformedPolicy: it inherits the jump machinery and overrides the step. Here the jump step is InformedPolicy.goal_driven_actions/4, taken by any policy carrying the jump fields, and Monty's _handle_successful_jump hook is handle_successful_jump/1 dispatched to the policy's own module (docs/monty-differences.md §2.9). A jump that lands with the object in view restarts the cycle as if the agent had just moved tangentially, which is how a goal-driven step hands back to walking.

Summary

Types

A policy that can run the surface cycle: the fields the step reads.

t()

Functions

One step: a jump if one is in flight or asked for, otherwise the cycle.

A jump has landed and the object is in view: carry on as if we had just moved.

A surface policy for :agent_id, with :alpha weighting new directions.

Starts the next episode: no jump in flight, and the search forgotten.

Nothing to report: what it knows is the action it would undo.

One step of the surface cycle, for any policy that carries its fields.

A direction that is mostly the last one and partly new, in the agent's frame.

Types

surface_state()

@type surface_state() :: %{
  :agent_id => atom() | String.t(),
  :rng => :rand.state() | nil,
  :alpha => float(),
  :desired_object_distance => float(),
  :use_goal_driven_actions => boolean(),
  :is_jumping => boolean(),
  :is_undoing_jump => boolean(),
  :pre_jump_state => map() | nil,
  :undo_jump_actions => [Monty.Frameworks.Actions.Action.t()],
  :attempting_to_find_object => boolean(),
  :tangential_angle => float() | nil,
  :touch_search_amount => float(),
  :last_surface_policy_action => Monty.Frameworks.Actions.Action.t() | nil,
  :telemetry => term(),
  optional(atom()) => term()
}

A policy that can run the surface cycle: the fields the step reads.

SurfacePolicy and SurfacePolicyCurvatureInformed both carry them, which is how Monty's inheritance is expressed here (docs/monty-differences.md §2.9).

t()

@type t() :: %Monty.Frameworks.Models.MotorPolicies.SurfacePolicy{
  action_sampler: Monty.Frameworks.Actions.ActionSamplers.ActionSampler.t(),
  agent_id: atom() | String.t(),
  alpha: float(),
  attempting_to_find_object: boolean(),
  desired_object_distance: float(),
  is_jumping: boolean(),
  is_undoing_jump: boolean(),
  last_surface_policy_action: Monty.Frameworks.Actions.Action.t() | nil,
  pre_jump_state: Monty.Frameworks.Models.MotorSystemState.AgentState.t() | nil,
  rng: :rand.state() | nil,
  tangential_angle: float(),
  telemetry:
    Monty.Frameworks.Models.MotorPolicies.SurfacePolicyTelemetry.t() | nil,
  touch_search_amount: float(),
  undo_action: Monty.Frameworks.Actions.Action.t() | nil,
  undo_jump_actions: [Monty.Frameworks.Actions.Action.t()],
  use_goal_driven_actions: boolean()
}

Functions

call(policy, ctx, observations, state, percept, goal)

One step: a jump if one is in flight or asked for, otherwise the cycle.

The search state is reset on every step that goes on to the cycle, so falling off the object later starts a search from the beginning.

handle_successful_jump(policy)

@spec handle_successful_jump(surface_state()) :: surface_state()

A jump has landed and the object is in view: carry on as if we had just moved.

Monty overrides its _handle_successful_jump here so that the corrective part of the cycle runs next, and records that the step where the jump landed is telemetry rather than a curvature choice.

new(options)

@spec new(keyword()) :: t()

A surface policy for :agent_id, with :alpha weighting new directions.

:desired_object_distance is how far the sensor should sit from the surface, Monty's 2.5 cm unless told otherwise.

reset(policy)

@spec reset(surface_state()) :: surface_state()

Starts the next episode: no jump in flight, and the search forgotten.

state_dict(surface_policy)

@spec state_dict(t()) :: map()

Nothing to report: what it knows is the action it would undo.

surface_step(policy, ctx, observations, state, percept, goal)

One step of the surface cycle, for any policy that carries its fields.

Monty's SurfacePolicy.__call__, reached here through call/6 and directly by SurfacePolicyCurvatureInformed, which is a subclass there and a second struct here (docs/monty-differences.md §2.9). The tangential direction is the one part it overrides, so that is asked of the policy's own module.

tangential_direction(policy, ctx, state, percept)

@spec tangential_direction(
  surface_state(),
  Monty.Context.t(),
  map(),
  Monty.Cmp.Message.t() | nil
) ::
  {Monty.Geometry.Rotation.vector(), surface_state()}

A direction that is mostly the last one and partly new, in the agent's frame.

Monty's SurfacePolicy.tangential_direction, and the one method SurfacePolicyCurvatureInformed overrides — hence public, and reached by the policy's own module (docs/monty-differences.md §2.9).