Grasping analysis API

The interactive grasping demo uses a randomly wandering ball. The analysis package supports measured comparisons by presenting the same predefined ball visit to each robot. These tools are separate from GraspingWorld: they configure and observe the demonstrated behavior without changing a robot’s decisions.

A GraspingEncounter defines the ball’s starting distance and movement. Negative velocity approaches the jaws and positive velocity moves away. Its opportunity interval states when capture counts as a valid response; the robots cannot read this interval. Optional noise, dropouts, and false detections alter only the proximity reading.

from qrobot_simulator.grasping_world import ClassicalGripper
from qrobot_simulator.grasping_world.analysis import (
    ControlledGraspingWorld,
    GraspingEncounter,
    MotionSegment,
    ProximityDisturbance,
    TimeInterval,
    record_encounter,
)

encounter = GraspingEncounter(
    initial_distance=24.0,
    motion=(
        MotionSegment(duration=2.0, velocity=-5.0),
        MotionSegment(duration=2.5, velocity=0.0),
        MotionSegment(duration=2.0, velocity=5.0),
    ),
    opportunity=TimeInterval(start=1.8, duration=2.7),
    disturbance=ProximityDisturbance(noise_standard_deviation=0.05),
)

world = ControlledGraspingWorld.create(ClassicalGripper(), encounter, seed=7)
record = record_encounter(world, duration=encounter.duration, dt=0.05)
print(record.outcome.label, record.outcome.capture_latency)

record.samples contains the readings, jaw command, and physical state at each step. record.events identifies closing, capture, consumption, opening, and failed actions. record.outcome derives the encounter result and response times from those events. record.metadata retains the configurations, seeds, duration, and physics step needed to identify the run. Trial generation, parameter selection, aggregate statistics, and paper figures belong in the experiment repository.

class qrobot_simulator.grasping_world.analysis.ControlledGraspingWorld(arena: GraspingArena, gripper: BaseGripper, ball: BallPrey, config: GraspingWorldConfig = GraspingWorldConfig(near_distance=5.0, far_distance=20.0, minimum_distance=5.0, grippable_distance=15.0, consumption_time=2.5, ball_distance_scale=0.16, sensor_offset_x=0.55, arena_width=12.0, arena_height=6.0, arena_cell_size=1.0), prey_config: BallPreyConfig = BallPreyConfig(radius=0.32, color='#2878d0', max_distance=45.0, initial_distance_range=(24.0, 40.0), respawn_distance_range=(30.0, 45.0), initial_velocity_range=(-1.0, 1.0), velocity_kick_range=(-4.0, 4.0), max_speed=7.0, motion_change_interval=(0.35, 1.1), max_near_duration=1.0, escape_speed=5.0), elapsed: float = 0.0, touch_pressed: bool = False, readings: dict[str, float]=<factory>, correct_grips: int = 0, missed_grips: int = 0, empty_grips: int = 0, consumed_prey: int = 0, premature_releases: int = 0, seed: int | None = None, grip_response_times: list[float] = <factory>, _inside_visit: bool = False, _visit_gripped: bool = False, _consumption_started_at: float | None = None, _visit_started_at: float | None = None, _rng: Random = <factory>)[source]

Present one predefined ball visit instead of randomly wandering prey.

This class uses the sensors and physical interactions of GraspingWorld, but the analysis code supplies the ball’s starting distance, movement, and optional sensor errors. Consumed prey does not respawn because each instance represents one visit. The random demonstration remains entirely in GraspingWorld.

classmethod create(gripper: BaseGripper, encounter: GraspingEncounter, seed: int | None = None, disturbance_seed: int | None = None, config: GraspingWorldConfig = GraspingWorldConfig(near_distance=5.0, far_distance=20.0, minimum_distance=5.0, grippable_distance=15.0, consumption_time=2.5, ball_distance_scale=0.16, sensor_offset_x=0.55, arena_width=12.0, arena_height=6.0, arena_cell_size=1.0), prey_config: BallPreyConfig = BallPreyConfig(radius=0.32, color='#2878d0', max_distance=45.0, initial_distance_range=(24.0, 40.0), respawn_distance_range=(30.0, 45.0), initial_velocity_range=(-1.0, 1.0), velocity_kick_range=(-4.0, 4.0), max_speed=7.0, motion_change_interval=(0.35, 1.1), max_near_duration=1.0, escape_speed=5.0)) → ControlledGraspingWorld[source]

Create one repeatable visit for a classical or quantum gripper.

class qrobot_simulator.grasping_world.analysis.GraspingEncounter(initial_distance: float, motion: tuple[MotionSegment, ...], opportunity: TimeInterval | None, disturbance: ProximityDisturbance = ProximityDisturbance(noise_standard_deviation=0.0, dropouts=(), false_positives=()))[source]

Define the exogenous inputs and ground truth of one paired encounter.

motion is a sequence of constant-velocity segments. Negative velocity approaches the gripper and positive velocity moves away. opportunity is ground-truth metadata for experiment scoring; it does not alter the physics.

property duration: float

Return the total simulated duration of all motion segments.

is_valid_opportunity(elapsed: float) → bool[source]

Return the declared grasp ground truth at elapsed.

velocity_at(elapsed: float) → float[source]

Return the commanded velocity at one elapsed encounter time.

class qrobot_simulator.grasping_world.analysis.MotionSegment(duration: float, velocity: float)[source]

Command one constant ball velocity for a simulated duration.

class qrobot_simulator.grasping_world.analysis.TimeInterval(start: float, duration: float)[source]

Represent one half-open interval in simulated seconds.

class qrobot_simulator.grasping_world.analysis.ProximityDisturbance(noise_standard_deviation: float = 0.0, dropouts: tuple[TimeInterval, ...] = (), false_positives: tuple[TimeInterval, ...] = ())[source]

Configure repeatable noise, dropouts, and false-positive intervals.

Dropouts and false positives replace the clean reading during their configured intervals. A dropout takes precedence if the two kinds of interval overlap. Gaussian noise is then added and the result is clipped to the sensor range.

class qrobot_simulator.grasping_world.analysis.GraspingRunRecord(samples: tuple[GraspingSample, ...], events: tuple[GraspingEvent, ...], metadata: GraspingRunMetadata, outcome: GraspingOutcome)[source]

Collect raw samples, events, setup metadata, and the derived result.

class qrobot_simulator.grasping_world.analysis.GraspingSample(elapsed: float, ball_distance: float, ball_velocity: float, ball_present: bool, ball_caught: bool, gripper_closed: bool, activation: float, proximity: float, touch: float, valid_opportunity: bool, diagnostics: dict[str, float | None])[source]

Store everything needed to inspect one instant of the simulation.

The sample contains the readings available to the brain, its returned jaw command, and the physical state after that command has been applied.

class qrobot_simulator.grasping_world.analysis.GraspingEvent(elapsed: float, kind: Literal['jaws_closed', 'prey_captured', 'empty_closure', 'prey_consumed', 'jaws_opened', 'premature_release'])[source]

Record one discrete physical action or consequence and when it occurred.

class qrobot_simulator.grasping_world.analysis.GraspingOutcome(label: Literal['true_positive', 'false_positive', 'false_negative', 'correct_rejection'], capture_latency: float | None, release_latency: float | None, premature_releases: int, extra_closures: int, failed_release: bool)[source]

Summarize whether the robot acted correctly during one encounter.

A valid encounter is a true positive when prey is captured during the declared opportunity and a false negative otherwise. An encounter without an opportunity is a false positive when the robot closes its jaws and a correct rejection when it leaves them open.

Capture latency measures the delay from the start of a valid opportunity to capture. Release latency measures the delay from consumption to the first subsequent opening. failed_release is true when consumed prey disappears but the robot is still closed when recording ends.

class qrobot_simulator.grasping_world.analysis.GraspingRunMetadata(encounter: GraspingEncounter, world_config: GraspingWorldConfig, prey_config: BallPreyConfig, gripper_config: BaseGripperConfig, seed: int | None, disturbance_seed: int | None, duration: float, physics_step: float)[source]

Identify the complete setup required to repeat one recorded run.