Advanced Metal Research
GitHub Contact AMR

Simulate a Rosie robot

On this page
  1. Downloads
  2. What a kit contains
  3. Frames and conventions
  4. Quickstart
  5. robot.json
  6. Accelerations and motion profiles
  7. Benchmark cycle
  8. IK reference cases
  9. GLB
  10. Simulation parameters
  11. Geometry
  12. Licence

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#

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.
RobotWrist centre at J2 = 90°, J3 = -90°Published reach (wrist / flange)tool0 at homeMass
Rosie 600600.0 mm from the J1 axis600 / 677 mm(0.330, 0, 0.423) m29.3 kg
Rosie 10001,000.0 mm1,000 / 1,077 mm(0.530, 0, 0.623) m38.6 kg
Rosie 14001,433.7 mm1,400 / 1,499 mm(0.834, 0, 0.756) m77.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:

TerminalBash
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_hold.pyPython
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_move.pyPython
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])
Terminal, in a ROS 2 workspaceBash
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
fk.py, numpy onlyPython
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#

KeyContents
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
framesbase_footprint, base_link (height above the floor) and tool0
home, posesJoint 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_referenceWhere the IK reference cases are (ik_cases.json), the solver and the branch flags
motionMotion profiles RosieOS uses, its acceleration and jerk settings, and how derived_max_acceleration is computed
benchmarksThe cycle behind the published cycle times: path, TCP, payload, profile, accelerations, segment times, trajectory files
reach, payload, repeatability_mm, massPublished ratings, and the geometric reach
geometryHow 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) 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.pyPython
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.