Skip to content

Rizon 4 arm: URDF conversion, factory, and end-to-end validation #1

Description

@siddhss5

Context

Flexiv Rizon 4 is a 7-DOF cobot with non-spherical kinematics — the wrist joints have 13 cm of offset between them, which means EAIK has no analytical decomposition (discovered during personalrobotics/mj_manipulator#127). With mink numerical IK now shipping as the automatic fallback in mj_manipulator v2.0 (personalrobotics/mj_manipulator#136), the Rizon 4 is the first arm that exercises the "EAIK fails, mink takes over" path end-to-end.

Source data: flexivrobotics/flexiv_description (Apache 2.0) — xacro-based URDFs, STL meshes, YAML configs with joint limits and kinematics for the full Rizon family.

Scope: Rizon 4 (P0)

1. URDF → MuJoCo conversion

scripts/convert_urdf.py — loads the Rizon 4 URDF via mujoco.MjSpec.from_file, then:

  • Fixes mesh paths (URDF uses package:// ROS convention → local relative paths)
  • Adds position actuators with per-joint PD gains (URDF has none — joints are just kinematic)
  • Adds a grasp_site on the flange (link7_to_flange offset: [0, 0, 0.081] from their kinematics YAML)
  • Adds a home keyframe
  • Saves as src/flexiv_mj/models/rizon4/rizon4.xml + scene.xml

The meshes directory from flexiv_description is ~50 MB of STL per variant. Open question: redistribute the converted meshes in this repo (simple but bulky), or have the user clone flexiv_description and convert locally (like mj_manipulator does with mujoco_menagerie via find_menagerie())?

Recommendation: start with a find_flexiv_description() resolver + conversion script (no mesh redistribution). Add a setup.sh that clones the upstream repo if absent.

2. Constants from Flexiv datasheets

Already extracted from their YAML configs:

Joint limits (from config/Rizon4/joint_limits.yaml):

RIZON4_JOINT_NAMES = [f"joint{i}" for i in range(1, 8)]

# Position limits (rad)
RIZON4_LOWER = [-2.8798, -2.3562, -3.0543, -1.9548, -3.0543, -1.4835, -3.0543]
RIZON4_UPPER = [ 2.8798,  2.3562,  3.0543,  2.7751,  3.0543,  4.6251,  3.0543]

# Velocity limits (rad/s) — halved for conservative planning
RIZON4_VELOCITY_LIMITS = np.array([2.0944, 2.0944, 2.4435, 2.4435, 4.8869, 4.8869, 4.8869]) * 0.5

# Acceleration limits — not published by Flexiv; derive from v_max / 0.1s
RIZON4_ACCELERATION_LIMITS = np.array([20.9, 20.9, 24.4, 24.4, 48.9, 48.9, 48.9]) * 0.5

Kinematics (from config/Rizon4/default_kinematics.yaml):

joint2: y=+30mm   ← the offsets that make this arm non-spherical
joint3: y=+35mm
joint4: x=-20mm, y=-30mm
joint5: x=-20mm, y=+25mm
joint6: y=+30mm
joint7: x=-15mm, y=+73mm  ← big offset, confirmed non-spherical wrist

These offsets are why EAIK returned None — 73 mm between wrist joints is too far from co-spherical for the exact-equality checks.

3. Arm factory

src/flexiv_mj/rizon4.py:

def create_rizon4_arm(env, *, ee_site="grasp_site", with_ik="auto", ...):
    config = ArmConfig(
        name="rizon4",
        joint_names=RIZON4_JOINT_NAMES,
        kinematic_limits=KinematicLimits(
            velocity=RIZON4_VELOCITY_LIMITS.copy(),
            acceleration=RIZON4_ACCELERATION_LIMITS.copy(),
        ),
        ee_site=ee_site,
    )
    arm = Arm(env, config)
    ik_solver = resolve_ik_solver(arm, with_ik=with_ik)
    # with_ik="auto": EAIK fails (non-spherical) → mink takes over
    return Arm(env, config, ik_solver=ik_solver, ...)

4. Actuator tuning

The URDF provides joint effort limits but no actuator model. We need:

  • PD gains — start with the "reach max velocity in 100 ms" rule of thumb that worked for iiwa14, then tune from scripts/trajectory_tracking_study.py
  • Gravity compensation — add a add_rizon4_gravcomp(spec) helper (same add_subtree_gravcomp pattern)
  • Contact/friction — default MuJoCo values for the collision meshes; tune if grasping is unreliable

5. Validation

  • scripts/validate_rizon4.py — FK-IK round-trip check, confirm mink produces valid plans
  • A simple demo scene (arm + table + can) with robot.pickup() to exercise the full pipeline
  • scripts/benchmark_ik.py extended (or local copy) to include Rizon 4 — the first arm where mink is the only IK option

6. Documentation

  • README with setup instructions (clone flexiv_description, convert, run demo)
  • Constants sourced and cited

Future variants (P1/P2)

Model Priority Notes
Rizon 4s P1 Same kinematics as Rizon 4 + integrated F/T sensor
Rizon 10 P1 Different link dimensions, 10 kg payload
Rizon 10s P2 Rizon 10 + F/T
Rizon 4M/4R P2 Mobile/ruggedized variants

Each variant follows the same pattern: convert URDF, add constants from their YAML, write factory. The kinematics offsets differ per variant so mink handles them all the same way.

License

flexiv_description is Apache 2.0. Our repo is MIT. If we redistribute converted meshes, we need to include Flexiv's copyright notice. If we use the "clone + convert locally" pattern (recommended), no redistribution issue.

Dependencies

  • mj-manipulator >= 2.0.0 (for resolve_ik_solver with mink fallback)
  • flexivrobotics/flexiv_description (cloned locally for meshes/URDFs, not a pip dep)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions