Software & Driver Setup

Install the Paxini SDK, stream pressure data in Python, visualize contact heatmaps, add optional ROS2 integration, and synchronize with your robot arm for full data collection pipelines.

Step 1 — Installation

Install the Paxini SDK

The Paxini Gen3 communicates over USB HID — no kernel modules or vendor drivers required. The Python SDK wraps the low-level HID protocol and exposes a clean streaming API.

Any OS supported The Gen3 enumerates as a standard USB HID device. Windows, macOS, and Linux all support it with inbox drivers. Python 3.10 or later is required.
# Python 3.10+ required pip install paxini-sdk # With visualization extras (matplotlib, numpy) pip install "paxini-sdk[viz]" # With ROS2 bridge (requires ROS2 Humble or Jazzy) pip install "paxini-sdk[ros2]"

Verify the install:

python -c "import paxini; print(paxini.__version__)" # Expected: 1.2.x or later
Step 2 — USB Detection

Detecting the Sensor

Plug the Gen3 sensor into your computer via USB-C. Use the SDK's device discovery utility to confirm it is recognized:

python -m paxini.discover # Example output: # Found 1 Paxini device(s): # [0] PX-6AX-GEN3 serial=PX3A0042 firmware=1.2.4 variant=fingertip

For multi-sensor setups (e.g., five fingers on a USB hub):

python -m paxini.discover --all # Found 5 Paxini device(s): # [0] PX-6AX-GEN3 serial=PX3A0042 variant=fingertip (index 0) # [1] PX-6AX-GEN3 serial=PX3A0043 variant=fingertip (index 1) # ...
Linux USB permissions On Linux, you may need to add a udev rule to access HID devices without root. Run sudo python -m paxini.install_udev once to install the rule, then unplug and replug the sensor.
Step 3 — Streaming API

Python Streaming API

The core API is a callback-based stream. Each frame delivers a TactileFrame object containing the full pressure array and metadata.

# Minimal streaming example — 5 lines import paxini sensor = paxini.Sensor() # connects to first detected device sensor.start() for frame in sensor.stream(): print(frame.pressure_map) # 2D numpy array, shape (rows, cols) print(frame.total_force_n) # float, total normal force in Newtons

All public API methods:

Method / Property Description Returns
sensor.start()Begin streaming; non-blockingNone
sensor.stop()Stop the stream cleanlyNone
sensor.stream()Generator yielding TactileFrame objects at configured HzGenerator[TactileFrame]
sensor.latest()Returns the most recent frame without blockingTactileFrame
sensor.set_rate(hz)Set sample rate (50–500 Hz for USB-C, 50–200 Hz for BLE)None
sensor.calibrate()Zero-offset calibration against current resting loadNone
sensor.serialDevice serial number stringstr
sensor.variant"fingertip" | "finger-pad" | "palm"str
Step 4 — Data Format

Tactile Data Format

Each TactileFrame contains:

Field Type Description
frame.timestamp_nsintNanosecond timestamp (monotonic, host clock)
frame.pressure_mapnp.ndarray (H, W) float32Pressure in kPa per taxel. Shape varies by variant: fingertip is 8×8, palm is 16×12.
frame.contact_masknp.ndarray (H, W) boolTrue where pressure exceeds contact threshold (default: 5 kPa)
frame.contact_area_mm2floatSum of active taxel areas in mm²
frame.total_force_nfloatIntegrated normal force across all taxels, in Newtons
frame.contact_centroid(float, float)(row, col) centroid of the contact region in taxel coordinates
frame.in_contactboolTrue if total_force_n exceeds contact threshold (default: 0.05 N)
frame.seqintMonotonically increasing frame sequence number
Spatial resolution Fingertip variant: 8×8 taxel array covering 14×14 mm, 1.75 mm taxel pitch. Palm variant: 16×12 array covering 48×36 mm, 3 mm pitch. Each taxel measures 0–600 kPa.
Step 5 — Visualization

Live Heatmap Visualization

The SDK ships a ready-to-run live visualizer. Run it directly from the command line:

# Launch live heatmap — press Q to quit python -m paxini.visualize # Multi-sensor visualization (e.g., 5-finger setup) python -m paxini.visualize --all

Or embed in your own script using the paxini.viz module:

import paxini from paxini.viz import HeatmapWindow sensor = paxini.Sensor() sensor.start() window = HeatmapWindow(title="Paxini Gen3 — Fingertip") for frame in sensor.stream(): window.update(frame.pressure_map) if window.should_close(): break sensor.stop()
Step 6 (Optional) — ROS2

ROS2 Interface

The ROS2 bridge publishes a sensor_msgs/Image (pressure map) and a custom paxini_msgs/TactileFrame topic per sensor. Requires ROS2 Humble or Jazzy.

# Install the ROS2 extras and launch the node pip install "paxini-sdk[ros2]" ros2 run paxini_ros2 sensor_node # Published topics (per sensor): # /paxini/PX3A0042/pressure_image sensor_msgs/Image # /paxini/PX3A0042/tactile_frame paxini_msgs/TactileFrame # /paxini/PX3A0042/contact_event std_msgs/Bool

For multi-sensor setups, each device publishes under its serial number. You can remap topics in your launch file using standard ROS2 remapping.

Step 7 — Arm Integration

Robot Arm Integration

To synchronize Paxini data with robot arm joint state, use the paxini.sync module. It timestamps all sensor frames against a shared clock and provides a blocking API that yields synchronized (arm_state, tactile_frame) pairs:

import paxini from paxini.sync import MultiSourceSync # Initialize sensor and arm interface sensor = paxini.Sensor() arm = YourArmInterface() # any arm with a get_joint_state() method sync = MultiSourceSync( sensor=sensor, arm=arm, max_dt_ms=5.0 # reject pairs with >5ms timestamp gap ) for arm_state, tactile in sync.stream(): print(arm_state.joint_positions) print(tactile.total_force_n) print(tactile.pressure_map)
Timestamp alignment The Gen3 timestamps frames with nanosecond resolution using the host clock at USB interrupt time. Jitter is typically <0.5 ms. The MultiSourceSync class handles interpolation when arm and sensor rates differ.
Troubleshooting

Top 3 Issues

paxini.discover() returns empty list

The sensor is not being detected. Check in order: (1) Try a different USB-C cable — some cables are charge-only and do not carry data. (2) On Linux, run sudo python -m paxini.install_udev then unplug/replug the sensor. (3) Confirm the sensor LED is solid (not blinking) — blinking means it is in firmware update mode; hold the reset button for 5 seconds to exit.

pressure_map is all zeros when pressing

The sensor needs calibration or the threshold is set too high. Run sensor.calibrate() with the sensor unloaded (no contact). If the issue persists, check that the sensor variant matches your mount — a palm sensor installed on a fingertip will show very low pressure values due to the larger taxel area. Also confirm firmware version 1.2+ with python -m paxini.discover.

MultiSourceSync raises TimestampDriftError

The arm interface clock and sensor timestamp are diverging by more than max_dt_ms. This is usually caused by the arm SDK returning a stale joint state (e.g., buffered at a lower rate). Increase max_dt_ms to 10.0 as a first test, then investigate why the arm state refresh rate is lower than expected. For USB arms, confirm the USB polling rate is not being throttled by power management (sudo powertop on Linux).

Still stuck? Post in the forum questions thread with your OS, SDK version (paxini.__version__), and the exact error output.