Back to packages

robonix.primitive.linkerbot.linker_hand_o6.hand

LinkerHand O6 dexterous-hand primitive — axis-level position control + speed/torque limits over CAN.

README

primitive-linkerbot-linker_hand_o6-hand-rbnx

中文版

robonix.primitive.linkerbot.linker_hand_o6.hand — Robonix primitive for the LinkerBot LinkerHand O6 five-finger dexterous hand (six axes). Drives the hand over SocketCAN directly, with no ROS backend.

The LinkerHand CAN backend is vendored into this package (linkerhand_o6/vendor/, from the official linkerhand-python-sdk), so the package is self-contained — no PyPI package exists for that SDK, and rbnx build needs no network access to fetch it. See linkerhand_o6/vendor/README.md for what was copied, what was changed, and how to refresh it.

Capabilities

Contract Transport What it does
robonix/primitive/hand/driver gRPC lifecycle (CMD_INIT / ACTIVATE / DEACTIVATE / SHUTDOWN)
robonix/primitive/hand/info gRPC each axis's finger and type, so consumers can map fingers
robonix/primitive/hand/move_joint gRPC axis-level position control
robonix/primitive/hand/set_joint_speed_limits gRPC per-axis speed ceilings
robonix/primitive/hand/set_joint_torque_limits gRPC per-axis torque ceilings
robonix/primitive/hand/get_state gRPC read the current axis positions

The finger-level contracts (move_finger / set_finger_*) and the state_joint / state_finger streams (topic_out, which would need a ROS backend this deployment does not run) are intentionally not provided. Consumers read the axis↔finger mapping from info and then drive move_joint.

Axis layout

Six axes, in firmware order (which is also info's order and the order any 6-vector is interpreted in):

# Name Finger Type
0 thumb_cmc_pitch thumb bend
1 thumb_cmc_yaw thumb oppose
2 index_mcp_pitch index bend
3 middle_mcp_pitch middle bend
4 ring_mcp_pitch ring bend
5 pinky_mcp_pitch pinky bend

Units

Every value crossing the gRPC surface is normalized to [0, 1], per the robonix hand contracts:

  • position0 = open (extended), 1 = closed (flexed); for thumb_cmc_yaw, 0 = neutral and 1 = full opposition.
  • speed / torque limits0 = min, 1 = max.

The O6 firmware itself speaks raw 0–255 per axis, and for position its polarity is inverted (255 = fully open). linkerhand_o6/hand.py is the only place that conversion happens; it is unit-checked against the upstream SDK's own preset poses (its "张开" / open pose is all-255).

Configuration

Fields, units and defaults are in config.spec. The essentials:

  • hand_type (right | left, default right) — selects the hand's CAN arbitration id (0x27 right / 0x28 left). A wrong value means the hand never answers, and init fails on the state readback.
  • can_iface (default can0) — the SocketCAN interface the hand is wired to.
  • baudrate (default 1000000) — CAN bitrate; the O6 firmware runs at 1 Mbps.

Every field also falls back to a matching O6_HAND_* env var.

Dependencies and permissions

  • Python 3.8+ with python-can, numpy, and robonix_api.
  • CAN permissions. The primitive runs ip link set … at init to bring the interface up. Either bring it up beforehand —
sudo ip link set can0 up type can bitrate 1000000

— or export PREFLIGHT_SUDO_PASS so the internal sudo -S works. Bringing it up beforehand is preferred: then nothing here needs elevated rights.

Note that unlike the upstream SDK, this package does not read a sudo password from a committed yaml file. - scripts/start.sh defaults to $HOME/miniconda3/envs/bb_d1_robonix/bin/python3; override with LINKERHAND_O6_PYTHON.

Build and run

bash scripts/build.sh      # rbnx codegen, generating the gRPC stubs
bash scripts/start.sh      # or let rbnx boot start it from a deployment manifest
rbnx caps -v | grep hand   # check all 6 capabilities registered and the provider is ACTIVE

Shutdown is handled by the Driver's CMD_SHUTDOWN (on_shutdown opens the hand and releases the CAN handle). There is no cleanup beyond that, so no scripts/stop.sh is provided. The CAN interface is deliberately left up on shutdown — it may be shared with other devices on the bus.

Safety

  • Do the read-only checks first (info / get_state) before commanding move_joint.
  • For the first open/close, lower the ceiling with set_joint_torque_limits to avoid pinching a finger or damaging a knuckle.
  • Shut down any other controller (the LinkerHand ROS/ROS2 SDK, a mocap glove, the vendor GUI) before starting this primitive — two writers on one CAN bus fight over the hand.
  • Providers stop hardware output on input timeout, process exit, or CMD_SHUTDOWN.

Layout

package_manifest.yaml   config.spec   README.md   README_CN.md
scripts/{build.sh, start.sh}
linkerhand_o6/
  __init__.py   main.py            # robonix contract surface
  hand.py                          # normalized [0,1] wrapper
  vendor/                          # vendored LinkerHand SDK — see its README
    linker_hand_o6_can.py  color_msg.py  can_iface.py  UPSTREAM_COMMIT

License

MulanPSL-2.0. The vendored files retain their upstream licensing — see linkerhand_o6/vendor/README.md.