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¶
- Prioritize Largest Errors: Start with objects furthest from their targets
- Approach Carefully: Use slow, controlled movements near targets
- Center Above Target: Position gripper directly over marker before lowering
- Fine Adjustment: Make small corrections at placement height
- 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.