# Simulate a Rosie robot

> Download the URDF, MuJoCo model, STEP and robot.json of the Rosie 600, 1000 or 1400 and run it in PyBullet, MuJoCo or ROS 2, with the robot's own kinematics, joint limits, speeds, torques and mass properties.

URL: https://advancedmetalresearch.com/docs/guides/simulate-a-rosie-robot
Section: RosieOS docs / Guides
Last updated: 2026-10-10

Each Rosie robot has a free simulation and CAD kit: a URDF, a MuJoCo model, an SRDF, a STEP assembly, meshes and `robot.json`, plus quickstart scripts and a ROS 2 package. The geometry is the robot's exterior, one filled solid per link: the structural parts keep the CAD's own surfaces, and the motors and gearboxes are plain envelopes of the same outer size. The kinematics, joint limits, speeds, torques, payload ratings and mass properties are the robot's own.

To run RosieOS itself against a simulated cell, see [Run everything in simulation](https://advancedmetalresearch.com/docs/guides/run-in-simulation). The kits use the same joint names, axes and zero pose as RosieOS's robot descriptions, so joint values carry over unchanged.

## Downloads

| Robot | Kit | robot.json | URDF | MuJoCo | STEP | GLB | IK cases |
|---|---|---|---|---|---|---|---|
| Rosie 600 | [rosie-600-sim-kit.zip](https://advancedmetalresearch.com/assets/sim/rosie-600-sim-kit.zip) | [robot.json](https://advancedmetalresearch.com/assets/sim/600/robot.json) | [rosie_600.urdf](https://advancedmetalresearch.com/assets/sim/600/urdf/rosie_600.urdf) | [rosie_600.xml](https://advancedmetalresearch.com/assets/sim/600/mjcf/rosie_600.xml) | [rosie_600.step](https://advancedmetalresearch.com/assets/sim/600/step/rosie_600.step) | [rosie_600.glb](https://advancedmetalresearch.com/assets/sim/600/glb/rosie_600.glb) | [ik_cases.json](https://advancedmetalresearch.com/assets/sim/600/ik_cases.json) |
| Rosie 1000 | [rosie-1000-sim-kit.zip](https://advancedmetalresearch.com/assets/sim/rosie-1000-sim-kit.zip) | [robot.json](https://advancedmetalresearch.com/assets/sim/1000/robot.json) | [rosie_1000.urdf](https://advancedmetalresearch.com/assets/sim/1000/urdf/rosie_1000.urdf) | [rosie_1000.xml](https://advancedmetalresearch.com/assets/sim/1000/mjcf/rosie_1000.xml) | [rosie_1000.step](https://advancedmetalresearch.com/assets/sim/1000/step/rosie_1000.step) | [rosie_1000.glb](https://advancedmetalresearch.com/assets/sim/1000/glb/rosie_1000.glb) | [ik_cases.json](https://advancedmetalresearch.com/assets/sim/1000/ik_cases.json) |
| Rosie 1400 | [rosie-1400-sim-kit.zip](https://advancedmetalresearch.com/assets/sim/rosie-1400-sim-kit.zip) | [robot.json](https://advancedmetalresearch.com/assets/sim/1400/robot.json) | [rosie_1400.urdf](https://advancedmetalresearch.com/assets/sim/1400/urdf/rosie_1400.urdf) | [rosie_1400.xml](https://advancedmetalresearch.com/assets/sim/1400/mjcf/rosie_1400.xml) | [rosie_1400.step](https://advancedmetalresearch.com/assets/sim/1400/step/rosie_1400.step) | [rosie_1400.glb](https://advancedmetalresearch.com/assets/sim/1400/glb/rosie_1400.glb) | [ik_cases.json](https://advancedmetalresearch.com/assets/sim/1400/ik_cases.json) |

Every file of every kit also has its own URL under `/assets/sim/<model>/`, with the same layout as the zip. [`/assets/sim/index.json`](/assets/sim/index.json) lists the kits and all their files. The URDF and MJCF reference their meshes by relative path, so download the zip (or the whole folder) rather than the single file.

## What a kit contains

rosie_1000_description/:

```text
README.md            conventions, joint table, quickstart (the same code as below)
LICENSE.txt
robot.json           kinematics, limits, speeds, torques, accelerations, payloads, mass properties, frames, FK reference poses, benchmark cycle
ik_cases.json        IK reference cases solved by RosieOS's IK, self-contained
benchmark/           the benchmark pick-and-place cycle with 1 kg: t, q, qd, qdd every 1 ms (CSV and JSON)
glb/rosie_1000.glb   glTF binary, one node per link in the joint hierarchy
urdf/rosie_1000.urdf
mjcf/rosie_1000.xml  MuJoCo 3.1 or later
srdf/rosie_1000.srdf planning group "manipulator", named poses, disabled collision pairs (MoveIt)
step/rosie_1000.step AP214 assembly, mm, one solid per link (exact CAD surfaces) at the home pose
meshes/visual/       one STL per link, link frame, metres
meshes/collision/    convex pieces per link, link frame, metres
launch/ rviz/ package.xml CMakeLists.txt   ROS 2 package rosie_1000_description
examples/            pybullet_demo.py, mujoco_demo.py, fk_ik.py
```

## Frames and conventions

- Units: metres, kilograms, radians, seconds. The STEP is in millimetres.
- Joints `J1` to `J6` connect `base_link`, `link_1` ... `link_6`. Every link frame is parallel to `base_link` at the zero pose and sits on its joint, so each joint origin is a pure translation.
- Axes: J1 z, J2 y, J3 y, J4 x, J5 y, J6 z. Positive follows the right-hand rule.
- Zero pose is home: upper arm vertical, forearm along +x, tool flange facing down.
- `tool0` is on the tool flange face (the ISO 9409 mounting face), z out of the flange, x along base +x at home. Put your tool's TCP at a fixed offset from `tool0`.
- `base_footprint` is the floor or mounting face under J1, and the root of the URDF. `base_link` is the robot base frame above it: 175 mm for the Rosie 600 and 1000, 29.4 mm for the Rosie 1400 (its CAD base frame, inside the base plate). The STEP's origin is `base_link`.

| Robot | Wrist centre at J2 = 90°, J3 = -90° | Published reach (wrist / flange) | tool0 at home | Mass |
|---|---|---|---|---|
| Rosie 600 | 600.0 mm from the J1 axis | 600 / 677 mm | (0.330, 0, 0.423) m | 29.3 kg |
| Rosie 1000 | 1,000.0 mm | 1,000 / 1,077 mm | (0.530, 0, 0.623) m | 38.6 kg |
| Rosie 1400 | 1,433.7 mm | 1,400 / 1,499 mm | (0.834, 0, 0.756) m | 77.0 kg |

The Rosie 1400's published reach is 1,400 mm; its geometry stretches to 1,433.7 mm. `robot.json` gives both, under `reach`.

## Quickstart

Download and run a kit:

Terminal:

```bash
curl -LO https://advancedmetalresearch.com/assets/sim/rosie-1000-sim-kit.zip
unzip -q rosie-1000-sim-kit.zip && cd rosie_1000_description
pip install mujoco pybullet numpy
python examples/mujoco_demo.py      # also: pybullet_demo.py, fk_ik.py
```

The snippets below run from inside the kit folder. They are written for the Rosie 1000; for another robot, change `1000` to `600` or `1400`.

**MuJoCo**

mujoco_hold.py:

```python
import mujoco
import numpy as np

model = mujoco.MjModel.from_xml_path("mjcf/rosie_1000.xml")
data = mujoco.MjData(model)
data.ctrl[:] = np.radians([30, 45, -10, 0, -35, 0])   # position servos on J1..J6
for _ in range(1500):                                  # 3 s at 2 ms steps
    mujoco.mj_step(model, data)
print(np.degrees(data.qpos).round(2))                  # joint angles (deg)
print(data.actuator_force.round(1))                    # joint torques (N m)
print(data.site("tool0").xpos.round(4))                # flange face (m)
```
**PyBullet**

pybullet_move.py:

```python
import math
import pybullet as p

p.connect(p.DIRECT)                                    # p.GUI for a window
p.setGravity(0, 0, -9.81)
robot = p.loadURDF("urdf/rosie_1000.urdf", useFixedBase=True,
                   flags=p.URDF_USE_INERTIA_FROM_FILE)
joints = [p.getJointInfo(robot, i) for i in range(p.getNumJoints(robot))]
arm = [j for j in joints if j[2] == p.JOINT_REVOLUTE]  # J1..J6
tool0 = next(j[0] for j in joints if j[12] == b"tool0")

target = [math.radians(a) for a in (30, 45, -10, 0, -35, 0)]
for j, q in zip(arm, target):                          # j[10] peak torque, j[11] max speed
    p.setJointMotorControl2(robot, j[0], p.POSITION_CONTROL, targetPosition=q,
                            force=j[10], maxVelocity=j[11])
for _ in range(3 * 240):
    p.stepSimulation()
print(p.getLinkState(robot, tool0, computeForwardKinematics=True)[4])
```
**ROS 2**

Terminal, in a ROS 2 workspace:

```bash
cp -r rosie_1000_description ~/ros2_ws/src/
cd ~/ros2_ws && colcon build --packages-select rosie_1000_description
source install/setup.bash
ros2 launch rosie_1000_description display.launch.py   # RViz with joint sliders
```
**Kinematics**

fk.py, numpy only:

```python
import json
import urllib.request

import numpy as np

URL = "https://advancedmetalresearch.com/assets/sim/1000/robot.json"
req = urllib.request.Request(URL, headers={"User-Agent": "rosie-sim-kit/1.0"})
robot = json.load(urllib.request.urlopen(req))

def rot(axis, q):
    x, y, z = axis
    c, s, t = np.cos(q), np.sin(q), 1 - np.cos(q)
    return np.array([[t * x * x + c, t * x * y - s * z, t * x * z + s * y],
                     [t * x * y + s * z, t * y * y + c, t * y * z - s * x],
                     [t * x * z - s * y, t * y * z + s * x, t * z * z + c]])

def fk(q):
    """Pose of tool0 (4x4) in base_link for joint angles q (rad)."""
    T = np.eye(4)
    for j, qi in zip(robot["joints"], q):
        A = np.eye(4)
        A[:3, 3], A[:3, :3] = j["origin_xyz"], rot(j["axis"], qi)
        T = T @ A
    return T @ np.diag([1, -1, -1, 1])                 # tool0: z out of the flange

print(fk(robot["poses"]["stretched"])[:3, 3])          # (m)
```

`examples/fk_ik.py` in each kit adds a damped least-squares inverse kinematics solver that respects the joint limits.

The URDF's mesh paths are relative to the file, which PyBullet, Isaac Sim and most URDF libraries resolve directly. ROS 2 needs `package://` URIs: `display.launch.py` rewrites them on load, or run `sed 's|filename="../meshes/|filename="package://rosie_1000_description/meshes/|'` over the URDF yourself.

## robot.json

| Key | Contents |
|---|---|
| `joints[]` | `name`, `parent`, `child`, `origin_xyz` (m), `axis`, `lower` / `upper` (rad and deg), `max_velocity` (rad/s and deg/s), `effort_peak` and `effort_continuous` (N·m), `drive_peak_torque_at_joint` (N·m), `armature` (kg·m²), `damping`, `derived_max_acceleration` (rad/s² and deg/s²) |
| `links[]` | `mass` (kg), `com` (m) and `inertia` (kg·m², about the CoM, link frame), mesh paths |
| `frames` | `base_footprint`, `base_link` (height above the floor) and `tool0` |
| `home`, `poses` | Joint angles of `home`, `work` and `stretched` (rad) |
| `fk_reference[]` | For each pose: wrist centre, `tool0` position and rotation, to check your own kinematics against |
| `ik_reference` | Where the IK reference cases are (`ik_cases.json`), the solver and the branch flags |
| `motion` | Motion profiles RosieOS uses, its acceleration and jerk settings, and how `derived_max_acceleration` is computed |
| `benchmarks` | The cycle behind the published cycle times: path, TCP, payload, profile, accelerations, segment times, trajectory files |
| `reach`, `payload`, `repeatability_mm`, `mass` | Published ratings, and the geometric reach |
| `geometry` | How the geometry was made and how it compares with the CAD |

## Accelerations and motion profiles

- `joints[].derived_max_acceleration` is each joint's peak-torque acceleration from standstill at the stretched pose with the rated payload, computed from `robot.json` itself: (`effort_peak` − |gravity torque|) / (`armature` + the inertia about the joint axis of everything it moves). It is conservative: `effort_peak` is the torque the gearbox passes to the arm, while the motor accelerates its own rotor ahead of the gearbox with its own torque, up to `drive_peak_torque_at_joint`. `motion.derived_max_acceleration` also gives the values at the `work` pose with 1 kg.
- `benchmarks.acceleration` lists the accelerations behind the published cycle times: 80 % of each joint's peak-torque acceleration at the cycle poses, from the drive model.
- `motion.rosieos_settings` (Rosie 1400 only) lists the planning settings of RosieOS's simulator model, `rosie_1400_v3`. They are software settings for that simulated cell, well under the robot's capability, not ratings. RosieOS states no per-joint jerk limit: its planner bounds jerk at 2 × the acceleration setting / 0.2 s, and jog, stop ramps and Move-to are jerk-limited by the cell's machine jog jerk where one is set.
- Profiles: RosieOS plans programs as joint-space cubic splines (free-space moves as clamped cubic B-splines, so acceleration is continuous; welds as cubic Hermite curves) within the velocity and acceleration settings, and plays them as planned. Jog, stops and Move-to are jerk-limited (double-S). The benchmark cycle uses plain trapezoids.

## Benchmark cycle

The published cycle times ("Cycle, 25 / 305 / 25 mm, 1 kg" and "Cycle at rated payload" in the [specification](https://advancedmetalresearch.com/rosie#specs)) come from one model, and `robot.json` `benchmarks` gives every condition it used:

- Path: the tool points straight down and keeps its heading. Pick point A and place point B are 305 mm apart along y, centred 350 mm from the J1 axis on +x. The TCP lifts 25 mm at each: A low, A high, B high, B low, then back the same way. `benchmarks.path.tcp_points_base_link_m` gives the points in `base_link`. The low points are 50 mm above `base_link` on the Rosie 600 and 1000 and 75.5 mm above it on the Rosie 1400.
- TCP: 50 mm out from the flange face along the tool axis.
- Payload: everything on the flange as one body, gripper included, with no other tool mass. That is 1 kg for the 1 kg cycle and the rated payload for the other. The CoM is 100 mm out along the tool axis and 50 mm off it, with the inertia of a uniform 100 mm cube. AMR defines no standard gripper or tool.
- Moves: six joint-space moves, each from rest to rest, with no blending, dwell or gripper time. Every joint follows a trapezoid at its benchmark acceleration, and the segment takes the slowest joint's time at `max_velocity`.

`benchmark/rosie_<model>_cycle_1kg.csv` (and `.json`) is that cycle with 1 kg, sampled every 1 ms: time, `q`, `qd` and `qdd` for J1 to J6, and the TCP. Its duration rounds to the published time. `benchmarks.notes` records how the model's payload and mass inputs relate to the published figures. `benchmarks.kit_model_check` gives the most torque each joint of the kit's MuJoCo model needs to follow the trajectory, as a fraction of its force limit; every joint stays within it.

## IK reference cases

Each kit's `ik_cases.json` is a standalone file, also online at `/assets/sim/<model>/ik_cases.json`. It holds twelve cases solved by RosieOS's own IK: the Cartesian resolver `robot-v4-cartesiand`, the same solver the pendant and offline programming use for Cartesian moves. Each case gives:

- a seed pose;
- the straight Cartesian move RosieOS resolved from it;
- the target pose of `tool0` in `base_link`;
- the solution RosieOS reached (`expected`, with its shoulder, elbow and wrist branch);
- every other solution inside the joint limits (`all_solutions`).

The file carries the kinematic chain, units, frames, tolerance, the solver's method and its RosieOS source files and commit. RosieOS has no robot description of the Rosie 600 or 1000, and its Rosie 1400 description is the simulator's cell model, so the solver ran on each kit's own chain; the `solver.provenance` field says so for each robot. This check needs only numpy and the file:

check_ik_cases.py:

```python
import json
import numpy as np

doc = json.load(open("ik_cases.json"))

def rot(axis, q):
    x, y, z = axis
    c, s, t = np.cos(q), np.sin(q), 1 - np.cos(q)
    return np.array([[t * x * x + c, t * x * y - s * z, t * x * z + s * y],
                     [t * x * y + s * z, t * y * y + c, t * y * z - s * x],
                     [t * x * z - s * y, t * y * z + s * x, t * z * z + c]])

def fk(q):
    T = np.eye(4)
    for j, qi in zip(doc["chain"]["joints"], q):
        A = np.eye(4)
        A[:3, 3], A[:3, :3] = j["origin_xyz"], rot(j["axis"], qi)
        T = T @ A
    return T @ np.diag([1, -1, -1, 1])                 # tool0: link_6 turned 180 deg about x

worst = 0.0
for case in doc["cases"]:
    for sol in [case["expected"]] + case["all_solutions"]:
        T = fk(sol["q"])
        worst = max(worst, np.abs(T[:3, 3] - case["target"]["xyz"]).max(),
                    np.abs(T[:3, :3] - case["target"]["rotation"]).max())
print(len(doc["cases"]), "cases, worst error", worst, "tolerance", doc["tolerance"]["position_m"])
```

## GLB

`glb/rosie_<model>.glb` is the visual geometry as one glTF 2.0 binary, for three.js, Babylon.js, `<model-viewer>`, Blender or a game engine. Its nodes follow the joint chain (`base_footprint` > `base_link` > `link_1` ... `link_6` > `tool0`). Each link node sits on its joint origin, so rotating it about its joint axis (in the node's `extras`) by the joint angle moves the robot. glTF is Y-up, so the root node turns the kit's Z-up frames upright.

## Simulation parameters

- URDF `effort` is `effort_peak`, the peak torque the gearbox passes to the arm. MuJoCo `forcerange` is `drive_peak_torque_at_joint`, the motor's peak torque times the gear ratio, which also pays for accelerating the motor's own rotor (`armature`); on the Rosie 1400 J4 it is the drive's set torque limit times the ratio. URDF `velocity` is the maximum joint speed.
- MuJoCo actuators are position servos: `ctrl` is the joint target in radians. Gains are the output peak torque (`effort_peak`) per 0.01 rad, critically damped, so a held pose sits within a few tenths of a degree under gravity. Replace them with your own controller as needed.
- `armature` is the reflected inertia of each joint's motor and gearbox. Joint damping is a nominal value.
- MuJoCo and the SRDF skip collisions between adjacent links only, whose hulls meet at each joint. Every other pair of links is checked.

## Geometry

For each link, every part except fasteners, pulleys and belts is fused into one solid whose outside is the original CAD surface: the same planes, cylinders, cones, tori and B-spline faces, so in a CAD system you can pick faces, edges and hole axes and dimension them. Bolt holes, counterbores and openings into the inside are capped flush with the face around them, open channels get a cover that follows their rim, and the inside is solid, with no internal parts. The base mounting face with its holes and bore, and the tool flange, are unchanged. The visual meshes are that solid, tessellated; the collision meshes are convex decompositions of it, up to ten pieces per link.

## Licence

The kits are free to use for simulation and integration, including commercially. See `LICENSE.txt` in each kit.

## Sources

Written from these files in the RosieOS repository (https://github.com/advanced-metal-research/RosieOS):

- `/assets/sim/index.json and each kit's robot.json and ik_cases.json (generated by AMR from the robot CAD and engineering data; amr-cad-work sim-kit/kitspec.py BENCHMARK, ROSIEOS_MOTION, ROSIEOS_IK)`
- `robot_description/robots/rosie_1400_v3/robot.urdf (joint names, axes, zero pose), RosieOS motion-server/v1/src cartesian_resolver.hpp and robot_v4_cartesian_nats_protocol.hpp (IK)`
- `src/docs/guides/run-in-simulation.md`
