Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/build_wheels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@ jobs:
fail-fast: false
matrix:
extension:
- rcs_digit
- rcs_realsense
- rcs_robotiq2f85
- rcs_tilburg_hand
- rcs_ur5e
- rcs_usb_cam
- rcs_xarm7
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ jobs:
extension:
# Python extensions
- rcs_xarm7
- rcs_digit
- rcs_tilburg_hand
- rcs_realsense
- rcs_robotiq2f85
- rcs_tacto
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ WHEELHOUSE = wheelhouse
CORE_WHEELHOUSE = ${WHEELHOUSE}/core
PY_EXT_WHEELHOUSE = ${WHEELHOUSE}/py_extensions
CPP_EXT_WHEELHOUSE = ${WHEELHOUSE}/cpp_extensions
PY_EXTENSIONS ?= rcs_realsense rcs_robotiq2f85 rcs_ur5e rcs_usb_cam rcs_xarm7 rcs_zed
PY_EXTENSIONS ?= rcs_digit rcs_realsense rcs_robotiq2f85 rcs_tilburg_hand rcs_ur5e rcs_usb_cam rcs_xarm7 rcs_zed
CPP_EXTENSIONS ?= rcs_fr3 rcs_panda rcs_so101
# set to pypi to publish to the real index
PYPI_REPOSITORY ?= testpypi
Expand Down
17 changes: 16 additions & 1 deletion docs/development/python_extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,22 @@ rcs_myext/

2. **Implement the Interface**: Create your device class in `src/rcs_myext/my_device.py`. You should inherit from the appropriate RCS base class (e.g., `Camera`, `Gripper`) if applicable, or implement the required methods.

3. **Register the Extension**: If your extension needs to be discoverable by RCS (e.g., for CLI tools or automatic loading), ensure it's installed in the same environment.
3. **Register the Backend**: If your extension provides a camera, gripper or hand, declare a factory
as an entry point so the robot extensions can create it from a config. The entry point name is
the type id the config carries (`camera_type_id`, `GripperType.id` or `HandType.id`), and the
group is one of `rcs.cameras`, `rcs.grippers` or `rcs.hands`:

```toml
[project.entry-points."rcs.cameras"]
mycam = "rcs_myext.creators:create_camera_set"
```

The factory takes the config and returns the device, for a camera
`(HardwareCameraCreatorConfig) -> HardwareCamera`. Nothing else has to know about your
extension: `rcs.registry` lists installed backends from package metadata without importing
them, and imports yours only when its id is requested. Reinstall the extension after changing
entry points, they are read from the installed metadata. For code that is not installed as a
package, `rcs.registry.CAMERAS.register("mycam", create_camera_set)` does the same at runtime.

## Example: USB Camera

Expand Down
2 changes: 2 additions & 0 deletions docs/extensions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ rcs_ur5e
rcs_so101
rcs_yam
rcs_realsense
rcs_digit
rcs_usb_cam
rcs_tacto
rcs_robotiq2f85
rcs_tilburg_hand
rcs_zed
```
2 changes: 2 additions & 0 deletions docs/extensions/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ RCS comes with several supported extensions:
- **rcs_yam**: Support for the I2RT YAM arm.
- **rcs_realsense**: Support for Intel RealSense cameras.
- **rcs_usb_cam**: Support for generic USB webcams.
- **rcs_digit**: Support for DIGIT tactile sensors.
- **rcs_tilburg_hand**: Support for the Tilburg Hand (hardware; the simulated hand is in `rcs-core`).
- **rcs_tacto**: Integration with the Tacto tactile sensor simulator.
- **rcs_robotiq2f85**: Integration with the Robotiq 2F-85 Gripper.

Expand Down
20 changes: 20 additions & 0 deletions docs/extensions/rcs_digit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# RCS DIGIT Extension

This extension provides support for [DIGIT](https://digit.ml) tactile sensors in RCS.

The sensor is exposed as an RCS `HardwareCamera`, so it is configured and polled like any other
camera in a camera set. The robot extensions register a `digit` camera type that resolves once
this extension is installed.

## Installation

```shell
pip install rcs-digit
```

For local development:

```shell
pip install -ve . --no-build-isolation
pip install -ve extensions/rcs_digit
```
22 changes: 22 additions & 0 deletions docs/extensions/rcs_tilburg_hand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# RCS Tilburg Hand Extension

This extension provides support for the Tilburg Hand in RCS.

It covers the **hardware** hand only. The simulated hand, `rcs.sim.SimTilburgHand`, is implemented
in C++ inside `rcs-core` and needs nothing from this extension.

The arm extensions accept a `THConfig` wherever they take a gripper config, and attach the hand
through a `HandWrapper` once this extension is installed.

## Installation

```shell
pip install rcs-tilburg-hand
```

For local development:

```shell
pip install -ve . --no-build-isolation
pip install -ve extensions/rcs_tilburg_hand
```
2 changes: 1 addition & 1 deletion docs/extensions/rcs_zed.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pip install -ve extensions/rcs_zed
## Calibration

The `default_zed(...)` helper uses the identity
`DummyCalibrationStrategy` unless a `calibration_strategy` mapping is supplied.
`IdentityCalibrationStrategy` unless a `calibration_strategy` mapping is supplied.
Mapping keys must match the logical camera names, and values must implement
`rcs.camera.hw.CalibrationStrategy`. Calibration is injected explicitly so the
ZED extension remains independent of other hardware-camera extensions.
Expand Down
53 changes: 53 additions & 0 deletions extensions/rcs_digit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# RCS DIGIT Extension

Support for the [DIGIT](https://digit.ml) tactile sensor in RCS, built on the
[`digit-interface`](https://pypi.org/project/digit-interface/) driver.

This extension depends on [`rcs-core`](https://pypi.org/project/rcs-core/).
Documentation: <https://robotcontrolstack.org/extensions/rcs_digit>

## Installation

Install from PyPI:

```shell
pip install rcs-digit
```

Warning: plain `pip install rcs-digit` will install the published `rcs-core` dependency from PyPI.

Install from a local checkout:

```shell
pip install -ve .
```

If you want this extension to use your local RCS checkout instead of the published `rcs-core` package, first install the main package from the repository root:

```shell
pip install -ve . --no-build-isolation
pip install -ve extensions/rcs_digit
```

## Usage

The robot extensions register a `digit` camera type that resolves to `DigitCam` once this
extension is installed, so a DIGIT is configured like any other hardware camera:

```python
from rcs.envs.base import HardwareCameraCreatorConfig

camera_cfgs["digit"] = HardwareCameraCreatorConfig(
camera_type_id="digit",
camera_cfgs={"digit": BaseCameraConfig(identifier="D00001")},
)
```

`default_digit` builds those configs from a name-to-serial mapping, using the resolution and
frame rate of a named `digit-interface` stream:

```python
from rcs_digit.camera import default_digit

cameras = default_digit({"thumb": "D00001", "index": "D00002"}, stream_name="QVGA")
```
26 changes: 26 additions & 0 deletions extensions/rcs_digit/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "rcs_digit"
version = "0.7.3"
description = "RCS DIGIT tactile sensor module"
readme = "README.md"
license = "AGPL-3.0-or-later"
dependencies = ["rcs-core>=0.7.3", "digit-interface"]
maintainers = [
{ name = "Tobias Juelg", email = "tobias.juelg@utn.de" },
]
authors = [{ name = "Tobias Juelg", email = "tobias.juelg@utn.de" }]
requires-python = ">=3.11"

[project.entry-points."rcs.cameras"]
digit = "rcs_digit.creators:create_camera_set"

[tool.black]
line-length = 120
target-version = ["py310"]

[tool.isort]
profile = "black"
1 change: 1 addition & 0 deletions extensions/rcs_digit/src/rcs_digit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.7.3"
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from digit_interface.digit import Digit
"""DIGIT tactile sensor support, built on the `digit-interface` driver.

The sensor is exposed as an RCS `HardwareCamera`, so it is configured and polled like any other
camera in a camera set. `default_digit` builds the per-sensor configs from a name-to-serial map.
"""

from digit_interface import Digit
from rcs._core.common import BaseCameraConfig
from rcs.camera.hw import HardwareCamera
from rcs.camera.interface import CameraFrame, DataFrame, Frame
Expand Down Expand Up @@ -55,3 +61,20 @@ def config(self, camera_name) -> BaseCameraConfig:
def calibrate(self) -> bool:
"""No calibration needed for DIGIT cameras."""
return True


def default_digit(name2id: dict[str, str] | None, stream_name: str = "QVGA") -> DigitCam | None:
"""Build a `DigitCam` for `name -> serial`, sized from the named `digit-interface` stream."""
if name2id is None:
return None
stream_dict = Digit.STREAMS[stream_name]
cameras = {
name: BaseCameraConfig(
identifier=identifier,
resolution_width=stream_dict["resolution"]["width"],
resolution_height=stream_dict["resolution"]["height"],
frame_rate=stream_dict["fps"]["30fps"],
)
for name, identifier in name2id.items()
}
return DigitCam(cameras=cameras)
13 changes: 13 additions & 0 deletions extensions/rcs_digit/src/rcs_digit/creators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Factory for the `digit` camera backend, registered as an `rcs.cameras` entry point."""

import typing

from rcs.camera.hw import HardwareCamera, HardwareCameraCreatorConfig
from rcs_digit.camera import DigitCam


def create_camera_set(cfg: HardwareCameraCreatorConfig) -> HardwareCamera:
if cfg.calibration is not None:
msg = "DIGIT sensors are not calibrated, `calibration` must be None"
raise ValueError(msg)
return typing.cast(HardwareCamera, DigitCam(cameras=cfg.camera_cfgs))
3 changes: 3 additions & 0 deletions extensions/rcs_fr3/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ authors = [{ name = "Tobias Juelg", email = "tobias.juelg@utn.de" }]
requires-python = ">=3.11"


[project.entry-points."rcs.grippers"]
FrankaHand = "rcs_fr3.creators:create_franka_gripper"

[dependency-groups]
build_deps = [
"pin==3.7.0",
Expand Down
6 changes: 3 additions & 3 deletions extensions/rcs_fr3/src/rcs_fr3/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def config(self) -> FR3MultiHardwareEnvCreatorConfig:
cfg = base.config()
cfg.robot_cfg.async_control = True
cfg.robot_cfg.ip = self.robot_ip
cfg.robot_cfg.tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType("Robotiq2F85")]
cfg.robot_cfg.tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType.Robotiq2F85]
cfg.robot_cfg.q_home = rcs.HOME_POSITIONS["FR3_DROID"]

return FR3MultiHardwareEnvCreatorConfig(
Expand Down Expand Up @@ -208,8 +208,8 @@ def config(self) -> FR3MultiHardwareEnvCreatorConfig:

cfg = super().config()
cfg.camera_cfgs = None
cfg.robot_cfgs["left"].tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType("Robotiq2F85")]
cfg.robot_cfgs["right"].tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType("Robotiq2F85")]
cfg.robot_cfgs["left"].tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType.Robotiq2F85]
cfg.robot_cfgs["right"].tcp_offset = rcs.GRIPPER_TCP_OFFSETS[common.GripperType.Robotiq2F85]
cfg.robot_cfgs["left"].q_home = rcs.HOME_POSITIONS["FR3_DUO_LEFT"]
cfg.robot_cfgs["right"].q_home = rcs.HOME_POSITIONS["FR3_DUO_RIGHT"]
cfg.gripper_cfgs = {
Expand Down
Loading
Loading