URLab Bridge
The URLab Bridge is a separate companion repository (urlab_bridge) in the same GitHub organization. It provides Python-side middleware between external systems and URLab's MuJoCo simulation, communicating over ZMQ for RL policy deployment, remote teleoperation, data logging, sensor monitoring, and custom control pipelines.
What It Does
- Bidirectional ZMQ communication: receives joint state, sensor data, base state, and camera images from Unreal; sends actuator commands and PD gains back
- Remote control: drive robots from Python scripts, Jupyter notebooks, ROS 2 nodes, or any ZMQ-capable system
- Policy deployment: run pretrained RL policies (locomotion, motion imitation, quadruped gaits) against the Unreal simulation
- Sensor monitoring: real-time dashboard (DearPyGui) for visualizing joint states, sensor readouts, and camera feeds
- Handles articulation discovery, joint mapping, and actuator ID resolution automatically
- Provides forward kinematics for body position computation
- Prefix-based filtering for multi-articulation scenes
- Supports both position targets (for position actuators) and torque targets (for motor + PD)
Architecture
┌─────────────────────────────────┐ ZMQ ┌──────────────────┐
│ Python (URLab Bridge) │◄────────────►│ Unreal (URLab) │
│ │ │ │
│ Your Control System │ joint state │ MuJoCo physics │
│ ├─ RL Policy (RoboJuDo) │◄─────────────│ ZMQ Broadcaster │
│ ├─ Custom Controller │ │ │
│ └─ Teleoperation │ ctrl targets│ ZMQ Subscriber │
│ │─────────────►│ → d->ctrl │
└─────────────────────────────────┘ └──────────────────┘
Setup
cd Plugins/UnrealRoboticsLab/urlab_bridge
uv python install 3.11
uv venv --python 3.11 .venv
uv pip install numpy pyzmq
For RL policy support (RoboJuDo):
uv pip install -e RoboJuDo
uv pip install dearpygui opencv-python
cd RoboJuDo && git submodule update --init --recursive
ZMQ Protocol
For ZMQ protocol details, topic formats, and message structures, see ZMQ Networking.
Using the Bridge Directly
For custom controllers without RoboJuDo:
from urlab_policy.unreal_env import ZmqLink
import struct, time, numpy as np
zmq = ZmqLink("tcp://127.0.0.1:5555", "tcp://127.0.0.1:5556")
time.sleep(1) # Wait for connection
# Read state
messages = zmq.drain()
for topic, payload in messages.items():
if "/joint/" in topic and len(payload) == 16:
jid, pos, vel, acc = struct.unpack("<Ifff", payload)
print(f"Joint {jid}: pos={pos:.3f}")
# Send control (12 actuators, all to zero)
targets = np.zeros(12)
zmq.send_control("my_robot_prefix", targets)
zmq.close()
RoboJuDo Integration
For running pretrained RL policies, the bridge wraps RoboJuDo as the policy runtime.
GUI:
.venv\Scripts\activate
$env:PYTHONPATH = "src"
python src\urlab_policy\policy_gui.py
CLI:
python src/run_policy.py --policy unitree --prefix g1
Available policies are registered in policy_registry.py. Each entry defines the policy config class, environment config, DOF count, and controller type.
Go2 Walk-These-Ways Policy
12-DOF quadruped policy with gait conditioning. Supports preset gaits: Trot, Pronk, Bound, and Pace. Requires a checkpoint download (see RoboJuDo/checkpoints/ README for links).
Motor vs Position Actuators
| Approach | MJCF Actuator | Control Signal | PD Location |
|---|---|---|---|
| Position | <position kp="100" kv="5"> |
Position target (rad) | MuJoCo internal |
| Motor + PD | <motor forcerange="-88 88"> |
Position target (rad) | C++ UMjPDController |
Position actuators are simpler and more stable. Motor + PD matches training dynamics exactly but requires gain configuration. Use the GUI checkbox to toggle.
Configuration
Key settings in env_config.py:
| Field | Default | Must Match |
|---|---|---|
sim_dt |
0.002 | Unreal manager timestep |
sim_decimation |
10 | Policy frequency = 1/(sim_dt * decimation) |
state_endpoint |
tcp://127.0.0.1:5555 | ZmqSensorBroadcaster endpoint (PUB) |
control_endpoint |
tcp://127.0.0.1:5556 | ZmqControlSubscriber endpoint (SUB) |
All Unreal-side sockets bind; the Python bridge connects. See ZMQ Networking for topic formats and port assignments.
Force Twist Override
The policy GUI provides a force twist override that lets you send manual velocity commands (vx, vy, yaw_rate) directly, bypassing keyboard possession input. Useful for scripted motion or testing without possessing the articulation in Unreal.
Multi-Articulation Scenes
ZMQ topics use prefix-based filtering (<prefix>/joint/..., <prefix>/control, etc.). In scenes with multiple articulations, each one publishes and subscribes under its own prefix. The Python side filters incoming messages by prefix to route state to the correct policy instance.
Mesh Preparation
Use Scripts/clean_meshes.py to convert meshes to GLB and resolve filename conflicts before import.
Debugging Tools
| Script | Purpose |
|---|---|
zmq_visualizer_gui.py |
Standalone ZMQ state viewer with sliders |
policy_gui.py |
Full policy runner with visualization |
test_native_mujoco.py |
Run policy in native MuJoCo viewer (ground truth) |
test_compare_targets.py |
Log targets for native vs Unreal comparison |