Skip to main content

Python SDK for the Digity sensorized exohand

Project description

digity SDK

Python SDK for the Digity sensorized exohand — plug in the glove, stream live sensor data directly into your Python code in three lines.

The Digity exohand is a sensorized glove that captures real-time hand motion data: joint angles for every finger, 6-axis IMU (accelerometer + gyroscope), and 6-channel capacitive touch per node. This SDK handles the USB connection, binary protocol decoding, and threading — so you get clean, typed Python objects at ~50 Hz without any low-level plumbing.

from digity import GloveStream, AnglesSensor

with GloveStream() as stream:
    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                print(sensor.finger, sensor.samples[-1].angles_deg)

Use cases: motion capture, rehabilitation, robotics control, gesture recognition, HCI research, data collection.


Requirements

  • Python 3.9 or later
  • The Digity exohand connected via USB

Installation

# SDK only
pip install digity

# SDK + real-time dashboard
pip install digity[viz]

Real-time dashboard

digity[viz] includes a full web-based dashboard that visualises live hand data, records sessions, and relays data from a remote agent — all locally, no internet required.

digity-viz              # opens the dashboard as a desktop window
digity-viz --port COM3  # explicit serial port
digity-viz --browser    # open in system browser instead

Or launch from Python:

import digity.viz
digity.viz.start()

The dashboard runs on http://localhost:5001/chiro/ and opens automatically. No login needed — it is intended for single-user local use.


Quick start

Plug in the glove, then run:

from digity import GloveStream, AnglesSensor, ImuSensor, TouchSensor

with GloveStream() as stream:
    for frame in stream:
        print(f"side={frame.side}  node={frame.node_id}  seq={frame.seq}")

        for sensor in frame.sensors:

            if isinstance(sensor, AnglesSensor):
                angles = sensor.samples[-1].angles_deg
                print(f"  finger {sensor.finger} angles: {angles}")

            elif isinstance(sensor, ImuSensor):
                s = sensor.samples[-1]
                print(f"  finger {sensor.finger} acc={s.acc}  gyro={s.gyro}")

            elif isinstance(sensor, TouchSensor):
                print(f"  finger {sensor.finger} touch={sensor.channels}")

The with block handles connection and cleanup automatically. Press Ctrl+C to stop.


Specifying the USB port

The SDK auto-detects the glove port on all platforms. If auto-detection fails, pass the port explicitly:

GloveStream(port="/dev/ttyUSB0")   # Linux
GloveStream(port="/dev/tty.usbserial-0001")  # macOS
GloveStream(port="COM3")           # Windows

Data types

GloveFrame

Every iteration of GloveStream yields one GloveFrame:

Field Type Description
ts float Host timestamp (seconds since epoch) when the frame arrived
side str "right" or "left"
group str "hand" or "arm"
node_id int PCB node number on the glove
seq int Packet counter 0–65535, wraps around
sensors list[Sensor] List of sensor readings in this frame

AnglesSensor

Joint angles for one finger node.

Field Type Description
finger int Finger index (0=thumb, 1=index, …, 4=pinky)
com int Communication line index
samples list[AnglesSample] One or more timestamped angle readings

AnglesSample

Field Type Description
ts_us int Sensor timestamp in microseconds
angles_deg list[float] Joint angles in degrees (5 values for hand group)
if isinstance(sensor, AnglesSensor):
    latest = sensor.samples[-1]
    print(latest.angles_deg)   # e.g. [12.3, 45.1, 30.0, 5.5, 2.0]

ImuSensor

6-axis IMU (accelerometer + gyroscope) for one finger node.

Field Type Description
finger int Finger index
com int Communication line index
samples list[ImuSample] One or more timestamped IMU readings

ImuSample

Field Type Description
ts_us int Sensor timestamp in microseconds
acc tuple[int, int, int] Accelerometer x/y/z, raw i16 counts
gyro tuple[int, int, int] Gyroscope x/y/z, raw i16 counts
if isinstance(sensor, ImuSensor):
    s = sensor.samples[-1]
    print(s.acc)    # e.g. (312, -128, 16384)
    print(s.gyro)   # e.g. (5, -12, 3)

TouchSensor

6-channel capacitive touch for one finger node.

Field Type Description
finger int Finger index
com int Communication line index
ts_us int Sensor timestamp in microseconds
channels list[float] 6 values normalised 0.0–1.0
channels_raw list[int] 6 raw ADC counts 0–4095
if isinstance(sensor, TouchSensor):
    print(sensor.channels)      # [0.0, 0.82, 0.0, 0.0, 0.41, 0.0]
    print(sensor.channels_raw)  # [0, 3358, 0, 0, 1680, 0]

Type annotation helper

from digity import Sensor  # Union[AnglesSensor, ImuSensor, TouchSensor]

def process(sensor: Sensor) -> None:
    if isinstance(sensor, AnglesSensor):
        ...

Common patterns

Record data to a file

import csv
from digity import GloveStream, AnglesSensor

with GloveStream() as stream, open("recording.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["ts", "finger", "a0", "a1", "a2", "a3", "a4"])

    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                a = sensor.samples[-1].angles_deg
                writer.writerow([frame.ts, sensor.finger] + a)

Detect a closed fist

from digity import GloveStream, AnglesSensor

with GloveStream() as stream:
    finger_angles = {}

    for frame in stream:
        for sensor in frame.sensors:
            if isinstance(sensor, AnglesSensor):
                finger_angles[sensor.finger] = sensor.samples[-1].angles_deg

        if len(finger_angles) == 5:
            avg_flex = [sum(finger_angles[f][1] for f in range(1, 5)) / 4]
            if avg_flex[0] > 60:
                print("Fist detected!")

Stop the stream from another thread

import threading
from digity import GloveStream

stream = GloveStream()
stream.connect()

def stop_after(seconds):
    import time; time.sleep(seconds)
    stream.disconnect()

threading.Thread(target=stop_after, args=(10,), daemon=True).start()

for frame in stream:
    print(frame.seq)

Remote mode (ZMQ)

If the glove is connected to a remote machine running glove-core, you can receive frames over the network:

GloveStream(host="192.168.1.10")          # default port 5555
GloveStream(host="192.168.1.10", zmq_port=5556)

No USB connection is needed on the client machine in this mode.


Error handling

from digity import GloveStream, GloveNotFoundError

try:
    with GloveStream() as stream:
        for frame in stream:
            ...
except GloveNotFoundError:
    print("Glove not found — check the USB cable")
except KeyboardInterrupt:
    pass

GloveNotFoundError is raised at connection time if the glove port cannot be found.


Connection speed

The glove streams at approximately 50 Hz. Each call to for frame in stream blocks until the next frame arrives (up to 1 second timeout before checking for errors).

The SDK uses a background thread and an internal queue so that your processing code never blocks the serial buffer. If your code is slower than 50 Hz, frames are dropped to keep the stream live rather than accumulating memory.


License

© Digity. All rights reserved.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

digity-0.1.5.tar.gz (278.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

digity-0.1.5-py3-none-any.whl (283.1 kB view details)

Uploaded Python 3

File details

Details for the file digity-0.1.5.tar.gz.

File metadata

  • Download URL: digity-0.1.5.tar.gz
  • Upload date:
  • Size: 278.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for digity-0.1.5.tar.gz
Algorithm Hash digest
SHA256 924f95dacd9e771a1bedb5983d9a9551f5a6d4034d0a064402f0eb47f808ffaf
MD5 6876d8de2a46c60ffa3547b28dcb17c6
BLAKE2b-256 d14fb1b75efeddbcb97bc484e37da38dd5d44ce1a9404ad3aefcfd0094ddb6d9

See more details on using hashes here.

File details

Details for the file digity-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: digity-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 283.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for digity-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 a230cfa06207f56d9686011af181f1be4a0fae2011dfeb151b2a995a70dc950a
MD5 1d4a528ff4151aee0fe871753c9302da
BLAKE2b-256 5b42ddd1783b0a60289f4dfc338c45f20af31343e0180b3fbd42abf729f9f7d8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page