Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
# Each assembly must expand on every backend it supports, not just the
# default. Only the real backend emits the per-bus <ros2_control>
# blocks that the hardware bring-ups depend on.
for assembly in robots/*/xacro/*.urdf.xacro; do
for assembly in lite_description/robots/*/xacro/*.urdf.xacro; do
for backend in \
"" \
"use_mock_hardware:=false" \
Expand Down
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ wheels/

# Onshape export intermediates (transient; the committed urdf/<robot>.urdf hub
# is what the generator reuses instead of re-exporting).
robots/*/cad/assets/
robots/*/cad/robot.pkl

# load_asset() download cache
data/
lite_description/robots/*/cad/assets/
lite_description/robots/*/cad/robot.pkl

# colcon build artifacts (when robot_descriptions is built in a ROS workspace)
/install/
Expand Down
18 changes: 10 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ project(lite_description)

find_package(ament_cmake REQUIRED)

# Lite variants live under robots/<robot>/ (franka_description layout: package at
# the repo root, per-robot assets in a robots/ subdir). Each ships generated,
# installable artifacts (xacro / urdf / mjcf / meshes). The cad/ subdir holds
# generation INPUTS (Onshape config, joint_properties.json, physics.json,
# ros2_control.json, scad) and is intentionally NOT installed.
# Keep this list in sync with the converter's robot list.
# Lite variants live under lite_description/robots/<robot>/, inside the Python
# package that ships them in a wheel. They install to
# share/lite_description/robots/<robot>/, so package:// URLs and $(find ...) do
# not depend on the source layout. Each ships generated, installable artifacts
# (xacro / urdf / mjcf / meshes). The cad/ subdir holds generation INPUTS
# (Onshape config, joint_properties.json, physics.json, ros2_control.json, scad)
# and is intentionally NOT installed.
# The generator's package stage adds each new robot to this list.
set(ROBOTS
lite
lite_bimanual
Expand All @@ -22,9 +24,9 @@ set(ROBOTS
# a model-only robot may have no ros2_control xacro, etc.).
foreach(robot ${ROBOTS})
foreach(sub xacro urdf mjcf meshes)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/robots/${robot}/${sub})
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/lite_description/robots/${robot}/${sub})
install(
DIRECTORY robots/${robot}/${sub}
DIRECTORY lite_description/robots/${robot}/${sub}
DESTINATION share/${PROJECT_NAME}/robots/${robot}
)
endif()
Expand Down
83 changes: 49 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ meshes, all generated from the Onshape CAD.

The same source serves both worlds:

- Simulation and RL, through the `robot_assets` Python loader, for Mujoco Lab and Isaac Lab.
- Simulation and RL, through the `lite_description` Python package, for Mujoco Lab and Isaac Lab.
- ROS 2, through `ros2_control` and `robot_state_publisher`, as the `lite_description`
ament package.

Expand Down Expand Up @@ -53,61 +53,73 @@ path emits several blocks while the mock and MuJoCo paths share one combined blo
## CAD source

Every description is generated from an Onshape assembly (document
`e9ee61a2e2678af2088d9f31`) by the `robot_assets` tool. See
[Re-generating from CAD](#re-generating-from-cad). The files under `robots/<variant>/` are
build artifacts. Do not hand-edit them. Change the `cad/` inputs and regenerate.
`e9ee61a2e2678af2088d9f31`) by the generator in `lite_description.workflow`. See
[Re-generating from CAD](#re-generating-from-cad). The files under
`lite_description/robots/<variant>/` are build artifacts. Do not hand-edit them. Change the
`cad/` inputs and regenerate.

## Usage

### Simulation and RL (Python, no ROS toolchain)

The `lite_description` Python package carries every variant, so a simulator or a training
run reaches the assets without a checkout. The package has no dependencies of its own.

```bash
uv add git+https://github.com/Berkeley-Humanoids/Lite-Description.git
```

```python
from robot_assets import load
import mujoco
from lite_description import VARIANTS, get_mjcf_path, get_urdf_path

urdf_path = load("robots/lite/urdf/lite.urdf") # Isaac Lab
mjcf_path = load("robots/lite_dummy/mjcf/lite_dummy.xml") # MuJoCo
model = mujoco.MjModel.from_xml_path(str(get_mjcf_path("lite_biped"))) # MuJoCo
urdf_path = get_urdf_path("lite") # Isaac Lab
```

`load()` fetches the requested variant's subtree from this GitHub repo and caches it. No
ROS install is required.
Each function returns a path inside the installed package. A name that is not in
`VARIANTS` raises `ValueError`. A URDF or MJCF reaches its meshes by a path relative to its
own directory, so load the file where it lies instead of copying it out alone.

The `uv.lock` of the consuming project records the commit of this repository. The assets
thus change only when that project upgrades `lite-description`. No ROS install is required.

### ROS 2

`lite_description` is a standard `ament_cmake` package whose `package.xml` sits at the
repo root. Build it in a ROS 2 workspace, or pull it with `vcs` or
`humanoid_control.repos` from `Humanoid Control`, then run `colcon build`. Downstream,
`robot_state_publisher` runs xacro on
`robots/<variant>/xacro/<variant>.urdf.xacro`, and
`$(find lite_description)/robots/<variant>/xacro/<variant>.urdf.xacro`, and
`package://lite_description/robots/<variant>/meshes/visual/...` resolves after install.
colcon installs `lite_description/robots/<variant>/` to
`share/lite_description/robots/<variant>/` and leaves out `cad/`.

## Repository layout

```
Lite-Description/ # repo root == ament package "lite_description"
package.xml CMakeLists.txt # ament (colcon); installs robots/<variant>/...
pyproject.toml # pip/uv: builds the robot_assets Python module
robot_assets/ # Python module: load() and the CAD->assets generator
package.xml CMakeLists.txt # ament (colcon); installs lite_description/robots/<variant>/...
pyproject.toml # pip/uv: builds the lite-description wheel
lite_description/ # Python package
__init__.py # ROBOTS_DIR, VARIANTS, get_urdf_path(), get_mjcf_path()
actuators/ # actuator spec tables (velocity/effort/armature)
workflow/ # the generator stages
robots/ # per-variant assets (franka_description-style subdir)
<variant>/
xacro/ # ROS entry (GENERATED)
<variant>.urdf.xacro # assembly: args, includes, instantiation
<variant>.description.xacro # model macro: kinematics, ${mesh_root}, base_link
<variant>.ros2_control.xacro # hardware macros: joints, groups, backends
urdf/<variant>.urdf # flat URDF (GENERATED; the kinematic HUB)
mjcf/<variant>.xml # MJCF (GENERATED; MuJoCo training + deployment sim)
meshes/visual/*.stl # one shared mesh copy
cad/ # generation INPUTS (not installed):
config.json # Onshape document + export options
joint_properties.json # sim tuning: armature / friction / effort_limit
physics.json # MJCF <option>, freejoint, IMU, contact (optional)
ros2_control.json # ROS hardware map (optional)
scad/ # collider sources
workflow/ # the generator stages (needs the `cad` extra)
robots/ # per-variant assets, inside the package so a wheel carries them
<variant>/
xacro/ # ROS entry (GENERATED)
<variant>.urdf.xacro # assembly: args, includes, instantiation
<variant>.description.xacro # model macro: kinematics, ${mesh_root}, base_link
<variant>.ros2_control.xacro # hardware macros: joints, groups, backends
urdf/<variant>.urdf # flat URDF (GENERATED; the kinematic HUB)
mjcf/<variant>.xml # MJCF (GENERATED; MuJoCo training + deployment sim)
meshes/visual/*.stl # one shared mesh copy
cad/ # generation INPUTS (not in the wheel, not installed):
config.json # Onshape document + export options
joint_properties.json # sim tuning: armature / friction / effort_limit
physics.json # MJCF <option>, freejoint, IMU, contact (optional)
ros2_control.json # ROS hardware map (optional)
scad/ # collider sources
```

The committed `urdf/<variant>.urdf` is the single kinematic hub. The `mjcf` and `xacro`
Expand All @@ -123,17 +135,20 @@ uv sync --extra cad
sudo apt install openscad # for collider editing (onshape-to-robot)
```

The generator reads `cad/`, which only a checkout has, so run it from a checkout. In an
installed package, it stops with an error instead of writing into `site-packages`.

One command produces all three formats from a variant's `cad/` inputs:

```bash
# Full pipeline. The Onshape stage is skipped whenever the URDF hub is committed.
uv run robot-assets-generate lite_dummy
uv run lite-description-generate lite_dummy

# Re-emit only some stages, after editing physics.json or ros2_control.json:
uv run robot-assets-generate lite_dummy --only mjcf,xacro
uv run lite-description-generate lite_dummy --only mjcf,xacro

# Re-run the Onshape export even though the hub is committed:
uv run robot-assets-generate lite_dummy --force
uv run lite-description-generate lite_dummy --force
```

| Stage | Reads | Writes |
Expand All @@ -156,8 +171,8 @@ that is not backed by a `<site>`.
### Editing colliders (OpenSCAD)

```bash
uv run robot-assets-onshape-to-urdf lite_dummy --keep-assets
cd robots/lite_dummy/cad/assets/
uv run lite-description-onshape-to-urdf lite_dummy --keep-assets
cd lite_description/robots/lite_dummy/cad/assets/
uv run onshape-to-robot-edit-shape ./chest.stl
```

Expand Down
28 changes: 28 additions & 0 deletions lite_description/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Paths to the generated Lite robot descriptions.

Each path names a file inside the installed package. A URDF or MJCF reaches its meshes by a
path relative to its own directory, so open the file where it lies instead of copying it out.
"""

from pathlib import Path

ROBOTS_DIR = Path(__file__).parent / "robots"
VARIANTS = tuple(sorted(path.name for path in ROBOTS_DIR.iterdir() if path.is_dir()))

__all__ = ["ROBOTS_DIR", "VARIANTS", "get_mjcf_path", "get_urdf_path"]


def get_urdf_path(variant: str) -> Path:
"""Return the flat URDF of ``variant``, for Isaac Lab and other URDF consumers."""
return _variant_dir(variant) / "urdf" / f"{variant}.urdf"


def get_mjcf_path(variant: str) -> Path:
"""Return the MJCF of ``variant``, the MuJoCo training and deployment model."""
return _variant_dir(variant) / "mjcf" / f"{variant}.xml"


def _variant_dir(variant: str) -> Path:
if variant not in VARIANTS:
raise ValueError(f"Unknown variant {variant!r}. Expected one of {VARIANTS}.")
return ROBOTS_DIR / variant
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Compute actuator parameters from a second-order model of the joint.

The equations come from BeyondMimic: From Motion Tracking to Versatile Humanoid Control
via Guided Diffusion (https://arxiv.org/abs/2508.08241). Each function takes one of the
actuator spec tables in this package, such as ``ROBSTRIDE_06_ACTUATOR_PARAMS``.
via Guided Diffusion (https://arxiv.org/abs/2508.08241). Each input is a field of an
actuator spec table in this package, such as ``ROBSTRIDE_06_ACTUATOR_PARAMS["armature"]``.
"""

from math import pi
Expand All @@ -12,35 +12,36 @@


def compute_stiffness(
actuator_params: dict[str, float],
armature: float,
natural_frequency: float = NATURAL_FREQUENCY,
) -> float:
"""Return the joint stiffness in Nm/rad.

Args:
actuator_params: An actuator spec table. Reads ``armature`` in kg m^2.
armature: Armature in kg·m^2.
natural_frequency: Closed-loop natural frequency in rad/s.
"""
return actuator_params["armature"] * natural_frequency**2
return armature * natural_frequency**2


def compute_damping(
actuator_params: dict[str, float],
armature: float,
natural_frequency: float = NATURAL_FREQUENCY,
damping_ratio: float = 2.0,
) -> float:
"""Return the joint damping in Nm s/rad.
"""Return the joint damping in Nm·s/rad.

Args:
actuator_params: An actuator spec table. Reads ``armature`` in kg m^2.
armature: Armature in kg·m^2.
natural_frequency: Closed-loop natural frequency in rad/s.
damping_ratio: Damping ratio. Above 1.0 is overdamped.
"""
return 2.0 * damping_ratio * actuator_params["armature"] * natural_frequency
return 2.0 * damping_ratio * armature * natural_frequency


def compute_action_scale(
actuator_params: dict[str, float],
armature: float,
effort_limit: float,
natural_frequency: float = NATURAL_FREQUENCY,
action_scale_coefficient: float = 0.25,
) -> float:
Expand All @@ -50,9 +51,10 @@ def compute_action_scale(
``action_scale_coefficient`` of the actuator's effort limit.

Args:
actuator_params: An actuator spec table. Reads ``armature`` and ``effort_limit``.
armature: Armature in kg·m^2.
effort_limit: Effort limit in Nm.
natural_frequency: Closed-loop natural frequency in rad/s.
action_scale_coefficient: Fraction of the effort limit a unit action commands.
"""
stiffness = compute_stiffness(actuator_params, natural_frequency)
return action_scale_coefficient * actuator_params["effort_limit"] / stiffness
stiffness = compute_stiffness(armature, natural_frequency)
return action_scale_coefficient * effort_limit / stiffness
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<mujoco model="lite">
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite -->
<compiler angle="radian" meshdir="../meshes/visual/" />
<asset>
<mesh name="pelvis_visual" content_type="model/stl" file="pelvis_visual.stl" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="lite">
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite -->
<link name="pelvis">
<inertial>
<origin xyz="0.0135183 5.78646e-07 0.0691358" rpy="0 0 0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="lite_description" params="mesh_root:=package://lite_description/robots/lite/meshes/visual">
<link name="base_link" />
Expand All @@ -8,7 +8,7 @@
<child link="pelvis" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite -->
<link name="pelvis">
<inertial>
<origin xyz="0.0135183 5.78646e-07 0.0691358" rpy="0 0 0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="lite">

<xacro:include filename="$(find lite_description)/robots/lite/xacro/lite.description.xacro"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<mujoco model="lite_bimanual">
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<compiler angle="radian" meshdir="../meshes/visual/" />
<option timestep="0.001" integrator="implicitfast" solver="Newton" cone="elliptic" impratio="10" />
<asset>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<robot name="lite_bimanual">
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<link name="chest">
<inertial>
<origin xyz="0.0223994 -6.1147e-08 0.218844" rpy="0 0 0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:macro name="lite_bimanual_description" params="mesh_root:=package://lite_description/robots/lite_bimanual/meshes/visual">
<link name="base_link" />
Expand All @@ -8,7 +8,7 @@
<child link="chest" />
<origin xyz="0 0 0" rpy="0 0 0" />
</joint>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<link name="chest">
<inertial>
<origin xyz="0.0223994 -6.1147e-08 0.218844" rpy="0 0 0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro">

<!-- One joint: 5 command + 3 state interfaces (MIT mode).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_bimanual -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_bimanual -->
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="lite_bimanual">
<xacro:arg name="use_mock_hardware" default="true"/>
<xacro:arg name="sim_mujoco" default="false"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<mujoco model="lite_biped">
<!-- GENERATED by robot_assets from Onshape CAD. Do not edit by hand. Regenerate: robot-assets-generate lite_biped -->
<!-- GENERATED by lite_description.workflow from Onshape CAD. Do not edit by hand. Regenerate: lite-description-generate lite_biped -->
<compiler angle="radian" meshdir="../meshes/visual/" />
<option timestep="0.005" integrator="implicitfast" solver="Newton" cone="pyramidal" impratio="1" iterations="10" ls_iterations="20" />
<asset>
Expand Down
Loading
Loading