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
21 changes: 18 additions & 3 deletions docs/extensions/rcs_robotiq2f85.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 14 additions & 3 deletions extensions/rcs_robotiq2f85/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion extensions/rcs_robotiq2f85/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ 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==0.2.0",
"typer~=0.9",
]
readme = "README.md"
maintainers = [
Expand Down
30 changes: 30 additions & 0 deletions extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/__main__.py
Original file line number Diff line number Diff line change
@@ -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()
18 changes: 8 additions & 10 deletions extensions/rcs_robotiq2f85/src/rcs_robotiq2f85/hw.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 Robotiq2F85, Robotiq2FStatus

import rcs

Expand All @@ -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
Expand All @@ -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

Expand All @@ -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:
"""
Expand All @@ -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:
Expand Down
Loading