Concept — Pressure Array Structure

The Spatial Pressure Map

The frame.pressure_map field is a 2D NumPy array where each element represents one taxel (tactile pixel). For the fingertip variant, the array is 8×8, covering the 14×14 mm sensing area at 1.75 mm pitch. Each value is in kilopascals (kPa).

Pressure (kPa)

Force per unit area. Range: 0–600 kPa. Normal finger contact is 10–150 kPa depending on grip force.

Contact Area (mm²)

Sum of taxel areas where pressure exceeds threshold. A typical fingertip-object contact is 15–60 mm².

Total Force (N)

Integral of pressure over contact area. Proportional to gripper closing force. Typical grasp: 1–20 N.

Centroid (row, col)

Center of mass of the contact region in taxel coordinates. Shifts when object slips.

# Explore the pressure map structure import paxini import numpy as np sensor = paxini.Sensor() sensor.start() sensor.calibrate() # zero-offset with no contact frame = sensor.latest() print("Array shape:", frame.pressure_map.shape) # (8, 8) print("Max pressure:", frame.pressure_map.max(), "kPa") print("Total force:", frame.total_force_n, "N") print("Contact area:", frame.contact_area_mm2, "mm²") print("Centroid:", frame.contact_centroid) # (row, col) float
Step 1 — Threshold Calibration

Contact vs. No-Contact Threshold

The default contact threshold is 5 kPa per taxel and 0.05 N total force. These defaults work for most setups but may need adjustment based on your gripper's preload and cable stress on the sensor.

Run this calibration script to measure your sensor's resting noise level and set an appropriate threshold:

import paxini import numpy as np sensor = paxini.Sensor() sensor.start() sensor.calibrate() # zero-offset first # Sample 100 frames with no contact baseline_forces = [] for i, frame in enumerate(sensor.stream()): baseline_forces.append(frame.total_force_n) if i >= 99: break noise_floor = np.max(baseline_forces) recommended_threshold = noise_floor * 3.0 print(f"Noise floor: {noise_floor:.4f} N") print(f"Recommended contact threshold: {recommended_threshold:.4f} N") # Apply the threshold sensor.set_contact_threshold_n(recommended_threshold)
Typical values Noise floor is usually 0.005–0.02 N. A threshold of 0.05–0.06 N gives reliable contact detection without false positives from cable movement.
Step 2 — Grasp Detection

Grasp Detection Logic

A simple but robust grasp detector uses three conditions simultaneously: contact flag true, contact area above minimum, and stable centroid (centroid not moving rapidly). This eliminates false positives from light brush contacts:

import paxini import numpy as np from collections import deque sensor = paxini.Sensor() sensor.start() sensor.calibrate() centroid_history = deque(maxlen=10) # last 10 frames def is_stable_grasp(frame): if not frame.in_contact: return False if frame.contact_area_mm2 < 8.0: # at least ~3 taxels return False centroid_history.append(frame.contact_centroid) if len(centroid_history) < 5: return False centroids = np.array(centroid_history) centroid_std = centroids.std(axis=0).max() return centroid_std < 0.5 # centroid stable within 0.5 taxel for frame in sensor.stream(): state = "STABLE GRASP" if is_stable_grasp(frame) else "no contact" print(f"{state} F={frame.total_force_n:.2f}N A={frame.contact_area_mm2:.1f}mm²")
Step 3 — Contact Event Visualization

Visualizing Contact Events Over Time

Plot total force and contact state over a 10-second recording to understand the temporal structure of a grasp-and-release cycle:

import paxini import numpy as np import matplotlib.pyplot as plt sensor = paxini.Sensor() sensor.start() sensor.calibrate() forces = [] contacts = [] timestamps = [] print("Recording 10 seconds — grasp and release the sensor...") for frame in sensor.stream(): forces.append(frame.total_force_n) contacts.append(float(frame.in_contact)) timestamps.append(frame.timestamp_ns / 1e9) if timestamps[-1] - timestamps[0] >= 10.0: break t = np.array(timestamps) - timestamps[0] fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 5), sharex=True) ax1.plot(t, forces, color='#3b82f6') ax1.set_ylabel("Force (N)") ax1.set_title("Grasp Force Over Time") ax2.fill_between(t, contacts, alpha=0.5, color='#10b981') ax2.set_ylabel("In Contact") ax2.set_xlabel("Time (s)") plt.tight_layout() plt.savefig("contact_events.png") print("Saved contact_events.png")

For force-torque terminology used in the field, see the robotics glossary.

Unit 2 Complete When...

You can print the pressure map shape and confirm it is (8, 8) for fingertip variant. Your calibrated contact threshold produces no false positives when you shake the sensor cable gently. The grasp detector prints "STABLE GRASP" within 0.5 seconds of gripping the sensor surface firmly. Your contact events plot shows clear rising/falling edges at grasp and release.