// real-robot controller

flexiv_control

A unified, real-time execution & safety layer for Flexiv Rizon arms — one action contract for teleoperation, MPC, reinforcement learning, and real-to-sim-to-real manipulation.

Apache-2.0 Python ≥ 3.8 numpy-only core 70+ tests · CI green
⚠ Community project — NOT affiliated with Flexiv Robotics
rizon · base frame live setpoint workspace box
safety: tabletop_safe rate: 100 Hz track_err: 2.5 mm

why it looks the way it does

The shape the field converged on

A thin Python client over a real-time-capable control loop, one action interface, and sim + real behind a single backend switch — what Deoxys, Polymetis, frankapy, SERL/HIL-SERL, and LeRobot all settled on, specialized here to the Rizon. The decisive fact: the Rizon runs its hard real-time loop inside the robot, so a Python host loop is genuinely reactive.

what you get

One contract. Many consumers. Safe by default.

Every planner, RL trainer, MPC loop, and teleop session speaks the same action contract; every backend consumes the same safety-filtered setpoint stream.

One action contract

CartesianChunk, CartesianDelta, JointChunk, GripperCommand + a quantified ExecutionResult. from_waypoint_array(u) ingests a planner's (H,5) array directly.

🛡

Safety is first-class

A named, version-controlled SafetyProfile + a microsecond per-tick filter: workspace box, speed & jump caps, joint limits, contact-wrench stop, watchdog. It clips or rejects — and reports.

Two real-time tiers

Tier A: a 100–500 Hz Python loop, no root, Standard license. Tier B: an optional C++ 1 kHz daemon. Same contract & wire protocol across both.

Sim ↔ real, one switch

Develop offline on fake, simulate on mujoco (Jacobian IK + a faithful GN01 gripper), deploy on a real Rizon via RDK — your policy code never changes.

Receding-horizon & VLA

A RecedingHorizonRunner drives any policy(obs) → chunk — the seam for an openpi/VLA policy server, an MPC solver, or a sampler. Predict H, execute H_exec, replan.

Multi-user safe

An in-process lease plus a host-wide lock, so a second server or a stray script can't fight over the same arm.

Optional layers

A numpy-only server + RemoteRobot client (no ROS), a Gymnasium env, a LeRobot adapter, SpaceMouse teleop / RL intervention, and a ROS 2 overlay with a MoveIt-Servo jog.

π

numpy-only core

The client installs with nothing but numpy and runs anywhere — an RL trainer, an MPC loop, a notebook. Everything heavier is an opt-in extra.

real-time, two ways

Start on Tier A. Upgrade only if a measurement says so.

The Rizon servos at 1 kHz internally, so the host only streams setpoints. Both tiers share everything above the backend line.

TIER A · DEFAULT

Python control loop

  • loop100–500 Hz host loop, RDK non-real-time modes
  • needsnothing special — no root, no RT kernel, no C++
  • licenseFlexiv RDK Standard
  • forplanner chunks, MPC, reinforcement learning
TIER B · OPTIONAL

C++ 1 kHz daemon

  • looptrue 1 kHz host loop, RDK real-time modes
  • needsPREEMPT_RT kernel, root, rdk::Scheduler
  • licenseFlexiv RDK Professional
  • forhigh-rate streaming MPC, tight contact, torque research
the spine

The contract is the only thing crossing the boundary

Server, Gym env, and ROS overlay are pure pass-through — they serialize and forward these exact objects.

Language policy MPC planner RL trainer SpaceMouse
CartesianChunk · CartesianDelta · JointChunk · GripperCommand
Robot facadeSafetyFilter — per tickInterpolator
FlexivRdkBackendFakeBackendMujocoBackend
Rizon — hard real-time loop runs inside the robot
real2sim2real

A MuJoCo Rizon — with a faithful GN01 gripper

The mujoco backend tracks Cartesian setpoints with a damped-least-squares Jacobian IK under gravity compensation, behind the same action contract as the real arm. A one-command builder attaches the GN01 / Grav parallel-jaw gripper — the real Flexiv meshes, the 4-bar linkage reproduced with MuJoCo equality constraints — to the official menagerie Rizon, so executed poses and grasp geometry match the lab.

flange GN01 · Grav width · 0 – 0.10 m

Build it once

python -m flexiv_control.sim.build_rizon_gn01 \
  --arm <menagerie>/flexiv_rizon4/scene.xml \
  --grav-meshdir <flexiv_description>/meshes/Grav \
  --out rizon4_gn01.xml

Continuous, not binary

A parallel-jaw gripper is continuous width (0–0.10 m), not 0/1. The 4-bar actuator is ctrl = 9.404·w − 0.155; GripperCommand(width=…) maps straight through.

no hardware needed

Running in three lines

The dependency-free fake backend runs the whole stack — contract, safety, interpolation — with no robot.

quickstart.py
from flexiv_control import Robot, RobotConfig, CartesianChunk

robot = Robot(RobotConfig(backend="fake"))   # dependency-free sim
robot.connect()
robot.start_cartesian_impedance()

# a short Cartesian chunk: (x, y, z, gripper, n_frames)
chunk = CartesianChunk.from_waypoint_array([[0.45, 0.0, 0.30, 1.0, 20],
                                            [0.50, 0.0, 0.25, 0.0, 20]])
result = robot.execute_cartesian_chunk(chunk)
print(result.success, result.path_tracking_error)

robot.disconnect()

Install

pip install -e . # core pip install -e ".[rl]" # Gym env pip install -e ".[teleop]" # SpaceMouse pip install -e ".[lerobot]" # LeRobot pip install -e ".[mujoco]" # sim pip install -e ".[flexiv]" # real arm

Or the CLI

$ flexiv-control demo

Offline FakeBackend demo — no hardware.

read more

Documentation

Ten focused guides — architecture, the contract, safety, hardware setup, and one integration walkthrough per use case.