Sim-to-real robotics workflow with Sim2Bot¶
Sim-to-real is not a button that turns a simulated robot into a physical one. It is a controlled transfer process: keep the task logic stable, replace the simulator connection with a hardware-specific adapter, calibrate every assumption, and validate the real machine in progressively safer stages.
Sim2Bot is useful at the simulation side of that boundary. It can exercise joint and TCP commands, multi-robot addressing, cameras, telemetry, and scene interactions before hardware is involved. It is not a hardware driver, a hard-real-time controller, a collision-free planner, or a functional-safety system.
A simulated stop is not an emergency stop
Robot.stop() asks the browser simulation to hold its current joint pose.
It is not a safety-rated stop and must never replace the physical robot's
emergency-stop circuit, safeguards, safety controller, or application risk
assessment.
Keep three responsibilities separate¶
Task / controller logic
│
│ named joints, SI units, targets, state, faults
▼
Endpoint adapter
├── Sim2Bot adapter ── local bridge ── browser MuJoCo simulation
└── Robot adapter ── vendor or ROS driver ── physical controller
Independent machine-safety system
└── E-stop, safeguards, safe limits, interlocks, risk controls
The controller decides what motion is requested. An endpoint adapter owns the translation between that request and one specific target. The machine's safety system independently decides whether physical motion is allowed.
This separation prevents browser ports, vendor packet formats, encoder offsets, or unit conversions from leaking into task logic. It also makes a hardware transfer reviewable: changing endpoint should not silently change the meaning of a command.
The pattern is similar to the separation between controllers, state interfaces, command interfaces, and hardware components in ros2_control. Sim2Bot does not require ROS 2; this is an architectural comparison, not a claim that the two interfaces are interchangeable.
Define the command contract first¶
Write down the interface your controller expects before creating a hardware adapter.
| Contract item | Sim2Bot convention | Hardware adapter responsibility |
|---|---|---|
| Robot identity | Discover with describe(); address commands with RobotInfo.index |
Select one configured physical robot and reject an identity mismatch |
| Joint identity | RobotInfo.joint_names in command order |
Map by verified joint name, not by an assumed list position |
| Joint position | Radians | Convert only if the vendor API uses another unit |
| Joint velocity | Radians per second | Enforce the hardware's rated and configured limits |
| Cartesian position | World-frame metres | Transform from the controller frame into the robot's calibrated base frame |
| Cartesian orientation | World-frame quaternion [x, y, z, w] |
Convert frame and quaternion convention explicitly |
| TCP | Authored model TCP site | Use the measured flange-to-tool transform for the installed end effector |
| State | Latest simulated telemetry packet | Return measured encoder/sensor state and a trustworthy timestamp |
| Stop/fault | Simulation hold command and connection errors | Use the vendor's operational stop/fault path; safety stops remain independent |
| Timing | Browser-frame scheduled, latest-value commands | Meet the physical controller's documented cycle and deadline requirements |
Reject an incompatible robot instead of guessing. A robust adapter checks joint names, count, units, limits, available command mode, and TCP configuration before it enables motion.
Run a measured motion in Sim2Bot¶
The following program discovers the first simulated robot, returns it to its authored home pose, chooses a small in-limit joint movement, verifies the result, and returns home. It uses the actual Sim2Bot SDK contract rather than hard-coded joint ordering.
Before running it, sign in, open a robot scene, connect Tools → Bridge, and keep the Sim2Bot tab visible.
from math import degrees
from sim2bot import Robot
STEP_RAD = 0.10
LIMIT_MARGIN_RAD = 0.02
POSITION_TOLERANCE_RAD = 0.02
def make_small_target(home, limits):
"""Return (joint_index, target) for one small, in-limit movement."""
for joint_index, start in enumerate(home):
limit = limits[joint_index] if joint_index < len(limits) else None
for direction in (1.0, -1.0):
candidate = start + direction * STEP_RAD
if limit is None:
target = list(home)
target[joint_index] = candidate
return joint_index, target
lower, upper = limit
if lower + LIMIT_MARGIN_RAD <= candidate <= upper - LIMIT_MARGIN_RAD:
target = list(home)
target[joint_index] = candidate
return joint_index, target
raise RuntimeError("No joint has enough range for the test movement")
def main() -> None:
with Robot(auto_bridge=True, wait_for_sim=True) as sim:
arm = sim.describe()[0]
if len(arm.home) != arm.dof:
raise RuntimeError("The model does not expose a complete home pose")
if len(arm.joint_names) != arm.dof:
raise RuntimeError("The model does not expose complete joint names")
joint_index, target = make_small_target(arm.home, arm.joint_limits)
joint_name = arm.joint_names[joint_index]
delta = target[joint_index] - arm.home[joint_index]
print(f"Robot: {arm.name}")
print(f"Test joint: {joint_name}")
print(f"Requested step: {degrees(delta):.2f} deg")
sim.move_to(arm.home, robot=arm.index)
if not sim.wait_until_reached(
arm.home,
robot=arm.index,
tol=POSITION_TOLERANCE_RAD,
timeout=10.0,
):
raise RuntimeError("Robot did not reach home")
try:
sim.move_to(target, robot=arm.index)
if not sim.wait_until_reached(
target,
robot=arm.index,
tol=POSITION_TOLERANCE_RAD,
timeout=10.0,
):
raise RuntimeError("Robot did not reach the test target")
state = sim.state(robot=arm.index)
if state is None or len(state.q) != arm.dof:
raise RuntimeError("No telemetry received")
error = target[joint_index] - state.q[joint_index]
print(f"Measured error: {degrees(error):.3f} deg")
print(f"Measured TCP [m]: {state.tcp}")
finally:
sim.move_to(arm.home, robot=arm.index)
sim.wait_until_reached(
arm.home,
robot=arm.index,
tol=POSITION_TOLERANCE_RAD,
timeout=10.0,
)
sim.stop(robot=arm.index)
if __name__ == "__main__":
main()
Save the output with the model package version and Sim2Bot build. Repeat the same test for every joint, both signs, relevant TCP directions, and every tool variant. The useful result is a comparison record—not merely seeing the robot move.
This example intentionally controls simulation only
Do not change its WebSocket address to a physical robot and assume that the protocol, limits, timing, or stop behavior match. Implement and review a dedicated adapter for the real controller.
Calibrate the transfer boundary¶
Create a versioned calibration record for the exact arm, controller, end effector, and sensor set used in the experiment.
Joints¶
- Match joints by stable names and record their command order.
- Verify zero offsets and positive rotation directions one joint at a time.
- Compare software, model, and hardware position/velocity limits.
- Record coupled, mimic, gripper, and non-arm axes separately.
- Check the measured state against the commanded state; never substitute the last command for a real encoder reading.
A DH frame is a mathematical kinematic convention. A URDF or MJCF joint axis is the axis that actually defines positive model motion. They can be represented differently while describing the same mechanism. Do not reverse a controller command merely because a displayed analysis frame looks different—verify the model-to-hardware joint mapping with a small positive motion.
Base, flange, and TCP¶
- Measure the world-to-robot-base transform instead of copying a viewport pose.
- Keep the flange frame distinct from the active tool/TCP frame.
- Recalibrate the flange-to-TCP transform whenever the gripper, fingers, camera, or tool changes.
- Verify Cartesian translation and rotation in every supported reference frame.
- Record quaternion ordering and handedness at both sides of the adapter.
Tool, payload, and contact¶
- Configure the real tool mass, centre of gravity, inertia, and payload.
- Validate gripper direction, range, force, and object-retention behavior.
- Treat friction, cable forces, backlash, compliance, and contact geometry as model assumptions that need physical measurement.
Cameras and sensors¶
- Calibrate camera intrinsics and the camera-to-tool/base extrinsic transform.
- Match image resolution, orientation, colour format, timestamps, and latency.
- Validate sensor noise, bias, saturation, dropouts, and coordinate frames.
- Do not assume a rendered image proves real perception performance.
Transfer in stages¶
Each stage should have a written pass condition. A failure returns the process to the previous stage.
| Stage | What to do | Example pass condition |
|---|---|---|
| 1. Static review | Compare names, limits, units, transforms, payload, and command modes without enabling motion | Every mapping is explicit; no fallback by list position |
| 2. Simulation | Run nominal, boundary, disconnect, stale-command, and recovery cases in Sim2Bot | Targets remain in limits; faults stop the test; measured error is recorded |
| 3. Hardware state only | Connect the real adapter with drives disabled or motion inhibited | Identity and measured joint state match the calibration record |
| 4. Reduced manual test | Clear the workspace and exercise one small joint movement under the manufacturer's approved reduced/test mode | Direction, scale, stop behavior, and feedback match expectations |
| 5. Constrained task | Add the real tool and payload; use conservative speed, acceleration, workspace, and force limits | Joint/TCP traces stay within the accepted envelope |
| 6. Application validation | Validate normal operation, foreseeable faults, people, tooling, workpieces, guarding, and recovery | The integrator's documented acceptance and risk controls are complete |
Simulation tests remain useful after hardware deployment. Keep a regression scene for every released adapter and replay representative controller inputs before changing the real system.
Timing differences to expect¶
Sim2Bot's physics timestep may be 200–500 Hz, but command application and normal telemetry delivery are scheduled by the visible browser frame loop. A hidden or covered tab can be heavily throttled. Sending commands faster does not create a hard-real-time servo; newer commands may replace older ones before a simulation tick.
A physical adapter must follow the real controller's documented update rate, watchdog, buffering, interpolation, timeout, and fault semantics. Measure end-to-end command age and state age on both targets. Compare trajectories using timestamps rather than assuming equal loop iterations.
See Bridge and networking and Advanced networking and performance for Sim2Bot's current scheduling limits.
What a successful transfer does—and does not—prove¶
A good Sim2Bot result can show that:
- controller logic addresses the intended robot and joints;
- commands use the expected units, order, limits, and reference frame;
- nominal trajectories and selected failure cases behave as designed;
- telemetry and perception plumbing provide the expected data shape.
It does not prove that:
- a path is collision-free on the physical installation;
- the robot, tool, workpiece, guards, or application are safe;
- simulated contact, friction, backlash, compliance, or sensors match reality;
- browser timing satisfies a hardware controller's real-time requirements;
- a software stop is a safety-rated stop;
- the completed application complies with laws or standards.
For an industrial robot, identify the standards and regulations that apply to the complete application and jurisdiction. The current ISO 10218-1:2025 page distinguishes robot requirements from system integration, while ISO 10218-2:2025 covers industrial robot applications and cells. Use the complete applicable standards, the robot manufacturer's current manuals, and a qualified integrator or safety professional—not this guide—as the authority for a physical deployment.
As one manufacturer-specific example, Universal Robots documents that the application risk assessment determines emergency-stop needs. Use the corresponding manual for the actual robot being integrated.
Transfer checklist¶
- Controller behavior is separated from endpoint-specific transport.
- Robot identity, joint names, command order, units, signs, and limits match.
- Base, flange, TCP, tool, payload, and sensor calibrations are versioned.
- The adapter returns measured state with trustworthy timestamps and faults.
- Nominal and failure-path tests pass in a visible Sim2Bot session.
- Hardware tests start state-only, then use small motions and reduced limits.
- Physical safeguards and emergency stops are independent of Sim2Bot and the task controller.
- The complete application risk assessment and required validation are done.
Continue with the Python SDK guide, robot motion examples, and uploading your own robot.