Skip to content

Precision Placement Challenge

Difficulty: Hard Time Limit: 90 seconds Status: Active

Overview

The Precision Placement scenario is an advanced manipulation challenge where a robot must pick up colored objects and place them precisely on matching target markers. Unlike sorting (where proximity is enough), this scenario requires exact positioning within tight tolerances.

Objective

Place each colored object (red, blue, green cylinders) precisely on its matching colored target marker. Placement must be within 2cm of the marker center to count as successful.

Environment

Scene Layout

┌─────────────────────────────────────────┐
│                                         │
│        🔴 ← Red target marker           │
│   🔵          🟢  ← Target markers      │
│                                         │
│                                         │
│   🟥  🟦  🟩   ← Objects to place       │
│                                         │
│   🤖═══════╗                            │
│            ║  ← Robot arm with gripper  │
└────────────╬═════════════════════────────┘

Components

  • Objects: 3 colored cylinders (red, blue, green)
  • Targets: 3 colored circular markers on the table surface
  • Goal: Place each object precisely on its matching colored marker

Robot

  • Type: 4-DOF articulated arm with 2-finger gripper
  • Joints: Base rotation, shoulder, elbow, wrist rotation
  • Gripper: Two-finger parallel gripper
  • Strategy: Pick up objects and place them with precision

Observations

Your policy receives:

observation = {
    # Arm joint positions (4 values)
    'joint_positions': np.array([base, shoulder, elbow, wrist]),

    # Gripper opening (distance between fingers)
    'gripper_opening': float,

    # Gripper position (3 values)
    'gripper_pos': np.array([x, y, z]),

    # Object positions (dictionary)
    'object_positions': {
        'red_object_pos': np.array([x, y, z]),
        'blue_object_pos': np.array([x, y, z]),
        'green_object_pos': np.array([x, y, z]),
    },

    # Target marker positions (dictionary)
    'target_positions': {
        'red_target_pos': np.array([x, y, z]),
        'blue_target_pos': np.array([x, y, z]),
        'green_target_pos': np.array([x, y, z]),
    },

    # Current placement errors (dictionary)
    'placement_errors': {
        'red_error': float,    # Distance from red object to red target
        'blue_error': float,
        'green_error': float,
    },
}

Actions

Return a 6D action vector:

action = np.array([
    base_torque,      # Base joint torque (-5 to 5)
    shoulder_torque,  # Shoulder joint torque (-5 to 5)
    elbow_torque,     # Elbow joint torque (-5 to 5)
    wrist_torque,     # Wrist joint torque (-3 to 3)
    left_finger,      # Left finger force (-2 to 2)
    right_finger,     # Right finger force (-2 to 2)
])

Scoring

Verdict Criteria

Verdict Criteria
PASS At least one object precisely placed (< 2cm error)
FAIL No objects precisely placed on markers

Score Calculation

Precise placement (< 2cm): +50 points per object
Close placement (< 5cm): +20 points per object
Object fallen off table: -20 points per object
Distance penalty: -5 * error for imprecise objects
  • Maximum possible score: 150 points (all 3 objects precisely placed)

Precision Thresholds

Level Distance Points
Precise < 2cm +50
Close < 5cm +20
Miss > 5cm Penalty

Tips

Strategy

  1. Prioritize Largest Errors: Start with objects furthest from their targets
  2. Approach Carefully: Use slow, controlled movements near targets
  3. Center Above Target: Position gripper directly over marker before lowering
  4. Fine Adjustment: Make small corrections at placement height
  5. Stable Release: Open gripper slowly to avoid knocking object

Key Differences from Block Stacking

Aspect Block Stacking Precision Placement
Goal Vertical stack Horizontal spread
Tolerance ~5cm ~2cm
Constraint Order matters Position matters
Challenge Height XY accuracy

Common Mistakes

  • Rushing Placement: Moving too fast causes imprecision
  • Early Release: Opening gripper before centering over target
  • Wrong Target: Placing object on wrong colored marker
  • Knock-over: Hitting already-placed objects
  • Oscillation: Overcorrecting during fine adjustment

Example Approach

import numpy as np

# States
PICK = 0
MOVE = 1
PLACE = 2
RELEASE = 3

state = PICK
current_color = None
timer = 0

def policy(observation: dict) -> np.ndarray:
    global state, current_color, timer

    gripper_pos = observation['gripper_pos']
    objects = observation['object_positions']
    targets = observation['target_positions']
    errors = observation['placement_errors']

    # Select object with largest error
    if state == PICK and current_color is None:
        max_error = -1
        for key, error in errors.items():
            color = key.replace('_error', '')
            if error > max_error and error > 0.02:  # Not already placed
                max_error = error
                current_color = color

        if current_color is None:
            return np.zeros(6)  # All placed!

    obj_pos = objects[f'{current_color}_object_pos']
    target_pos = targets[f'{current_color}_target_pos']

    action = np.zeros(6)

    if state == PICK:
        # Move to object, grasp it
        target = [obj_pos[0], obj_pos[1], obj_pos[2] + 0.03]
        action = move_to(gripper_pos, target)
        action[4], action[5] = 1, -1  # Open

        if close_to(gripper_pos, target):
            action[4], action[5] = -2, 2  # Close
            timer += 1
            if timer > 20:
                state = MOVE
                timer = 0

    elif state == MOVE:
        # Lift and move to target
        target = [target_pos[0], target_pos[1], 0.55]
        action = move_to(gripper_pos, target)
        action[4], action[5] = -2, 2  # Keep closed

        if close_to(gripper_pos, target):
            state = PLACE

    elif state == PLACE:
        # Lower precisely to target
        target = [target_pos[0], target_pos[1], 0.44]
        action = move_to(gripper_pos, target, slow=True)  # Slow for precision
        action[4], action[5] = -2, 2

        if close_to(gripper_pos, target, tol=0.015):
            state = RELEASE
            timer = 0

    elif state == RELEASE:
        # Open gripper
        action[4], action[5] = 1, -1
        timer += 1

        if timer > 15:
            state = PICK
            current_color = None
            timer = 0

    return action

def move_to(current, target, slow=False):
    diff = np.array(target) - np.array(current)
    dist = np.linalg.norm(diff)
    gain = min(dist * (5 if slow else 10), (1.5 if slow else 3.0))
    direction = diff / (dist + 1e-6)

    action = np.zeros(6)
    action[0] = direction[1] * gain
    action[1] = -direction[0] * gain * 0.5
    action[2] = direction[2] * gain
    return action

def close_to(a, b, tol=0.03):
    return np.linalg.norm(np.array(a) - np.array(b)) < tol

Local Testing

Test your policy locally before submitting:

from botmanifold import BotManifoldClient

client = BotManifoldClient()

# Load scenario locally
env = client.load_scenario("precision_placement_v1")

# Run your policy
observation = env.reset()
done = False

while not done:
    action = policy(observation)
    observation, reward, done, info = env.step(action)

print(f"Precisely placed: {info['precisely_placed']}/{info['total_objects']}")
print(f"Average error: {info['average_error']:.3f}m")

Leaderboard

View current rankings at botmanifold.com/arena/leaderboard?scenario=precision_placement_v1

Video Example

Watch an example of a successful policy completing this scenario on the scenario detail page.