Skip to content

Object Sorting Challenge

Difficulty: Easy Time Limit: 30 seconds Status: Active

Overview

The Object Sorting scenario is a beginner-friendly challenge where a robot arm must push colored cubes into the correct bins. Red cubes go in the red bin (left), blue cubes go in the blue bin (right).

Objective

Sort at least one cube correctly by pushing it into the matching colored bin.

Environment

Scene Layout

┌─────────────────────────────────────────┐
│                                         │
│   [RED BIN]             [BLUE BIN]      │
│   ┌───────┐             ┌───────┐       │
│   │  🔴   │             │  🔵   │       │
│   └───────┘             └───────┘       │
│                                         │
│         🟥  🟦     ← Colored cubes      │
│      🟦     🟥                          │
│                                         │
│            🤖  ← Robot arm              │
│                                         │
└─────────────────────────────────────────┘

Objects

  • Red Cubes: 2 small red cubes that should go in the left (red) bin
  • Blue Cubes: 2 small blue cubes that should go in the right (blue) bin
  • Bins: Two collection bins positioned at fixed locations

Robot

  • Type: 3-DOF planar arm with pushing end-effector
  • Control: Joint torque control
  • Strategy: Push objects rather than grasp

Observations

Your policy receives:

observation = {
    # Joint positions (3 values)
    'joint_positions': np.array([base, shoulder, elbow]),

    # End-effector position (3 values)
    'end_effector_pos': np.array([x, y, z]),

    # Bin positions (3 values each)
    'red_bin_pos': np.array([x, y, z]),
    'blue_bin_pos': np.array([x, y, z]),

    # Cube positions (N x 3 arrays)
    'red_cube_positions': np.array([
        [x1, y1, z1],
        [x2, y2, z2],
    ]),
    'blue_cube_positions': np.array([
        [x1, y1, z1],
        [x2, y2, z2],
    ]),
}

Actions

Return a 3D action vector (joint torques):

action = np.array([
    base_torque,      # Base joint torque (-1 to 1)
    shoulder_torque,  # Shoulder joint torque (-1 to 1)
    elbow_torque      # Elbow joint torque (-1 to 1)
])

Scoring

Verdict Criteria

Verdict Criteria
PASS At least one cube sorted into the correct bin
FAIL No cubes sorted, or cubes placed in wrong bins

Reward Calculation

Correct placement: +10 points per cube
Wrong bin: -5 points per cube
Distance penalty: -(distance to nearest correct bin) per unsorted cube

Tips

Strategy

  1. Identify Targets: Locate the nearest unsorted cube
  2. Approach: Move the end-effector behind the cube
  3. Push: Apply force toward the correct bin
  4. Repeat: Move to the next cube

Common Mistakes

  • Wrong Direction: Pushing toward the wrong bin
  • Missing the Cube: Not aligning properly before pushing
  • Overcorrection: Oscillating instead of smooth movement
  • Ignoring Colors: Treating all cubes the same

Example Approach

import numpy as np

def policy(observation: dict) -> np.ndarray:
    ee_pos = observation['end_effector_pos']
    red_bin = observation['red_bin_pos']
    blue_bin = observation['blue_bin_pos']
    red_cubes = observation['red_cube_positions']
    blue_cubes = observation['blue_cube_positions']

    # Find nearest unsorted cube
    target_pos = None
    target_bin = None
    min_dist = float('inf')

    # Check red cubes
    for cube_pos in red_cubes:
        if np.linalg.norm(cube_pos[:2] - red_bin[:2]) < 0.1:
            continue  # Already sorted
        dist = np.linalg.norm(ee_pos[:2] - cube_pos[:2])
        if dist < min_dist:
            min_dist = dist
            target_pos = cube_pos
            target_bin = red_bin

    # Check blue cubes
    for cube_pos in blue_cubes:
        if np.linalg.norm(cube_pos[:2] - blue_bin[:2]) < 0.1:
            continue  # Already sorted
        dist = np.linalg.norm(ee_pos[:2] - cube_pos[:2])
        if dist < min_dist:
            min_dist = dist
            target_pos = cube_pos
            target_bin = blue_bin

    if target_pos is None:
        return np.zeros(3)  # All sorted!

    # Move toward object or push toward bin
    dist_to_target = np.linalg.norm(ee_pos[:2] - target_pos[:2])

    if dist_to_target > 0.08:
        direction = target_pos[:2] - ee_pos[:2]
    else:
        direction = target_bin[:2] - target_pos[:2]

    direction = direction / (np.linalg.norm(direction) + 1e-6)

    # Convert to joint torques (simplified)
    base_torque = direction[1] * 5.0
    shoulder_torque = -direction[0] * 3.0
    elbow_torque = 1.0

    return np.array([base_torque, shoulder_torque, elbow_torque])

Local Testing

Test your policy locally before submitting:

from botmanifold import BotManifoldClient

client = BotManifoldClient()

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

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

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

print(f"Sorted: {info['correct']} correct, {info['incorrect']} incorrect")

Leaderboard

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

Video Example

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