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 -> MoveTangentiallyMoving 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
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
@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).
@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
@spec call( t(), Monty.Context.t(), map() | nil, Monty.Frameworks.Models.MotorSystemState.t(), Monty.Cmp.Message.t() | nil, Monty.Cmp.Goal.t() | nil ) :: {Monty.Frameworks.Models.MotorPolicies.MotorPolicyResult.t(), t()}
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.
@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.
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.
@spec reset(surface_state()) :: surface_state()
Starts the next episode: no jump in flight, and the search forgotten.
Nothing to report: what it knows is the action it would undo.
@spec surface_step( surface_state(), Monty.Context.t(), map() | nil, map(), Monty.Cmp.Message.t() | nil, Monty.Cmp.Goal.t() | nil ) :: {Monty.Frameworks.Models.MotorPolicies.MotorPolicyResult.t(), surface_state()}
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.
@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).