From 688a5f8f480be59010d92bfca8688b72659da9ae Mon Sep 17 00:00:00 2001 From: Tobias Juelg Date: Mon, 24 Aug 2026 19:07:42 -0700 Subject: [PATCH 1/4] feat(extensions): robotiq bump --- extensions/rcs_robotiq2f85/pyproject.toml | 2 +- .../rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/extensions/rcs_robotiq2f85/pyproject.toml b/extensions/rcs_robotiq2f85/pyproject.toml index 4584132e..a79e4ffa 100644 --- a/extensions/rcs_robotiq2f85/pyproject.toml +++ b/extensions/rcs_robotiq2f85/pyproject.toml @@ -8,7 +8,7 @@ version = "0.7.2" description="RCS RobotiQ module" dependencies = [ "rcs-core>=0.7.2", - "2f85-python-driver @ git+https://github.com/RobotControlStack/2f85-python-driver.git", + "robotiq2f @ git+https://github.com/RobotControlStack/robotiq2f.git", ] readme = "README.md" maintainers = [ diff --git a/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py index db4be1fa..bfe75456 100644 --- a/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py +++ b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py @@ -2,7 +2,7 @@ from rcs._core.common import Gripper, GripperConfig, GripperState from rcs.common_typing import GripperConfigKwargs -from Robotiq2F85Driver.Robotiq2F85Driver import GripperStatus, Robotiq2F85Driver +from robotiq2f import Robotiq2FStatus, Robotiq2F85 import rcs @@ -22,7 +22,9 @@ def __init__( serial_number: Get the serial number with `udevadm info -a -n /dev/ttyUSB0 | grep serial`, make sure you have read/write permissions to the port. speed: Speed in mm/s. Must be between 20 and 150 mm/s. force: Force in N. Must be between 20 and 235 N. - async_control: If True, gripper commands return immediately without waiting for the movement to complete. A new command interrupts any ongoing movement. + async_control: If True, gripper commands return immediately without waiting for the movement to complete + (a new command interrupts any ongoing movement), and the driver polls the gripper status in a + background thread so that state reads are served from cache instead of blocking on Modbus. """ super().__init__(**kwargs) self.serial_number = serial_number @@ -33,7 +35,7 @@ def __init__( class RobotiQ2F85GripperState(GripperState): - def __init__(self, state: GripperStatus) -> None: + def __init__(self, state: Robotiq2FStatus) -> None: super().__init__() self.state = state @@ -42,13 +44,11 @@ class RobotiQ2F85Gripper(Gripper): def __init__(self, cfg: RobotiQ2F85GripperConfig): super().__init__() self._cfg: RobotiQ2F85GripperConfig = cfg - self.gripper = Robotiq2F85Driver(serial_number=cfg.serial_number) - self._last_normalized_width = 1.0 + self.gripper = Robotiq2F85(serial_number=cfg.serial_number, async_control=cfg.async_control) self.gripper.reset() def get_normalized_width(self) -> float: - # Return the last commanded width to avoid a synchronous Modbus read on every env step. - return self._last_normalized_width + return self.gripper.opening / self.gripper.MAX_OPENING def grasp(self) -> None: """ @@ -72,13 +72,11 @@ def set_normalized_width(self, width: float, force: float = 0) -> None: if not (0 <= width <= 1): msg = f"Width must be between 0 and 1, got {width}." raise ValueError(msg) - self._last_normalized_width = width - abs_width = width * 85 + abs_width = width * self.gripper.MAX_OPENING self.gripper.go_to( opening=float(abs_width), speed=self._cfg.speed, force=force if force != 0 else self._cfg.force, - blocking_call=not self._cfg.async_control, ) def shut(self) -> None: From f0f8ab6029b8d23e49eb67dd4891b5c5b4cacb32 Mon Sep 17 00:00:00 2001 From: Tobias Juelg Date: Tue, 25 Aug 2026 13:06:19 -0700 Subject: [PATCH 2/4] feat(extensions): robotiq cli for serials --- extensions/rcs_robotiq2f85/pyproject.toml | 1 + .../src/rcs_robotiq2f85/__main__.py | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/__main__.py diff --git a/extensions/rcs_robotiq2f85/pyproject.toml b/extensions/rcs_robotiq2f85/pyproject.toml index a79e4ffa..638a09df 100644 --- a/extensions/rcs_robotiq2f85/pyproject.toml +++ b/extensions/rcs_robotiq2f85/pyproject.toml @@ -9,6 +9,7 @@ description="RCS RobotiQ module" dependencies = [ "rcs-core>=0.7.2", "robotiq2f @ git+https://github.com/RobotControlStack/robotiq2f.git", + "typer~=0.9", ] readme = "README.md" maintainers = [ diff --git a/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/__main__.py b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/__main__.py new file mode 100644 index 00000000..b714038a --- /dev/null +++ b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/__main__.py @@ -0,0 +1,30 @@ +import logging + +import typer +from robotiq2f import LinuxFindTTYWithSerialNumber + +logger = logging.getLogger(__name__) +# callback=... keeps `serials` a subcommand; typer collapses a single-command app otherwise. +robotiq2f85_app = typer.Typer(help="CLI tool for the Robotiq 2F gripper module of rcs.", callback=lambda: None) + + +@robotiq2f85_app.command() +def serials(): + """Reads out the serial numbers of the connected serial devices.""" + devices = LinuxFindTTYWithSerialNumber().list_devices() + + if len(devices) == 0: + typer.secho("No serial devices connected.", fg=typer.colors.YELLOW, err=True) + return + + typer.echo("Connected devices:") + for port, serial in devices: + typer.echo(f" {port}: {serial if serial is not None else 'unknown'}") + + +def main(): + robotiq2f85_app() + + +if __name__ == "__main__": + main() From 4171bc1cab41550ba00e87a917cf1e3d3693ef4d Mon Sep 17 00:00:00 2001 From: Tobias Juelg Date: Tue, 25 Aug 2026 13:06:43 -0700 Subject: [PATCH 3/4] docs(extensions): robotiq serials cli --- docs/extensions/rcs_robotiq2f85.md | 21 ++++++++++++++++++--- extensions/rcs_robotiq2f85/README.md | 17 ++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/extensions/rcs_robotiq2f85.md b/docs/extensions/rcs_robotiq2f85.md index 3a620b17..45a6723c 100644 --- a/docs/extensions/rcs_robotiq2f85.md +++ b/docs/extensions/rcs_robotiq2f85.md @@ -15,14 +15,29 @@ pip install -ve . --no-build-isolation pip install -ve extensions/rcs_robotiq2f85 ``` -Get the serial number of the gripper with this command: + +## Serial Numbers + ```shell +python -m rcs_robotiq2f85 serials +# the following command also works udevadm info -a -n /dev/ttyUSB0 | grep serial ``` -Provide the necessary permission: + +Lists every `/dev/ttyUSB*` device with its serial number. The serial number is what you pass as +`serial_number` to `RobotiQ2F85GripperConfig`. + +To access the serial port, add yourself to the `dialout` group, then log out and back in: + +```shell +sudo adduser $USER dialout +``` + +As a temporary alternative that is reset when the gripper is replugged: + ```shell -chmod 777 /dev/ttyUSB0 +sudo chmod 666 /dev/ttyUSB0 ``` ## Usage diff --git a/extensions/rcs_robotiq2f85/README.md b/extensions/rcs_robotiq2f85/README.md index 21afb65e..c731d0ec 100644 --- a/extensions/rcs_robotiq2f85/README.md +++ b/extensions/rcs_robotiq2f85/README.md @@ -28,16 +28,27 @@ pip install -ve . --no-build-isolation pip install -ve extensions/rcs_robotiq2f85 ``` -Get the serial number of the gripper: +## Serials ```shell +python -m rcs_robotiq2f85 serials +# the following command also works udevadm info -a -n /dev/ttyUSB0 | grep serial ``` -Provide device permissions if needed: +Lists every `/dev/ttyUSB*` device with its serial number. The serial number is what you pass as +`serial_number` to `RobotiQ2F85GripperConfig`. + +To access the serial port, add yourself to the `dialout` group, then log out and back in: + +```shell +sudo adduser $USER dialout +``` + +As a temporary alternative that is reset when the gripper is replugged: ```shell -chmod 777 /dev/ttyUSB0 +sudo chmod 666 /dev/ttyUSB0 ``` ## Usage From 1b1edffbf6dfa75d39d0681e73ace560014befe2 Mon Sep 17 00:00:00 2001 From: Tobias Juelg Date: Tue, 25 Aug 2026 13:10:49 -0700 Subject: [PATCH 4/4] chore(extensions): robotiq pypi package --- extensions/rcs_robotiq2f85/pyproject.toml | 2 +- extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/rcs_robotiq2f85/pyproject.toml b/extensions/rcs_robotiq2f85/pyproject.toml index 638a09df..02b02c22 100644 --- a/extensions/rcs_robotiq2f85/pyproject.toml +++ b/extensions/rcs_robotiq2f85/pyproject.toml @@ -8,7 +8,7 @@ version = "0.7.2" description="RCS RobotiQ module" dependencies = [ "rcs-core>=0.7.2", - "robotiq2f @ git+https://github.com/RobotControlStack/robotiq2f.git", + "robotiq2f==0.2.0", "typer~=0.9", ] readme = "README.md" diff --git a/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py index bfe75456..8046648b 100644 --- a/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py +++ b/extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py @@ -2,7 +2,7 @@ from rcs._core.common import Gripper, GripperConfig, GripperState from rcs.common_typing import GripperConfigKwargs -from robotiq2f import Robotiq2FStatus, Robotiq2F85 +from robotiq2f import Robotiq2F85, Robotiq2FStatus import rcs