Simulate a Rosie robot
On this page
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. The kits use the same joint names, axes and zero pose as RosieOS's robot descriptions, so joint values carry over unchanged.
Downloads#
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 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#
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.pyFrames and conventions#
- Units: metres, kilograms, radians, seconds. The STEP is in millimetres.
- Joints
J1toJ6connectbase_link,link_1...link_6. Every link frame is parallel tobase_linkat 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.
tool0is 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 fromtool0.base_footprintis the floor or mounting face under J1, and the root of the URDF.base_linkis 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 isbase_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:
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.pyThe snippets below run from inside the kit folder. They are written for the Rosie 1000; for another robot, change 1000 to 600 or 1400.
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)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])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 slidersimport 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_accelerationis each joint's peak-torque acceleration from standstill at the stretched pose with the rated payload, computed fromrobot.jsonitself: (effort_peak− |gravity torque|) / (armature+ the inertia about the joint axis of everything it moves). It is conservative:effort_peakis the torque the gearbox passes to the arm, while the motor accelerates its own rotor ahead of the gearbox with its own torque, up todrive_peak_torque_at_joint.motion.derived_max_accelerationalso gives the values at theworkpose with 1 kg.benchmarks.accelerationlists 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) 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_mgives the points inbase_link. The low points are 50 mm abovebase_linkon 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
tool0inbase_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:
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
effortiseffort_peak, the peak torque the gearbox passes to the arm. MuJoCoforcerangeisdrive_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. URDFvelocityis the maximum joint speed. - MuJoCo actuators are position servos:
ctrlis 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. armatureis 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.