Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8d56f54
Add tactile sensing integration with OrcaHandTouch subclass
fabricenoelbourquin Mar 27, 2026
3803580
Created constants file, moved sensor clients into hardware folder and…
fabricenoelbourquin Mar 28, 2026
4fd4383
Major refactor of MockSensorClient to reduce duplication with base cl…
fabricenoelbourquin Mar 31, 2026
c388a17
Extract protocol codec layer and harden contracts
fabricenoelbourquin Apr 3, 2026
5ea4575
Remove tactile sensing UI to separate orca_ui repository
fabricenoelbourquin Apr 3, 2026
129e6ec
Add MockOrcaHandTouch and modernize type annotations
fabricenoelbourquin Apr 3, 2026
c78b8f7
Address review feedback on tactile sensing API
fabricenoelbourquin Apr 7, 2026
8bde658
Simplify auto-stream error handling and reconfiguration
fabricenoelbourquin Apr 7, 2026
fa5bdfd
Address review feedback: rename stats fields, narrow excepts, remove …
fabricenoelbourquin Apr 7, 2026
2e6760c
Add wait_for_first_frame and taxel_counts to MockSensorClient
fabricenoelbourquin Apr 27, 2026
a5c96d9
Refactor tactile sensor tests to flat parametrized style
fabricenoelbourquin Apr 27, 2026
83aed75
Fix resultant decoder to read low byte only
fabricenoelbourquin May 5, 2026
ae12af3
Add tactile sensor port cascade and bring-up helpers
fabricenoelbourquin May 5, 2026
6de2833
Add tactile sensor health-check script
fabricenoelbourquin May 5, 2026
21ca38a
Remove dead reconfiguration code and simplify health-check zeroing
fabricenoelbourquin May 5, 2026
a8aa347
Rename SensorClient to TactileClient and add combined-data accessor
fabricenoelbourquin May 6, 2026
963446c
-Revert .gitignore chagnes unrelated to tactile sensing
fabricenoelbourquin May 6, 2026
f6c2935
Tighten tactile-sensing comments, drop unused mock kwarg, add pyserial
fabricenoelbourquin May 6, 2026
a0bd5ad
Add FingerName Literal type and consolidate test_protocol.py
fabricenoelbourquin May 6, 2026
cad7840
Drop name-restating docstrings, add pyserial dep
fabricenoelbourquin May 6, 2026
07a12ec
fix: uncrew CI
fracapuano May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ celerybeat-schedule
# Environments
.env
.venv
uv.lock
env/
venv/
ENV/
Expand Down Expand Up @@ -124,12 +123,9 @@ urdf/

dev_scripts/

calibration.yaml
orca_core/models/*

# macOS
.DS_Store

.pytest_cache/

# not storing the lockfile (as of now)
uv.lock
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ docs/ # Documentation

- Use conventional commits: `Add feature`, `Fix bug`, `Update docs`
- Keep messages concise and descriptive
- Do NOT add `Co-Authored-By` lines

---

Expand Down
4 changes: 4 additions & 0 deletions orca_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@
from .calibration import CalibrationResult
from .hand_config import BaseHandConfig
from .hand_config import OrcaHandConfig
from .hand_config import OrcaHandTouchConfig
from .hand_config import canonical_joint_ids
from .hardware_hand import OrcaHand
from .hardware_hand import OrcaHandTouch
from .joint_position import OrcaJointPositions
from .version import LATEST_VERSION

__all__ = [
"CalibrationResult",
"BaseHandConfig",
"OrcaHandConfig",
"OrcaHandTouchConfig",
"OrcaHand",
"OrcaHandTouch",
"OrcaJointPositions",
"canonical_joint_ids",
"LATEST_VERSION",
Expand Down
3 changes: 3 additions & 0 deletions orca_core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
0x1A86, # QinHeng Electronics CH340 (most Feetech USB adapters)
0x10C4, # Silicon Labs CP210x (some Feetech boards)
],
"tactile_sensor": [
0x28E9, # Paxini tactile sensor USB adapter
],
}

# Baudrates the connect-time probe will try, per motor family, in priority
Expand Down
63 changes: 63 additions & 0 deletions orca_core/hand_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# See the LICENSE file at the root of this repository for full license information.
# ==============================================================================

import dataclasses
import os
from dataclasses import dataclass, field
from typing import Dict, List, Literal, Optional
Expand All @@ -19,6 +20,13 @@
MOTOR_IDS,
SUPPORTED_MOTOR_TYPES,
)
from .hardware.sensing.constants import (
FINGER_NAMES,
VALID_SENSOR_IDS,
DEFAULT_SENSOR_PORT,
DEFAULT_SENSOR_BAUDRATE,
DEFAULT_FINGER_TO_SENSOR_ID,
)
from .joint_position import OrcaJointPositions
from .utils.utils import get_model_path, read_yaml

Expand Down Expand Up @@ -336,4 +344,59 @@ def __post_init__(self) -> None:
self.validate_config()


@dataclass(frozen=True)
class OrcaHandTouchConfig(OrcaHandConfig):
"""ORCA hand configuration with tactile sensor support."""

sensor_port: str = DEFAULT_SENSOR_PORT
sensor_baudrate: int = DEFAULT_SENSOR_BAUDRATE
finger_to_sensor_id: Dict[str, int] = field(
default_factory=lambda: dict(DEFAULT_FINGER_TO_SENSOR_ID)
)

@classmethod
def from_config_path(
cls,
config_path: str | None = None,
calibration_path: str | None = None,
model_version: str | None = None,
model_name: str | None = None,
) -> "OrcaHandTouchConfig":
base = OrcaHandConfig.from_config_path(
config_path=config_path,
calibration_path=calibration_path,
model_version=model_version,
model_name=model_name,
)

config = read_yaml(base.config_path)
sensors = config.get("sensors", {})

sensor_kwargs = {}
if "port" in sensors:
sensor_kwargs["sensor_port"] = sensors["port"]
if "baudrate" in sensors:
sensor_kwargs["sensor_baudrate"] = int(sensors["baudrate"])
if "finger_to_sensor_id" in sensors:
sensor_kwargs["finger_to_sensor_id"] = dict(sensors["finger_to_sensor_id"])

return cls(**{f.name: getattr(base, f.name) for f in dataclasses.fields(base)}, **sensor_kwargs)

def validate_config(self) -> None:
super().validate_config()

if set(self.finger_to_sensor_id.keys()) != set(FINGER_NAMES):
raise HandConfigValidationError(
f"finger_to_sensor_id must contain exactly {FINGER_NAMES}, "
f"got {sorted(self.finger_to_sensor_id.keys())}"
)

ids = set(self.finger_to_sensor_id.values())
if ids != VALID_SENSOR_IDS:
raise HandConfigValidationError(
f"finger_to_sensor_id values must be 0-4 with no duplicates, "
f"got {sorted(self.finger_to_sensor_id.values())}"
)


HandConfig = BaseHandConfig
Loading
Loading