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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ All notable changes to this project will be documented in this file.
- Description: AgileX PiPER (URDF)
- Description: Robot Soccer Kit
- Description: Robotiq 2F-85 (MJCF V4) (thanks to @peterdavidfagan)
- CLI: Add `show_in_meshcat` command
- CLI: Add `show_in_mujoco` command
- CLI: Add `show_in_pybullet` command

### Changed

- Enable command-line to run from `python -m robot_descriptions`
- CLI: Rename `show` command to `show_in_yourdfpy`

### Fixed

Expand Down
16 changes: 8 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ The goal of this project is to facilitate loading and sharing of robot descripti

- [Add a new robot description](#adding-a-new-robot-description)
- Raise any issue you find in a description, preferably directly in the description's repository (check out [`_repositories.py`](https://github.com/stephane-caron/robot_descriptions.py/blob/main/robot_descriptions/_repositories.py))
- Make a standalone ``<robot_name>_description`` repository for a description embedded in one of the big framework repositories (Bullet, Drake, ...)
- Make a standalone `<robot_name>_description` repository for a description embedded in one of the big framework repositories (Bullet, Drake, ...)

## Adding a new robot description

1. **License:** The robot description is distributed legally and under an open source license (permissive or copyleft).
2. **Repository:** If needed, add the repository containing the new description to ``_repositories.py``.
2. **Repository:** If needed, add the repository containing the new description to `_repositories.py`.
- Use a specific git commit ID. This way the robot description will still work in the interval between a change in the file structure of the target repository and the corresponding update in `robot_descriptions`.
3. **Submodule:** add a Python file for the robot descriptions to ``robot_descriptions/``.
- The file name for the new submodule is ``<robot_name>_description.py`` in snake-case.
- For example, the file name for the Kinova (maker) Gen2 (robot name) is ``gen2_description.py``.
- Use the ``mj_description`` suffix for an MJCF description.
4. **Listing:** Add the description metadata to the ``DESCRIPTIONS`` dictionary in ``_descriptions.py``.
3. **Submodule:** add a Python file for the robot descriptions to `robot_descriptions/`.
- The file name for the new submodule is `<robot_name>_description.py` in snake-case.
- For example, the file name for the Kinova (maker) Gen2 (robot name) is `gen2_description.py`.
- Use the `mj_description` suffix for an MJCF description.
4. **Listing:** Add the description metadata to the `DESCRIPTIONS` dictionary in `_descriptions.py`.
5. **README:** Document the description's submodule name in the Descriptions section of the [README](README.md).
- Use an [SPDX License Identifier](https://spdx.org/licenses/) in the License column.
6. **CHANGELOG:** Write down the new model at the top of the [changelog](CHANGELOG.md).
7. **Testing:** Check that all unit tests are successful by ``tox``.
7. **Testing:** Check that all unit tests are successful by `tox`.
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,19 @@ Loaders are implemented for the following robotics software:

Loading will automatically download the robot description if needed, and cache it to a local directory.

### Show a description

You can display a robot description directly from the command line:

```console
python -m robot_descriptions show_in_meshcat go2_description
```

A `robot_descriptions` alias for `python -m robot_descriptions` is also available.

### Import as submodule

You can also import a robot description directly as a submodule of ``robot_descriptions``:
You can also import a robot description directly as a submodule of `robot_descriptions`:

```python
from robot_descriptions import my_robot_description
Expand Down Expand Up @@ -83,7 +93,7 @@ The import will automatically download the robot description if you don't have i
</dd>
</dl>

Some robot descriptions include additional fields. For instance, the ``iiwa14_description`` exports ``URDF_PATH_POLYTOPE_COLLISION`` with more detailed collision meshes.
Some robot descriptions include additional fields. For instance, the `iiwa14_description` exports `URDF_PATH_POLYTOPE_COLLISION` with more detailed collision meshes.

## Examples

Expand All @@ -104,14 +114,6 @@ Visualizing a robot description:
- [RoboMeshCat](https://github.com/robot-descriptions/robot_descriptions.py/tree/main/examples/show_in_robomeshcat.py)
- [yourdfpy](https://github.com/robot-descriptions/robot_descriptions.py/tree/main/examples/show_in_yourdfpy.py)

## Command line tool

The command line tool can be used to visualize any of the robot descriptions below. For example:

```console
robot_descriptions show solo_description
```

## Descriptions

Available robot descriptions ([gallery](https://github.com/robot-descriptions/awesome-robot-descriptions#gallery)) are listed in the following categories:
Expand Down
7 changes: 4 additions & 3 deletions examples/show_in_meshcat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
"""
Show a robot descriptions, specified from the command line, using MeshCat.

This example requires Pinocchio, installed by e.g. `conda install pinocchio`,
and MeshCat, which is installed by `pip install meshcat`.
This example is equivalent to `python -m robot_descriptions show_in_meshcat`.
It requires Pinocchio, installed by e.g. `conda install pinocchio`, and
MeshCat, installed by e.g. `conda install meshcat-python`.
"""

import argparse

try:
from pinocchio.visualize import MeshcatVisualizer
except ImportError as e:
raise ImportError("Pinocchio not found, try ``pip install pin``") from e
raise ImportError("Pinocchio not found, try `pip install pin`") from e

from robot_descriptions.loaders.pinocchio import load_robot_description

Expand Down
5 changes: 3 additions & 2 deletions examples/show_in_mujoco.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"""
Show a robot description, specified from the command line, using MuJoCo.

This example requires MuJoCo, which is installed by `pip install mujoco`, and
the MuJoCo viewer installed by `pip install mujoco-python-viewer`.
This example is equivalent to `python -m robot_descriptions show_in_mujoco`. It
requires MuJoCo, which is installed by `pip install mujoco`, and the MuJoCo
viewer installed by `pip install mujoco-python-viewer`.
"""

import argparse
Expand Down
3 changes: 2 additions & 1 deletion examples/show_in_pybullet.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"""
Show a robot description, specified from the command line, using PyBullet.

This example requires PyBullet, which is installed by ``pip install pybullet``.
This example is equivalent to `python -m robot_descriptions show_in_pybullet`.
It requires PyBullet, which can be installed by `pip install pybullet`.
"""

import argparse
Expand Down
11 changes: 3 additions & 8 deletions examples/show_in_yourdfpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@
"""
Show a robot description, specified from the command line, using yourdfpy.

Note:
See ``robot_descriptions/_command_line.py`` for a more advanced
implementation, including the ability to set the robot configuration or
show collision meshes.

This example requires `yourdfpy` which is an optional dependency. It can be
installed separately (``pip install yourdfpy``), or when robot descriptions are
installed with optional dependencies ``pip install robot_descriptions[opts]``.
This example is equivalent to `python -m robot_descriptions show_in_yourdfpy`.
It requires `yourdfpy`, an optional dependency that can be installed by `pip
install yourdfpy`.
"""

import argparse
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ opts = [
]

[project.scripts]
robot_descriptions = "robot_descriptions._command_line:main"
robot_descriptions = "robot_descriptions.__main__:main"

[project.urls]
Homepage = "https://github.com/robot-descriptions"
Expand Down
147 changes: 134 additions & 13 deletions robot_descriptions/_command_line.py → robot_descriptions/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,53 @@ def get_argument_parser() -> argparse.ArgumentParser:
help="list all available robot descriptions",
)

# show --------------------------------------------------------------------
parser_show = subparsers.add_parser(
"show",
help="load and display a given robot description",
# show_in_meshcat --------------------------------------------------------
parser_meshcat = subparsers.add_parser(
"show_in_meshcat",
help="load and display a given robot description in Meshcat",
)
parser_show.add_argument(
parser_meshcat.add_argument(
"name",
help="name of the robot description",
)
parser_show.add_argument(

# show_in_mujoco --------------------------------------------------------
parser_mujoco = subparsers.add_parser(
"show_in_mujoco",
help="load and display a given robot description in mujoco",
)
parser_mujoco.add_argument(
"name",
help="name of the robot description",
)

# show_in_pybullet --------------------------------------------------------
parser_pybullet = subparsers.add_parser(
"show_in_pybullet",
help="load and display a given robot description in pybullet",
)
parser_pybullet.add_argument(
"name",
help="name of the robot description",
)

# show_in_yourdfpy --------------------------------------------------------
parser_yourdfpy = subparsers.add_parser(
"show_in_yourdfpy",
help="load and display a given robot description with yourdfpy",
)
parser_yourdfpy.add_argument(
"name",
help="name of the robot description",
)
parser_yourdfpy.add_argument(
"-c",
"--configuration",
nargs="+",
type=float,
help="configuration of the visualized robot description",
)
parser_show.add_argument(
parser_yourdfpy.add_argument(
"--collision",
action="store_true",
help="use collision geometry for the visualized robot description",
Expand Down Expand Up @@ -96,7 +126,88 @@ def list_descriptions():
print(f"- {name} [{formats}]")


def show(
def show_in_meshcat(name: str) -> None:
"""Show a robot description in MeshCat.

Args:
name: Name of the robot description.
"""
try:
from pinocchio.visualize import MeshcatVisualizer
except ImportError as e:
raise ImportError(
"Pinocchio not found, try for instance `conda install pinocchio`"
) from e

from robot_descriptions.loaders.pinocchio import load_robot_description

try:
robot = load_robot_description(name)
except ModuleNotFoundError:
robot = load_robot_description(f"{name}_description")

robot.setVisualizer(MeshcatVisualizer())
robot.initViewer(open=True)
robot.loadViewerModel()
robot.display(robot.q0)

input("Press Enter to close MeshCat and terminate... ")


def show_in_mujoco(name: str) -> None:
"""Show a robot description in MuJoCo.

Args:
name: Name of the robot description.
"""
import mujoco

try:
import mujoco_viewer
except ImportError as e:
raise ImportError(
"MuJoCo viewer not found, try `pip install mujoco-python-viewer`"
) from e

from robot_descriptions.loaders.mujoco import load_robot_description

try:
model = load_robot_description(name)
except ModuleNotFoundError:
model = load_robot_description(f"{name}_mj_description")

data = mujoco.MjData(model)
viewer = mujoco_viewer.MujocoViewer(model, data)
mujoco.mj_step(model, data) # step at least once to load model in viewer
while viewer.is_alive:
viewer.render()
viewer.close()


def show_in_pybullet(name: str) -> None:
"""Show a robot description in PyBullet.

Args:
name: Name of the robot description.
"""
import pybullet

from robot_descriptions.loaders.pybullet import load_robot_description

pybullet.connect(pybullet.GUI)
pybullet.configureDebugVisualizer(pybullet.COV_ENABLE_SHADOWS, 0)
pybullet.configureDebugVisualizer(pybullet.COV_ENABLE_GUI, 0)

try:
load_robot_description(name)
except ModuleNotFoundError:
load_robot_description(f"{name}_description")

input("Press Enter to close PyBullet and terminate... ")
pybullet.disconnect()


def show_in_yourdfpy(
name: str,
configuration: List[float],
collision: bool,
Expand All @@ -114,8 +225,8 @@ def show(
module = import_module(f"robot_descriptions.{name}_description")
if not hasattr(module, "URDF_PATH"):
raise ValueError(
"show command only applies to URDF, check out the "
"`show_in_mujoco.py` example for MJCF descriptions"
"This command only works with URDF descriptions, use "
"`show_in_mujoco` for MJCF descriptions"
)

try:
Expand Down Expand Up @@ -156,7 +267,7 @@ def animate(name: str) -> None:
if not hasattr(module, "URDF_PATH"):
raise ValueError(
"animation is only available for URDF descriptions, "
"check out the ``show_in_mujoco.py`` example for MJCF"
"check out the `show_in_mujoco.py` example for MJCF"
)

print(
Expand All @@ -172,9 +283,19 @@ def main(argv=None):
args = parser.parse_args(argv)
if args.subcmd == "list":
list_descriptions()
elif args.subcmd == "show":
show(args.name, args.configuration, args.collision)
elif args.subcmd == "show_in_meshcat":
show_in_meshcat(args.name)
elif args.subcmd == "show_in_mujoco":
show_in_mujoco(args.name)
elif args.subcmd == "show_in_pybullet":
show_in_pybullet(args.name)
elif args.subcmd == "show_in_yourdfpy":
show_in_yourdfpy(args.name, args.configuration, args.collision)
elif args.subcmd == "animate":
animate(args.name)
else: # no subcommand
parser.print_help()


if __name__ == "__main__":
main()
6 changes: 3 additions & 3 deletions robot_descriptions/_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def update(
op_code: Integer that can be compared against Operation IDs and
stage IDs.
cur_count: Current item count.
max_count: Maximum item count, or ``None`` if there is none.
max_count: Maximum item count, or `None` if there is none.
message: Unused here.
"""
self.progress.total = max_count
Expand Down Expand Up @@ -112,8 +112,8 @@ def clone_to_cache(description_name: str, commit: Optional[str] = None) -> str:

Notes:
By default, robot descriptions are cached to
``~/.cache/robot_descriptions``. This behavior can be overriden by
setting the ``ROBOT_DESCRIPTIONS_CACHE`` environment variable to an
`~/.cache/robot_descriptions`. This behavior can be overriden by
setting the `ROBOT_DESCRIPTIONS_CACHE` environment variable to an
alternative path.
"""
try:
Expand Down