Skip to content

With Python

The Spectral-BLDC library wraps the driver's CAN protocol, gripper messages included: one method per message, and the gripper's answers unpacked into attributes. The ROS 2 driver and the legacy gripper GUI are built on it.

Installing

In a virtual environment, with Python 3.10 or newer:

py -m venv .venv
.venv\Scripts\activate
pip install Spectral-BLDC
The CANvas adapter's port is COM and a number: Device Manager lists it under Ports (COM & LPT). Give it as channel='COM20'.

python3 -m venv .venv
source .venv/bin/activate
pip install Spectral-BLDC
sudo usermod -aG dialout $USER     # access to the adapter's port; log out and in again once
The adapter's port is /dev/ttyACM0 (ls /dev/ttyACM* lists it).

python3 -m venv .venv
source .venv/bin/activate
pip install Spectral-BLDC
The adapter's port is /dev/tty.usbmodem and a number (ls /dev/tty.usbmodem* lists it).

The library pulls python-can and pyserial. The source and the examples are in Spectral-BLDC-Python.

The first program

import Spectral_BLDC as Spectral
import time

# the adapter: a CANvas on COM20 (Windows) or /dev/ttyACM0 (Linux), 1 Mbit/s
bus = Spectral.CanCommunication(bustype='slcan', channel='COM20', bitrate=1000000)
gripper = Spectral.SpectralCAN(node_id=0, communication=bus)

gripper.Send_Clear_Error()                      # clear first: with an error active, the calibration does not run
gripper.Send_gripper_calibrate()                # the jaws open fully, then close fully
for _ in range(120):                            # wait for the calibrated bit, up to 24 s
    time.sleep(0.2)
    gripper.Send_gripper_data_pack()            # only empty frames now: a move or a clear ends the calibration
    message, ids = bus.receive_can_messages(timeout=0.05)
    if message is not None:
        gripper.UnpackData(message, ids)
        if gripper.gripper_calibrated:
            break

for position in (200, 20, 200, 20):
    # position 0..255, speed 0..255, current mA, activate, action, E-stop, release direction
    gripper.Send_gripper_data_pack(position, 30, 300, 1, 1, 0, 0)
    bus.receive_can_messages(timeout=0.05)      # its answer still carries the state from before the move
    time.sleep(0.1)                             # give the move time to start
    for _ in range(40):                         # poll the state for up to 2 s
        gripper.Send_gripper_data_pack()        # an empty frame: status only
        message, ids = bus.receive_can_messages(timeout=0.05)
        if message is not None:
            gripper.UnpackData(message, ids)
            print(gripper.gripper_position, gripper.gripper_current, gripper.gripper_object_detection)
            if gripper.gripper_object_detection in (1, 2, 3):   # object found, or at the position
                break

The answer to a move command is sent before the gripper acts on it, so it still carries the state from before: after a finished move, object detection 3. The example reads that answer and drops it. At the very start of a move the jaws are still slow, and with the current near its limit the object detection can briefly read 1 or 2; the pause of 0.1 s covers that. While the gripper calibration runs, send only empty frames: a full command or Send_Clear_Error ends the calibration, and the gripper stays uncalibrated.

Send_gripper_data_pack with all seven arguments sends the move; without arguments it sends the empty status poll. receive_can_messages returns the raw python-can message and the node ID, command ID and error bit from the identifier; UnpackData fills the gripper attributes:

Attribute Meaning
gripper_position 0 open to 255 closed
gripper_current the motor current in mA
gripper_activated, gripper_action_status the activate flag; the action bit of the last command, which stays 1 while the gripper holds
gripper_object_detection 0 moving, 1 object found while closing, 2 object found while opening, 3 at the position
gripper_temperature_error, gripper_timeout_error, gripper_estop_error the three error bits
gripper_calibrated the gripper calibration is done

Object detection in library 1.20

Library version 1.20 assembles gripper_object_detection with its two bits swapped relative to the firmware: it reports 1 where the gripper sends 2 (object found while opening) and 2 where it sends 1 (object found while closing); 0 and 3 are unaffected. Until the library is fixed, swap the two values in your program, or test for "1 or 2" as this example does.

Stopping

gripper.Send_gripper_data_pack(20, 30, 300, 1, 1, 1, 0)   # the E-stop bit: stops, raises the E-stop error
gripper.Send_gripper_data_pack(20, 30, 300, 1, 0, 0, 0)   # to recover: first the bit back at 0, action 0
gripper.Send_Clear_Error()                                 # then clear; while the bit is 1, the error comes back
gripper.Send_Watchdog(500)                                 # from now on the gripper stops 500 ms after the last frame

With the watchdog set, keep the status poll running at a steady rate; a program that crashes releases the grip within the watchdog time.

Several grippers

One CanCommunication per bus, one SpectralCAN per gripper with its node ID; every answer carries the node ID in ids.node_id, so route it to the right object. The teleoperation guide couples two grippers this way.

The complete map of calls to messages is in the Python library reference.

Comments


Found a mistake on this page? Edit it on GitHub · Report it · Get help · This page as Markdown · llms.txt · The whole manual as PDF