diff --git a/README.md b/README.md index 912ef544..6191c883 100644 --- a/README.md +++ b/README.md @@ -8,109 +8,149 @@ Tests -Orca Core is the core control package of the ORCA Hand. It's used to abstract hardware, provide scripts for calibration and tensioningm and to control the hand with simple high-level control methods in joint space. +# Orca Core -## Get Started +`orca_core` is the Python control package for the ORCA Hand. The refactored codebase is organized around a small set of explicit objects: -To get started with Orca Core, follow these steps: +- `OrcaHandConfig` loads the static hand description from `config.yaml`. +- `CalibrationResult` loads the mutable runtime state from `calibration.yaml`. +- `BaseHand` provides backend-agnostic joint-space helpers. +- `OrcaHand` adds hardware connection, torque, calibration, telemetry, and maintenance tasks. +- `OrcaJointPositions` is the typed container used for joint-space commands. -1. **Create a virtual environment** (recommended): +The practical workflow is: - ```sh - python -m venv venv - source venv/bin/activate - ``` +1. Choose a model config. +2. Connect to the hardware. +3. Call `init_joints()` to enable torque, apply motor settings, calibrate when needed, and move to neutral. +4. Command joint-space poses through `set_joint_positions(...)`. - You can also use **Poetry**, **pyenv**, **conda**, or any other environment manager if you prefer. +## Install -2. **Install dependencies**: - - ```sh - pip install -e . - ``` +```sh +python -m venv .venv +source .venv/bin/activate +pip install -e . +``` -3. **Check the configuration file**: +## Quick Start - - Review the config file (e.g., `orca_core/orca_core/models/orcahand_v1_right/config.yaml`) and make sure it matches your hardware setup. +Start by checking the model config you want to use. The bundled models live under: -4. **Run the tension and calibration scripts**: +- `orca_core/models/v2/orcahand_right/config.yaml` +- `orca_core/models/v1/orcahand_right/config.yaml` +- `orca_core/models/v1/orcahand_left/config.yaml` - ```sh - python scripts/tension.py orca_core/models/orcahand_v1_right - python scripts/calibrate.py orca_core/models/orcahand_v1_right - ``` +The refactored code expects `config_path` to point to the `config.yaml` file itself, not just the model directory. +Use the `v2` config as the canonical reference for the current schema. Older `v1` configs may still need the legacy `calib_*` keys renamed to `calibration_*`. - Replace the path with your specific hand model folder if needed. +For a first-time hardware bring-up, the recommended operator flow is: -5. **Move the hand to the neutral position**: +```sh +python scripts/setup.py orca_core/models/v2/orcahand_right/config.yaml +``` - ```sh - python scripts/neutral.py orca_core/models/orcahand_v1_right - ``` +If the hand is already assembled and tensioned, the shorter path is: -6. **Example usage: test.py** +```sh +python scripts/calibrate.py orca_core/models/v2/orcahand_right/config.yaml +python scripts/neutral.py orca_core/models/v2/orcahand_right/config.yaml +``` - Here is a minimal example script you can use to test your setup: +## Python Usage - ```python - from orca_core import OrcaHand - import time +```python +from orca_core import OrcaHand - hand = OrcaHand('orca_core/models/orcahand_v1_right') - status = hand.connect() - print(status) - if not status[0]: - print("Failed to connect to the hand.") - exit(1) +hand = OrcaHand(config_path="orca_core/models/v2/orcahand_right/config.yaml") - hand.enable_torque() +success, message = hand.connect() +if not success: + raise RuntimeError(message) - joint_dict = { - "index_mcp": 90, - "middle_pip": 30, - } +try: + hand.init_joints() - hand.set_joint_pos(joint_dict, num_steps=25, step_size=0.001) + neutral = hand.config.neutral_position + hand.set_joint_positions( + { + "thumb_mcp": neutral["thumb_mcp"] + 10, + "index_mcp": neutral["index_mcp"] + 10, + "middle_mcp": neutral["middle_mcp"] + 10, + }, + num_steps=25, + step_size=0.01, + ) - time.sleep(2) - hand.disable_torque() + print(hand.get_joint_position().as_dict()) +finally: + hand.stop_task() hand.disconnect() - ``` +``` + +### Notes on units ---- +- Joint-space values use the same units as `joint_roms` and `neutral_position` in your model config. The bundled hand configs use degrees. +- Low-level motor position telemetry returned by `get_motor_pos()` is in radians. +- Motor current is reported in mA and motor temperature in C. + +## Key Scripts + +- `scripts/setup.py`: guided tension -> calibrate -> verify workflow. +- `scripts/calibrate.py`: run calibration and persist `calibration.yaml`. +- `scripts/tension.py`: hold the spools in place for tendon tensioning. +- `scripts/neutral.py`: move to the configured neutral pose. +- `scripts/zero.py`: move all configured joints to zero. +- `scripts/record_angles.py` / `scripts/replay_angles.py`: capture and replay waypoint-based motions. +- `scripts/record_continuous.py` / `scripts/replay_continuous.py`: capture and replay continuous trajectories. +- `scripts/slider_joint.py` / `scripts/slider_motor.py`: manual debugging UIs. + +## Configuration and calibration files + +Each hand model directory contains: + +- `config.yaml`: static model and hardware metadata. +- `calibration.yaml`: calibration output written after calibration runs. + +The refactored config loader expects canonical calibration keys such as: + +- `calibration_current` +- `calibration_step_size` +- `calibration_step_period` +- `calibration_threshold` +- `calibration_num_stable` +- `calibration_sequence` + +If you are migrating an older config that still uses `calib_*` keys, rename those fields before using it with the refactored API. ## Troubleshooting -### Serial Port Permissions (Linux) +### Serial port permissions on Linux -On Linux, the serial port (e.g., `/dev/ttyUSB0`) is owned by the `dialout` group. If your user is not in this group, you will get a **permission denied** error and motors won't be detected. +On Linux, the serial port is usually owned by the `dialout` group. If your user is not in that group, the hand may fail to connect. -**Permanent fix** (requires re-login): +Permanent fix: ```sh sudo usermod -aG dialout $USER ``` -**Temporary fix** (resets on reboot/replug): +Temporary fix: ```sh sudo chmod 666 /dev/ttyUSB0 ``` -### Serial Port Path - -Make sure the `port` field in your `config.yaml` matches your operating system: +### Port mismatch -| OS | Example port | -|-------|---------------------------------| -| Linux | `/dev/ttyUSB0` | -| macOS | `/dev/tty.usbserial-XXXXXXXX` | +`OrcaHand.connect()` first tries the configured port, then attempts USB auto-detection, then falls back to an interactive chooser. If connection succeeds on a different port, the package updates `config.yaml` automatically. ---- +## Documentation -**Note:** -- Always ensure your `config.yaml` matches your hardware and wiring. -- All scripts in the `scripts/` folder take the model path as their first argument. -- For more advanced usage, see the other scripts and the API documentation. +The MkDocs site in `docs/` expands on: ---- +- hardware setup +- the `config.yaml` schema +- calibration and tensioning workflow +- the package architecture +- the current Python API diff --git a/docs/index.md b/docs/index.md index 205eb437..0ff5eb8e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,10 +1,34 @@ -# About Orca +# ORCA Core -## Welcome to the Orca Hand Core Documentation +`orca_core` is the software layer that turns an ORCA Hand model description and calibration state into a controllable hardware interface. -This site provides all the necessary information to get started with Orca Hand Software. Here you'll find instructions covering software of the core API for controlling the hand as well initial instructions for setting everything up. +The codebase is now organized around a few explicit concepts: -Whether you're configuring your first system or extending its capabilities, this documentation is intended to support development and experimentation with the control software. +- `OrcaHandConfig` is the static description of a hand model, loaded from `config.yaml`. +- `CalibrationResult` is the mutable calibration state, loaded from `calibration.yaml`. +- `BaseHand` defines the shared joint-space interface. +- `OrcaHand` is the hardware-backed implementation used for real hands. +- `MotorClient` implementations handle the concrete motor bus protocol. -!!! Info - To ensure the hand functions as intended, we strongly recommend following the official guide on the [ORCA Hand Official site](https://orcahand.com) closely. \ No newline at end of file +If you are new to the project, the shortest useful path through the docs is: + +1. Set up the motor chain and choose the right model config. +2. Review the `config.yaml` schema and confirm it matches your hardware. +3. Run the setup or calibration workflow. +4. Use `OrcaHand` for joint-space control. + +## What changed in the refactor + +The older docs described a more script-centric package and used several outdated names. The current codebase is centered on: + +- explicit config and calibration data objects +- a backend-agnostic `BaseHand` +- a hardware-specific `OrcaHand` +- typed joint payloads via `OrcaJointPositions` +- canonical config keys such as `calibration_sequence` instead of `calib_sequence` + +## Recommended first steps + +- Read the getting-started pages if you are bringing up hardware. +- Read the API page if you are integrating `orca_core` into another Python project. +- Read the repository structure page if you want the high-level architecture before diving into the source. diff --git a/docs/pages/getting-started-docs/initial-tensioning-and-calibration.md b/docs/pages/getting-started-docs/initial-tensioning-and-calibration.md index 3c63ee89..9b8b7e06 100644 --- a/docs/pages/getting-started-docs/initial-tensioning-and-calibration.md +++ b/docs/pages/getting-started-docs/initial-tensioning-and-calibration.md @@ -1,38 +1,111 @@ -# Calibration and Tensioning +# Tensioning And Calibration -## Calibration +This page documents the current maintenance workflow used by the refactored package. -Calibration ensures the hand operates with high accuracy and precision. It should be performed regularly, especially: +## Two files, two roles -- After extended use -- After slack has built up in the tendons -- After tensioning, to restore precision +The bring-up flow depends on two YAML files: -The calibration sequence is defined step-by-step in the `config.py` file, as explained in the [**Setting-up Config**](setting-up-config.md) guide. +- `config.yaml` stores the static hand description and calibration procedure. +- `calibration.yaml` stores the results of running that procedure on a specific physical hand. -**Be careful when modifying calibration steps**, some are intended to run independently and may not function correctly if grouped or reordered. +`OrcaHand` loads both: -## Tensioning +- `OrcaHandConfig` from `config.yaml` +- `CalibrationResult` from `calibration.yaml` -Tendons should be firm (not slack), but **not overtightened**. A small amount of give is acceptable. +## What calibration does -To tension the hand: +`hand.calibrate()` walks through the configured `calibration_sequence`, drives joints toward their mechanical limits, and writes the resulting values back to `calibration.yaml`. -1. Move all servos **fully counter-clockwise (CCW)**. Instead of moving all servos by hand you can also run `tension.py --move_motors` that turns all servos CCW using the calibration current specified in `config.py`. -2. Run the `tension.py` script located in the `scripts` folder. This locks the servos in place and secures the bottom spools. -3. Using the included 3D-printed **ratchet** (found in the spools print file or included in the kit), rotate the **top spool of each servo clockwise** to tighten the tendons. -4. As you turn, you should **hear or feel a "clicking" sound**, this confirms the ratchet is engaged and the tendon is being tensioned. +The stored result includes: -> **Careful:** It’s easy to overtension the tendons when using the ratchet. Overtensioning can interfere with performance. Tighten **only until the tendon feels firm**. +- per-motor lower and upper limits +- per-motor joint-to-motor ratios +- a `calibrated` flag +- a `wrist_calibrated` flag -## Initial Calibration and Tensioning (After Assembly) +Partial progress is written during calibration, so an interrupted run is not fully lost. -When the hand is first assembled, because tendons are spooled by hand, significant slack is introduced. This must be corrected before using the hand for other tasks. +## What `init_joints()` does -Recommended steps: +For most application code, you should prefer: -1. Run calibration (for all joints). Significant slack will accumulate during this step, this is expected, and calibration may seem ineffective at first. -2. Perform the tensioning procedure as described above. -3. Repeat steps 1 and 2 **1-2 more times**, or until **no additional slack** appears after calibration completes. +```python +hand.init_joints() +``` -This ensures the tendons are properly tensioned and the hand is correctly calibrated. +instead of manually calling each bring-up step. + +`init_joints()`: + +1. enables torque +2. applies the configured control mode +3. applies the configured current limit +4. calibrates if the hand is not yet calibrated +5. computes wrap offsets +6. moves to neutral + +## Recommended workflow after assembly + +The easiest operator path is the guided script: + +```sh +python scripts/setup.py orca_core/models/v2/orcahand_right/config.yaml +``` + +That workflow repeats tensioning and calibration with motion checks in between. + +## Manual workflow + +If you want to run the steps yourself: + +1. Tension the hand: + +```sh +python scripts/tension.py orca_core/models/v2/orcahand_right/config.yaml --move_motors +``` + +2. Run calibration: + +```sh +python scripts/calibrate.py orca_core/models/v2/orcahand_right/config.yaml +``` + +3. Move to neutral: + +```sh +python scripts/neutral.py orca_core/models/v2/orcahand_right/config.yaml +``` + +## Tensioning guidance + +The goal is to remove slack without overtightening. + +Typical procedure: + +1. Use `tension.py --move_motors` to drive the spools into a useful starting state. +2. Let `tension.py` hold the motors in place. +3. Tighten the top spools gradually with the mechanical tool. +4. Stop as soon as the tendons feel firm and consistent. + +Too much tension can hurt motion quality and calibration quality. + +## When to recalibrate + +Run calibration again when: + +- the hand has been re-strung +- tendon slack has changed noticeably +- the wrist has been serviced +- joint tracking no longer matches expectation + +## Wrist calibration behavior + +The wrist is handled slightly differently: + +- wrist calibration status is tracked separately +- `--force-wrist` can force wrist recalibration +- if wrist steps are missing from the sequence, the code can append them when needed + +This lets you skip unnecessary wrist passes during routine finger recalibration while still supporting a full bring-up when required. diff --git a/docs/pages/getting-started-docs/quickstart-with-core-package.md b/docs/pages/getting-started-docs/quickstart-with-core-package.md index 6c8605ff..b54036b6 100644 --- a/docs/pages/getting-started-docs/quickstart-with-core-package.md +++ b/docs/pages/getting-started-docs/quickstart-with-core-package.md @@ -1,77 +1,125 @@ -Orca Core is the core control package of the ORCA Hand. It's used to abstract hardware, provide scripts for calibration and tensioning and to control the hand with simple high-level control methods in joint space. +# Quickstart With `orca_core` -## Get Started +This page covers the current happy path for bringing up an ORCA Hand with the refactored package. -To get started with Orca Core, follow these steps: +## 1. Install the package -1. **Create a virtual environment** (recommended): +```sh +python -m venv .venv +source .venv/bin/activate +pip install -e . +``` - ```sh - python -m venv venv - source venv/bin/activate - ``` +## 2. Choose the hand model - You can also use **Poetry**, **pyenv**, **conda**, or any other environment manager if you prefer. +Pick the `config.yaml` file that matches your hardware. Bundled examples include: -2. **Install dependencies**: +- `orca_core/models/v2/orcahand_right/config.yaml` +- `orca_core/models/v1/orcahand_right/config.yaml` +- `orca_core/models/v1/orcahand_left/config.yaml` - ```sh - pip install -e . - ``` +`OrcaHand(config_path=...)` expects the full path to `config.yaml`. +For the current canonical schema, use the `v2` config as your reference. Older `v1` configs may still need the `calib_*` keys migrated to `calibration_*`. -3. **Check the configuration file**: +## 3. Check the config before moving hardware - - Review the config file (e.g., `orca_core/orca_core/models/orcahand_v1_right/config.yaml`) and make sure it matches your hardware setup. +At minimum, confirm: -4. **Run the tension and calibration scripts**: +- `port` matches your machine +- `motor_type` matches your motor chain +- `motor_ids` and `joint_to_motor_map` match the physical build +- `neutral_position` is reasonable for your assembly +- calibration keys use the canonical refactored names such as `calibration_current` and `calibration_sequence` - ```sh - python scripts/tension.py orca_core/orca_core/models/orcahand_v1_right/config.yaml - python scripts/calibrate.py orca_core/orca_core/models/orcahand_v1_right/config.yaml - ``` +## 4. Run the setup flow - Replace the path with your specific `config.yaml` file if needed. +For first-time bring-up or after re-stringing / major maintenance, use the guided workflow: -5. **Move the hand to the neutral position**: +```sh +python scripts/setup.py orca_core/models/v2/orcahand_right/config.yaml +``` - ```sh - python scripts/neutral.py orca_core/orca_core/models/orcahand_v1_right/config.yaml - ``` +This script walks through repeated rounds of: -6. **Example usage: test.py** +- tensioning +- calibration +- neutral positioning +- motion verification - Here is a minimal example script you can use to test your setup: +If the hand is already assembled and only needs a fresh calibration: - ```python - from orca_core import OrcaHand - import time +```sh +python scripts/calibrate.py orca_core/models/v2/orcahand_right/config.yaml +python scripts/neutral.py orca_core/models/v2/orcahand_right/config.yaml +``` - hand = OrcaHand('orca_core/orca_core/models/orcahand_v1_right/config.yaml') - status = hand.connect() - print(status) - if not status[0]: - print("Failed to connect to the hand.") - exit(1) +## 5. Minimal Python example - hand.enable_torque() +```python +from orca_core import OrcaHand - joint_dict = { - "index_mcp": 90, - "middle_pip": 30, - } +hand = OrcaHand(config_path="orca_core/models/v2/orcahand_right/config.yaml") - hand.set_joint_positions(joint_dict, num_steps=25, step_size=0.001) +success, message = hand.connect() +if not success: + raise RuntimeError(message) - time.sleep(2) - hand.disable_torque() +try: + hand.init_joints() + + neutral = hand.config.neutral_position + hand.set_joint_positions( + { + "thumb_mcp": neutral["thumb_mcp"] + 10, + "index_mcp": neutral["index_mcp"] + 8, + "middle_mcp": neutral["middle_mcp"] + 8, + }, + num_steps=25, + step_size=0.01, + ) +finally: + hand.stop_task() hand.disconnect() - ``` +``` + +## 6. Understand the main lifecycle + +The recommended runtime sequence is: + +1. `hand = OrcaHand(...)` +2. `hand.connect()` +3. `hand.init_joints()` +4. `hand.set_joint_positions(...)` +5. `hand.disconnect()` + +`init_joints()` is the main convenience entrypoint. It: + +- enables torque +- applies the configured control mode +- applies the configured current limit +- calibrates if needed +- computes wrap offsets +- moves the hand to neutral + +## 7. Units and command shapes + +- Joint-space commands accept `OrcaJointPositions`, `dict[str, float]`, or a 1-D numpy array. +- Joint-space values use the same units as `joint_roms` in the config. The bundled configs use degrees. +- Raw motor position telemetry is reported in radians. + +## 8. Common helper scripts + +- `scripts/tension.py`: hold spools in place during tendon tensioning +- `scripts/zero.py`: move every joint to zero +- `scripts/slider_joint.py`: debug joint-space control +- `scripts/slider_motor.py`: debug motor-space control + +## 9. Linux serial permissions ---- +If the hand fails to connect on Linux because of serial permissions: -**Note:** -- Always ensure your `config.yaml` matches your hardware and wiring. -- All scripts in the `scripts/` folder take the `config.yaml` path as their first argument. -- For more advanced usage, see the other scripts and the API documentation. +```sh +sudo usermod -aG dialout $USER +``` ---- +Re-log after changing group membership. diff --git a/docs/pages/getting-started-docs/setting-up-config.md b/docs/pages/getting-started-docs/setting-up-config.md index 593715b2..bceadb1a 100644 --- a/docs/pages/getting-started-docs/setting-up-config.md +++ b/docs/pages/getting-started-docs/setting-up-config.md @@ -1,156 +1,150 @@ -# Setting Up Config +# Setting Up `config.yaml` -Learn how to configure your ORCA Hand system settings. The primary configuration for the ORCA Hand is managed through the `config.yaml` file located in the model-specific directory (e.g., `orca_core/models/orcahand_v1_right/config.yaml`). +`config.yaml` is the static description of a specific ORCA Hand build. The refactored loader turns it into an immutable `OrcaHandConfig` object, validates it, and uses it to drive every later stage of the workflow. -This file defines parameters crucial for the hand's operation, including communication settings, motor and joint mappings, movement ranges, and calibration procedures. +The most important rule is simple: make the config describe the hardware you actually assembled. -!!! Warning - Be careful when editing this file, incorrect settings can cause unexpected behavior and make debugging more difficult. +## Where the file lives ---- +Each model directory contains a `config.yaml`, for example: -## `config.yaml` Structure Overview +- `orca_core/models/v2/orcahand_right/config.yaml` +- `orca_core/models/v1/orcahand_left/config.yaml` -### 1. General Settings +The `v2` model is the clearest example of the current canonical schema. Some older `v1` configs in the repository predate the calibration key rename and may need a small migration. -```yaml -version: 0.2.0 -baudrate: 3000000 -port: /dev/ttyUSB0 -max_current: 400 -type: right -control_mode: current_based_position -``` +When you construct `OrcaHand`, pass the path to the file itself: -**What should be changed?** +```python +from orca_core import OrcaHand -You should change the `port` to match your system (Linux or macOS). Change `type` to right or left depending on the hand assembly you have. `max_current` is set to a value found to be sufficient; you can adjust it depending on the needs of your tasks. The `baudrate` or `control_mode` should not be changed based on the current implementation in the repo. If you decide to change them you have to adapt the code accordingly. - ---- - -### 2. Motor and Joint Id's - -```yaml -motor_ids: [1, 2, 3, ..., 17] -joint_ids: [thumb_mcp, thumb_abd, ..., wrist] +hand = OrcaHand(config_path="orca_core/models/v2/orcahand_right/config.yaml") ``` -**What should be changed?** +## Current schema -If you ID'ed the servos as per our recommendation you should change nothing here. - ---- - -### 3. Joint to Motor Mapping and Inversion +The refactored code expects the following top-level fields: ```yaml -joint_to_motor_map: - thumb_mcp: -4 - thumb_abd: -3 - ... - wrist: 17 -``` - -**What should be changed?** - -This section defines which motor (by ID) controls each joint and how the sign of the motor ID affects its rotation direction. - -* The number is the **motor ID**. -* The **sign** is determined by direction of rotation when flexing a joint. +version: 0.2.1 +type: right +port: /dev/ttyUSB0 +baudrate: 1000000 +motor_type: dynamixel +max_current: 300 +control_mode: current_based_position -#### Hand-Specific Guidelines +motor_ids: [1, 2, 3, 4] +joint_ids: [wrist, thumb_cmc, thumb_abd, thumb_mcp] -Flexion decides the sign of an servo ID. +joint_to_motor_map: + wrist: -1 + thumb_cmc: 17 + thumb_abd: 14 + thumb_mcp: 15 - * If **flexion** moves the servo **CCW** then a **no sign** should be included to the motor ID. +joint_roms: + wrist: [-65, 35] + thumb_cmc: [-45, 33] - * If **flexion** moves the servo **CW** then a **negative sign** should be included to the motor ID. +neutral_position: + wrist: -8 + thumb_cmc: 0 + +calibration_current: 300 +wrist_calibration_current: 100 +calibration_step_size: 0.15 +calibration_step_period: 0.0001 +calibration_threshold: 0.01 +calibration_num_stable: 10 +calibration_sequence: + - step: 1 + joints: + thumb_cmc: flex +``` -**Right hand assembly:** Most joints flexions lead to **CW** rotation so mostly negative signs are found. +## What each section means -**Left hand assembly:** Most joints flexions lead to **CCW** rotation so mostly none/positive signs are found. +### Identity and communication -If tendon routing is done properly, all joints should have the same sign, except for `index_abd`, `middle_abd` and `pinky_abd` joints. +- `type`: left or right hand assembly +- `port`: serial device path +- `baudrate`: bus baudrate +- `motor_type`: currently selects the concrete motor backend -> **Note:** For abduction joints, flexion refers to movement **away from the thumb** and for the thumb flexion is **towards the fingers**. +These fields determine how `OrcaHand.connect()` talks to the motor bus. ---- +### Motor and joint layout +- `motor_ids`: the motor IDs expected on the bus +- `joint_ids`: the logical joint order for the hand model +- `joint_to_motor_map`: which motor drives which joint -For example: +`joint_to_motor_map` uses a signed motor ID convention: -```yaml -joint_to_motor_map: - thumb_mcp: -4 # thumb_mcp flexion moves the motor with ID 4 CW - index_abd: 14 # index_abd flexion moves the motor with ID 14 CCW -``` +- absolute value = physical motor ID +- negative sign = joint is inverted relative to the positive flexion convention ---- +Internally, the loader converts this into: -### 4. Joint Range of Motion (ROM) +- a normalized joint-to-motor map with absolute motor IDs +- a separate `joint_inversion_dict` -```yaml -joint_roms: - thumb_mcp: [-50, 50] - ... - wrist: [-50, 30] -``` +### Joint range and neutral pose -**What should be changed?** +- `joint_roms`: minimum and maximum joint values for each joint +- `neutral_position`: the default pose used by `set_neutral_position()` and `init_joints()` -Unless you have modified the design, you should not change the values. +The bundled configs use degrees for these values, and joint-space commands should use the same units. ---- +### Calibration settings -### 5. Neutral Position +These fields govern the automatic calibration routine: -```yaml -neutral_position: - thumb_mcp: -13 - ... - wrist: 0 -``` +- `calibration_current` +- `wrist_calibration_current` +- `calibration_step_size` +- `calibration_step_period` +- `calibration_threshold` +- `calibration_num_stable` +- `calibration_sequence` -**What should be changed?** +`calibration_sequence` is a list of steps. Each step names one or more joints and a direction (`flex` or `extend`). -You can adjust this section if you want the hand to return to a different default pose. +## Migration from older config keys ---- +Older configs and older docs often used `calib_*` names. The refactored code now expects canonical names. -### 6. Calibration Parameters +Use this mapping when migrating an older config: -```yaml -calib_current: 350 -calib_step_size: 0.1 -calib_step_period: 0.001 -calib_num_stable: 10 -calib_threshold: 0.01 -``` +| Old key | New key | +| --- | --- | +| `calib_current` | `calibration_current` | +| `calib_step_size` | `calibration_step_size` | +| `calib_step_period` | `calibration_step_period` | +| `calib_threshold` | `calibration_threshold` | +| `calib_num_stable` | `calibration_num_stable` | +| `calib_sequence` | `calibration_sequence` | -**What should be changed?** +## What you should usually edit -These parameters should generally not be changed unless you have experience tuning the calibration behavior for a specific hardware modification. +In a normal bring-up, the most common edits are: ---- +- `port` +- `motor_type` +- `joint_to_motor_map` +- sometimes `neutral_position` -### 7. Calibration Sequence +You should only edit calibration tuning parameters if you know why the defaults are not working for your build. -```yaml -calib_sequence: - - step: 1 - joints: - thumb_mcp: flex - - step: 2 - joints: - thumb_mcp: extend - - step: 3 - joints: - thumb_abd: flex - ... -``` +## Validation rules worth knowing -**What should be changed?** +The loader validates that: -If you want to specify the sequence or calibrate only specific joints you can adapt the sequence. If you are unsure, leave this section as is. An incomplete or incorrect sequence may lead to errors when executing commands later. +- every configured joint has a ROM +- every ROM entry corresponds to a known joint +- the number of joints, motor IDs, and mappings line up +- the configured control mode is supported +- calibration sequence entries refer to valid joints and directions ---- \ No newline at end of file +This means config mistakes are usually caught early, before motion commands run. diff --git a/docs/pages/getting-started-docs/setting-up-dynamixels.md b/docs/pages/getting-started-docs/setting-up-dynamixels.md index 44a82562..e1661036 100644 --- a/docs/pages/getting-started-docs/setting-up-dynamixels.md +++ b/docs/pages/getting-started-docs/setting-up-dynamixels.md @@ -1,78 +1,64 @@ -# Setting Up the Dynamixel Servos +# Setting Up Dynamixel Motors -This guide walks you through preparing your Dynamixel servos for use with the Orca hand control system. +This page covers the hardware preparation required before `orca_core` can talk to a Dynamixel-based ORCA Hand. -## Prerequisites +## Goal -### **Install the Dynamixel Wizard** - Use the [Dynamixel Wizard 2.0](https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_wizard2/) to assign unique IDs and update motor settings. +Before running the Python package, make sure: ---- +- every motor has a unique ID +- every motor is on the expected baudrate +- the full chain is visible from the host machine -## Assigning Unique Motor IDs +## Recommended tool -Before using the motors, each one must have a **unique ID**. +Use [Dynamixel Wizard 2.0](https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_wizard2/) to inspect the bus, assign IDs, and verify communication. -### Steps: +## Assign unique IDs -1. **Connect Hardware** - - Connect the U2D2 to your PC using USB. - - Use a 3-pin cable to connect U2D2 to the Power Hub Board (PHB). - - Power the PHB with a 12V adapter. - - Connect one motor at a time to the PHB. - - Switch on power (red LED should light up). +1. Connect the USB adapter and power hardware. +2. Connect one motor at a time. +3. Scan in Dynamixel Wizard. +4. Assign a unique ID and label the motor physically. +5. Set the target baudrate for the whole chain. +6. Repeat until all motors are configured. -2. **Launch Dynamixel Wizard** - - Open the Wizard and go to **Options**. - - Set: - - Protocol: **2.0** - - Baudrate: **57600** and **3Mbps** - - ID Range: **0–17** - - Click **Scan**. Your motor should appear. +After that, connect the motors in the full daisy chain and verify they all appear on the bus together. -3. **Change the ID** - - In the table, select the ID row (Address 7). - - Assign a **unique ID** and label it physically on the motor. - - Also update the **baudrate** to **3Mbps**. +## Match the software config -4. **Repeat for All Motors** - - Connect motors one-by-one (daisy-chain after setting unique IDs). +Once hardware IDs are assigned, update the ORCA model config so that: ---- +- `motor_ids` matches the chain +- `joint_to_motor_map` matches the physical routing +- `baudrate` matches the configured motor baudrate -## Verifying All Motors +If these values do not match the actual chain, the higher-level hand API will not behave correctly. -After assigning IDs: +## Linux latency optimization -- Connect all motors in a daisy chain. -- Scan in the Dynamixel Wizard. -- Ensure all are detected correctly. +On Linux, FTDI-based USB serial adapters often default to a high latency timer. The current `DynamixelClient` and `FeetechClient` attempt to enable low-latency mode automatically on connect. ---- - -## USB Latency (Linux) - -FTDI USB-serial adapters (like the U2D2) default to 16ms latency on Linux, which limits motor control to ~30 Hz. The `DynamixelClient` and `FeetechClient` automatically enable low latency mode when connecting, achieving ~500 Hz control rates without any manual configuration. - -### Troubleshooting - -If you experience slow communication, verify the latency setting: +You can verify the adapter latency with: ```bash cat /sys/bus/usb-serial/devices/ttyUSB0/latency_timer -# Should output: 1 (after connecting with DynamixelClient) ``` -If low latency mode isn't being set automatically, you can create a udev rule: +After the client enables low latency mode, this should typically read `1`. + +If your platform or adapter does not persist that setting, you can install a udev rule: ```bash -# Create the udev rule sudo tee /etc/udev/rules.d/99-usb-serial-low-latency.rules <<< 'ACTION=="add", SUBSYSTEM=="usb-serial", DRIVER=="ftdi_sio", ATTR{latency_timer}="1"' - -# Reload rules and replug the adapter sudo udevadm control --reload-rules ``` ---- +## After the hardware scan passes + +Once all motors are visible and the config matches the chain, continue with: -Now you're ready to put your servos in the hand tower! \ No newline at end of file +1. config review +2. tensioning +3. calibration +4. neutral positioning diff --git a/docs/pages/orca-core-docs/motor-client-api.md b/docs/pages/orca-core-docs/motor-client-api.md index 45212673..3bdd3313 100644 --- a/docs/pages/orca-core-docs/motor-client-api.md +++ b/docs/pages/orca-core-docs/motor-client-api.md @@ -1,572 +1,46 @@ -# Motor Client API +# Motor Client Layer -The motor client system provides a unified interface for controlling different motor types (Dynamixel, Feetech) in the ORCA Hand. +The motor client layer is the hardware abstraction underneath `OrcaHand`. -## Architecture +Most users do not need to interact with it directly, but it matters if you are: -```mermaid -graph TB - subgraph "Motor Client Hierarchy" - ABC[MotorClient
Abstract Base Class] - DYN[DynamixelClient
Dynamixel Protocol v2] - FEE[FeetechClient
SCServo Protocol] - MOCK[MockDynamixelClient
Testing] - end +- adding support for a new motor family +- debugging low-level bus behavior +- comparing protocol-specific performance - subgraph "Core Integration" - ORCA[OrcaHand API
core.py] - end +## Role in the architecture - subgraph "Hardware" - DYN_HW[Dynamixel Motors
XL330, XC330, etc.] - FEE_HW[Feetech Motors
STS3032, SMS, etc.] - end +`OrcaHand` owns hand-level behavior such as calibration, neutral positioning, and joint-space commands. - ABC --> DYN - ABC --> FEE - ABC --> MOCK +`MotorClient` implementations own protocol-level behavior such as: - ORCA --> ABC +- opening the serial bus +- reading motor position / velocity / current +- reading temperatures +- enabling torque +- writing desired position or current +- setting operating modes - DYN --> DYN_HW - FEE --> FEE_HW +## Current implementations - style ABC fill:#9C27B0,stroke:#6A1B9A,color:#fff - style ORCA fill:#4CAF50,stroke:#2E7D32,color:#fff - style DYN fill:#2196F3,stroke:#1565C0,color:#fff - style FEE fill:#FF9800,stroke:#E65100,color:#fff -``` +- `DynamixelClient` +- `FeetechClient` +- `MockDynamixelClient` for tests and in-memory workflows -## Quick Start +The specific implementation is chosen from `config.motor_type`. -```python -# Using Feetech motors directly -from orca_core.hardware.feetech_client import FeetechClient +## What stays above this layer -client = FeetechClient( - motor_ids=[1, 2, 3, 4, 5], - port="/dev/ttyUSB0", - baudrate=1000000 -) -client.connect() +These responsibilities stay in `OrcaHand`, not in the motor client: -# Read motor state -positions, velocities, currents = client.read_pos_vel_cur() -print(f"Position: {positions}") +- converting between joint space and motor space +- interpreting signed joint mappings +- loading and applying calibration results +- clamping joint commands against configured ROMs +- coordinating higher-level tasks such as tensioning and jitter -# Write position -import numpy as np -client.write_desired_pos([1, 2], np.array([1.57, 3.14])) # radians +## Extension contract -client.disconnect() -``` +If you are implementing a new backend, `MotorClient` is the interface to follow. ---- - -## Class: MotorClient (Abstract Base) - -`MotorClient` defines the interface that all motor implementations must follow. - -```python -from orca_core.hardware.motor_client import MotorClient -``` - -### Abstract Methods - -| Method | Description | -|--------|-------------| -| `connect()` | Establish connection to motors | -| `disconnect()` | Close connection | -| `is_connected` | Property: connection status | -| `set_torque_enabled()` | Enable/disable motor torque | -| `set_operating_mode()` | Set control mode | -| `read_pos_vel_cur()` | Read position, velocity, current | -| `read_temperature()` | Read motor temperatures | -| `write_desired_pos()` | Command target positions | -| `write_desired_current()` | Command target currents | - -### Operating Modes - -| Mode | Value | Description | -|------|-------|-------------| -| Current | 0 | Direct current/torque control | -| Velocity | 1 | Velocity control | -| Position | 3 | Position control | -| Multi-turn | 4 | Extended position control | -| Current-based Position | 5 | Position with current limit | - ---- - -## Class: FeetechClient - -Client for Feetech SMS/STS series servo motors. - -```python -from orca_core.hardware.feetech_client import FeetechClient -``` - -### Constructor - -```python -FeetechClient( - motor_ids: Sequence[int], - port: str = '/dev/ttyUSB0', - baudrate: int = 1000000, - lazy_connect: bool = False, - pos_scale: Optional[float] = None, - vel_scale: Optional[float] = None, - cur_scale: Optional[float] = None, -) -``` - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `motor_ids` | Sequence[int] | required | List of motor IDs to control | -| `port` | str | `/dev/ttyUSB0` | Serial port path | -| `baudrate` | int | 1000000 | Communication baud rate | -| `lazy_connect` | bool | False | Auto-connect on first operation | -| `pos_scale` | float | 2π/4095 | Position conversion (rad/unit) | -| `vel_scale` | float | 0.732 | Velocity conversion (rpm/unit) | -| `cur_scale` | float | 6.5 | Current conversion (mA/unit) | - -### Connection Methods - -#### connect() - -```python -connect() -> None -``` - -Connect to motors and enable torque. - -**Raises:** -- `RuntimeError`: If already connected -- `OSError`: If serial port cannot be opened - -**Example:** -```python -client = FeetechClient([1, 2, 3], port="/dev/ttyUSB0") -client.connect() -print(f"Connected: {client.is_connected}") -``` - ---- - -#### disconnect() - -```python -disconnect() -> None -``` - -Disable torque and close connection. Safe to call multiple times. - ---- - -#### is_connected - -```python -@property -is_connected -> bool -``` - -Returns `True` if connected to motors. - ---- - -### Torque Control - -#### set_torque_enabled() - -```python -set_torque_enabled( - motor_ids: Sequence[int], - enabled: bool, - retries: int = -1, - retry_interval: float = 0.25 -) -> None -``` - -Enable or disable torque for specified motors. - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `motor_ids` | Sequence[int] | required | Motor IDs to configure | -| `enabled` | bool | required | True to enable, False to disable | -| `retries` | int | -1 | Retry count (-1 = infinite) | -| `retry_interval` | float | 0.25 | Seconds between retries | - -**Example:** -```python -# Enable torque for motors 1-3 -client.set_torque_enabled([1, 2, 3], True) - -# Disable torque with limited retries -client.set_torque_enabled([1, 2, 3], False, retries=3) -``` - ---- - -### Operating Mode - -#### set_operating_mode() - -```python -set_operating_mode(motor_ids: Sequence[int], mode: int) -> None -``` - -Set the operating mode for motors. - -Feetech servos have two native modes: -- Mode 0: Servo (position control) -- Mode 1: Wheel (velocity control) - -This method maps Dynamixel mode values to Feetech equivalents: - -| Input Mode | Feetech Mode | Behavior | -|------------|--------------|----------| -| 0 (current) | 0 (servo) | Position with torque limit | -| 1 (velocity) | 1 (wheel) | Continuous rotation | -| 3 (position) | 0 (servo) | Standard position | -| 5 (current-based) | 0 (servo) | Position with torque limit | - -**Example:** -```python -# Set to position mode -client.set_operating_mode([1, 2, 3], 3) - -# Set to velocity mode -client.set_operating_mode([4, 5], 1) -``` - ---- - -### Reading State - -#### read_pos_vel_cur() - -```python -read_pos_vel_cur() -> Tuple[np.ndarray, np.ndarray, np.ndarray] -``` - -Read position, velocity, and current for all motors. - -**Returns:** Tuple of three numpy arrays: -- `positions`: Motor positions in radians -- `velocities`: Motor velocities in rpm -- `currents`: Motor currents in mA - -Arrays are ordered by `motor_ids` from constructor. - -**Example:** -```python -positions, velocities, currents = client.read_pos_vel_cur() -for i, motor_id in enumerate(client.motor_ids): - print(f"Motor {motor_id}: pos={positions[i]:.2f} rad, " - f"vel={velocities[i]:.1f} rpm, cur={currents[i]:.1f} mA") -``` - ---- - -#### read_temperature() - -```python -read_temperature() -> np.ndarray -``` - -Read temperature for all motors. - -**Returns:** Array of temperatures in degrees Celsius. - -**Example:** -```python -temps = client.read_temperature() -for i, motor_id in enumerate(client.motor_ids): - print(f"Motor {motor_id}: {temps[i]:.0f}°C") -``` - ---- - -### Writing Commands - -#### write_desired_pos() - -```python -write_desired_pos( - motor_ids: Sequence[int], - positions: np.ndarray -) -> None -``` - -Command target positions for specified motors. - -| Parameter | Type | Description | -|-----------|------|-------------| -| `motor_ids` | Sequence[int] | Motors to command | -| `positions` | np.ndarray | Target positions in radians | - -**Raises:** -- `ValueError`: If `motor_ids` and `positions` have different lengths - -**Example:** -```python -import numpy as np - -# Move motors 1 and 2 to 90° and 180° -client.write_desired_pos([1, 2], np.array([np.pi/2, np.pi])) -``` - ---- - -#### write_desired_current() - -```python -write_desired_current( - motor_ids: Sequence[int], - currents: np.ndarray -) -> None -``` - -Set current/torque limits for specified motors. - -**Note:** Feetech servos don't have direct current control. This sets the torque limit via the Goal Time register. - -| Parameter | Type | Description | -|-----------|------|-------------| -| `motor_ids` | Sequence[int] | Motors to configure | -| `currents` | np.ndarray | Current limits in mA | - -**Example:** -```python -# Set 500mA torque limit for motors 1-3 -client.write_desired_current([1, 2, 3], np.array([500, 500, 500])) -``` - ---- - -### Context Manager - -FeetechClient supports context manager usage: - -```python -with FeetechClient([1, 2, 3], port="/dev/ttyUSB0") as client: - positions, _, _ = client.read_pos_vel_cur() - print(positions) -# Automatically disconnects -``` - ---- - -## Class: DynamixelClient - -Client for Dynamixel servo motors (Protocol v2). - -```python -from orca_core.hardware.dynamixel_client import DynamixelClient -``` - -### Constructor - -```python -DynamixelClient( - motor_ids: Sequence[int], - port: str = '/dev/ttyUSB0', - baudrate: int = 1000000, - lazy_connect: bool = False, - pos_scale: Optional[float] = None, - vel_scale: Optional[float] = None, - cur_scale: Optional[float] = None, -) -``` - -| Parameter | Type | Default | Description | -|-----------|------|---------|-------------| -| `motor_ids` | Sequence[int] | required | List of motor IDs to control | -| `port` | str | `/dev/ttyUSB0` | Serial port path | -| `baudrate` | int | 1000000 | Communication baud rate | -| `lazy_connect` | bool | False | Auto-connect on first operation | -| `pos_scale` | float | 2π/4096 | Position conversion (rad/unit) | -| `vel_scale` | float | 0.229×2π/60 | Velocity conversion | -| `cur_scale` | float | 1.34 | Current conversion (mA/unit) | - -### Additional Methods - -DynamixelClient includes all MotorClient methods plus: - -#### sync_write() - -```python -sync_write( - motor_ids: Sequence[int], - values: Sequence[Union[int, float]], - address: int, - size: int -) -> None -``` - -Write values to multiple motors simultaneously using sync write. - ---- - -#### write_profile_velocity() - -```python -write_profile_velocity( - motor_ids: Sequence[int], - profile_velocity: np.ndarray -) -> None -``` - -Set the profile velocity for position moves. - ---- - -#### read_status_is_done_moving() - -```python -read_status_is_done_moving() -> bool -``` - -Check if motors have finished moving to their target positions. - ---- - -## Data Flow - -```mermaid -sequenceDiagram - participant App - participant MotorClient - participant SDK - participant Hardware - - App->>MotorClient: connect() - MotorClient->>SDK: Open serial port - SDK->>Hardware: Establish communication - MotorClient->>SDK: Enable torque - SDK->>Hardware: Write TORQUE_ENABLE=1 - - App->>MotorClient: read_pos_vel_cur() - MotorClient->>SDK: Read position register - SDK->>Hardware: Request data - Hardware-->>SDK: Position/velocity/current bytes - MotorClient-->>App: (pos, vel, cur) arrays - - App->>MotorClient: write_desired_pos(ids, pos) - MotorClient->>SDK: Write position command - SDK->>Hardware: Send position packet - Hardware-->>SDK: ACK - - App->>MotorClient: disconnect() - MotorClient->>SDK: Disable torque - SDK->>Hardware: Write TORQUE_ENABLE=0 - MotorClient->>SDK: Close port -``` - ---- - -## Hardware Specifications - -### Feetech STS3032 - -| Specification | Value | -|---------------|-------| -| Operating Voltage | 6-12.6V DC | -| Stall Torque | 3.5 kg·cm @ 7.4V | -| No-load Speed | 0.14 sec/60° @ 7.4V | -| Position Range | 0-4095 (360°) | -| Position Resolution | ~0.088° | -| Communication | Half-duplex TTL | -| Baud Rate | Up to 1 Mbps | -| Feedback | Position, Speed, Current, Voltage, Temperature | - -### Dynamixel XL330 - -| Specification | Value | -|---------------|-------| -| Operating Voltage | 5-9V DC | -| Stall Torque | 0.65 N·m @ 7.4V | -| No-load Speed | 76 rpm @ 7.4V | -| Position Range | 0-4095 (360°) | -| Position Resolution | ~0.088° | -| Communication | Half-duplex TTL | -| Baud Rate | Up to 4 Mbps | -| Feedback | Position, Velocity, Current, Voltage, Temperature | - ---- - -## Complete Example - -```python -from orca_core.hardware.feetech_client import FeetechClient -import numpy as np -import time - -def main(): - # Initialize client - motor_ids = [1, 2, 3, 4, 5] - client = FeetechClient( - motor_ids=motor_ids, - port="/dev/ttyUSB0", - baudrate=1000000 - ) - - try: - # Connect - client.connect() - print(f"Connected to {len(motor_ids)} motors") - - # Read initial state - positions, velocities, currents = client.read_pos_vel_cur() - print(f"Initial positions: {np.degrees(positions)}") - - # Read temperatures - temps = client.read_temperature() - print(f"Temperatures: {temps}°C") - - # Move to center position (180°) - target = np.full(len(motor_ids), np.pi) - client.write_desired_pos(motor_ids, target) - print("Moving to center position...") - - # Wait and read final position - time.sleep(2.0) - positions, _, _ = client.read_pos_vel_cur() - print(f"Final positions: {np.degrees(positions)}") - - except OSError as e: - print(f"Connection error: {e}") - finally: - client.disconnect() - print("Disconnected") - -if __name__ == "__main__": - main() -``` - ---- - -## Troubleshooting - -### Connection Errors - -| Error | Cause | Solution | -|-------|-------|----------| -| "Failed to open port" | Port not found or in use | Check cable, verify port path | -| "Must call connect() first" | Operating without connection | Call `connect()` before operations | -| "Client is already connected" | Double connect | Check connection state first | - -### Communication Errors - -| Error | Cause | Solution | -|-------|-------|----------| -| Timeout on read | Motor not responding | Check power, ID, baud rate | -| Checksum error | Electrical interference | Shorten cables, add shielding | -| Wrong data values | Scale factor mismatch | Verify pos_scale, vel_scale | - -### Motor Behavior - -| Issue | Cause | Solution | -|-------|-------|----------| -| Motor doesn't move | Torque disabled | Call `set_torque_enabled()` | -| Jerky motion | Wrong acceleration | Adjust internal `_default_acc` | -| Position drift | Calibration needed | Recalibrate motor offsets | +::: orca_core.hardware.motor_client.MotorClient diff --git a/docs/pages/orca-core-docs/orca-core-scripts.md b/docs/pages/orca-core-docs/orca-core-scripts.md index ccb55b98..0d8c7159 100644 --- a/docs/pages/orca-core-docs/orca-core-scripts.md +++ b/docs/pages/orca-core-docs/orca-core-scripts.md @@ -1,255 +1,85 @@ -# Scripts API Documentation +# Scripts -This document provides an overview of the available scripts in the `scripts` folder. +The `scripts/` directory contains operator tools and debugging entrypoints built on top of the refactored package. -### Calibration Scripts +They are best understood as workflow helpers, not as the primary API surface. The primary API is the Python package itself. -
-calibrate.py +## Recommended scripts -Calibrates the ORCA Hand. This script reads the calibration sequence from the hand's configuration and applies it. +### Bring-up and maintenance -
Args:
- +| Script | Purpose | +| --- | --- | +| `setup.py` | Guided setup workflow: tension, calibrate, neutral, motion test, repeat | +| `calibrate.py` | Run calibration and write `calibration.yaml` | +| `tension.py` | Hold spool tension during tendon maintenance | +| `neutral.py` | Move to the configured neutral pose | +| `zero.py` | Move every configured joint to zero | -Example: -```bash -python scripts/calibrate.py /path/to/orcahand_v1_right/config.yaml -``` -
- -### Motor and Joint Check Scripts - -
-check_motor.py - -Checks a specific motor by setting its operating mode and enabling torque. It then incrementally changes the motor's target position and prints the current and target positions. This script is useful for testing individual motor functionality. - -
Args:
- - -Example: -```bash -python scripts/check_motor.py --motor_id 5 --port /dev/ttyUSB0 -``` -
- -### Demonstration Scripts - -
-main_demo.py - -Runs a demonstration of the ORCA Hand, making the fingers perform a wave-like motion. It initializes the hand, defines joint ranges, and then continuously updates joint positions to create the animation. - -
Args:
- - -Example: -```bash -python scripts/main_demo.py -``` -
+### Recording and replay -
-main_demo_abduction.py +| Script | Purpose | +| --- | --- | +| `record_angles.py` | Record waypoint-style poses | +| `replay_angles.py` | Replay waypoint-style motions | +| `record_continuous.py` | Record continuous joint trajectories | +| `replay_continuous.py` | Replay continuous trajectories | -Runs a demonstration of the ORCA Hand, similar to `main_demo.py`, but with a focus on abduction movements. It initializes the hand, defines joint ranges, and then continuously updates joint positions. +### Debugging and inspection -
Args:
- +| Script | Purpose | +| --- | --- | +| `slider_joint.py` | Joint-space debug UI | +| `slider_motor.py` | Motor-space debug UI | +| `check_motor.py` | Single-motor inspection tool | +| `test.py` | Manual hardware motion / monitoring script | +| `test_motor_latency.py` | Measure bus performance and optional Dynamixel optimizations | +| `jitter.py` | Apply a short oscillation to one motor for debugging | -Example: -```bash -python scripts/main_demo_abduction.py -``` -
+### Demos and experiments -### Position Control Scripts +These scripts are useful examples but are less central than the workflow tools above: -
-neutral.py +- `main_demo.py` +- `main_demo_abduction.py` +- `overload_demo.py` +- `test_overload.py` +- `debug_overload.py` -Moves the ORCA Hand to its neutral (home) position. It connects to the hand, enables torque, sets the neutral position, and then disables torque and disconnects. +## Typical usage patterns -
Args:
- +### Full setup -Example: ```bash -python scripts/neutral.py /path/to/orcahand_v1_right/config.yaml +python scripts/setup.py orca_core/models/v2/orcahand_right/config.yaml ``` -
- -
-zero.py -Moves all joints of the ORCA Hand to the zero position. It connects to the hand, enables torque, sets all joint positions to 0, waits for stabilization, then disables torque and disconnects. +### Calibration only -
Args:
- - -Example: ```bash -python scripts/zero.py /path/to/orcahand_v1_right/config.yaml +python scripts/calibrate.py orca_core/models/v2/orcahand_right/config.yaml ``` -
- -### Recording and Replay Scripts - -
-record_angles.py - -Records a sequence of joint angle waypoints for the ORCA Hand. The user is prompted to press Enter to capture each waypoint. The recorded sequence is saved to a YAML file in the `replay_sequences` directory (or a custom directory). -
Args:
- +### Move to neutral -Example: ```bash -python scripts/record_angles.py /path/to/orcahand_v1_left/config.yaml --output_dir my_recordings -# Then enter a filename prefix when prompted. +python scripts/neutral.py orca_core/models/v2/orcahand_right/config.yaml ``` -
-
-record_continuous.py +### Hold tension during maintenance -Continuously records joint angles from the ORCA Hand at a specified frequency and for an optional duration. The data is saved to a YAML file in the `replay_sequences` directory (or a custom directory). - -
Args:
- - -Example: ```bash -python scripts/record_continuous.py /path/to/orcahand_v1_right/config.yaml --frequency 100 --duration 10 --output_dir ./custom_replays -# Then enter a filename prefix when prompted. +python scripts/tension.py orca_core/models/v2/orcahand_right/config.yaml --move_motors ``` -
- -
-replay_angles.py - -Replays a recorded sequence of hand movements (waypoints) from a YAML file. It interpolates between waypoints for smooth motion and loops the sequence indefinitely. -
Args:
- - -Example: -```bash -python scripts/replay_angles.py /path/to/orcahand_v1_right/config.yaml --replay_file my_capture_replay_sequence_TIMESTAMP.yaml --step_time 0.01 -``` -
+## Shared assumptions -
-replay_continuous.py +Most scripts: -Replays continuously recorded hand joint movements from a YAML file. It attempts to match the original sampling frequency. +- accept a `config.yaml` path as their first positional argument +- construct an `OrcaHand` +- connect to the hardware +- perform one workflow +- disconnect on exit -
Args:
- - -Example: -```bash -python scripts/replay_continuous.py /path/to/orcahand_v1_right/config.yaml --replay_file continuous_angles_YYYYMMDD_HHMMSS.yaml -``` -
- -### UI Control Scripts - -
-slider_joint.py - -Provides a Tkinter-based GUI with sliders to control each joint of the ORCA Hand individually. It allows enabling/disabling torque and displays current joint values. - -
Args:
- - -Example: -```bash -python scripts/slider_joint.py /path/to/orcahand_v1_right/config.yaml -``` -
- -
-slider_motor.py - -Provides a Tkinter-based GUI with sliders to control each motor of the ORCA Hand individually. This allows for direct motor position control rather than joint-level control. Sliders have a small range for precise adjustments around the current motor position. - -
Args:
- - -Example: -```bash -python scripts/slider_motor.py /path/to/orcahand_v1_right/config.yaml -``` -
- -### Miscellaneous Scripts - -
-tension.py - -Enables torque on the ORCA Hand servos and holds the current position, effectively locking the bottom spools in order to be able to rachet the top spools. The script runs until interrupted (Ctrl+C). - -
Args:
- - -Example: -```bash -python scripts/tension.py /path/to/orcahand_v1_left/config.yaml -``` -
- -
-test.py - -A test script that connects to the ORCA Hand, enables torque, sets a specific pose for a few joints (index_mcp, middle_pip, pinky_abd), waits for 2 seconds, disables torque, and disconnects. - -
Args:
- - -Example: -```bash -python scripts/test.py -``` -
+Because of that, the scripts remain useful examples of how the package is intended to be used. diff --git a/docs/pages/orca-core-docs/orca-core-structure.md b/docs/pages/orca-core-docs/orca-core-structure.md index cc21bc92..dcd3eccb 100644 --- a/docs/pages/orca-core-docs/orca-core-structure.md +++ b/docs/pages/orca-core-docs/orca-core-structure.md @@ -1,30 +1,96 @@ -# Orca Core Repository Structure +# ORCA Core Structure -This document outlines the general structure of the `orca_core` repository. +This page explains the current architecture of the refactored repository. -* **`orca_core/`**: The main python package for OrcaHand control. - * `core.py`: Contains the central *OrcaHand class*, which provides the high-level interface for controlling the robotic hand. This includes methods for connection, calibration, setting joint positions, and reading sensor data.
[**OrcaHand Class methods**](orcahand-api.md) - * **`api/`**: Contains the FastAPI application for exposing *OrcaHand* functionalities over a web API. - * `api.py`: Defines the FastAPI endpoints, request/response models, and integrates with the OrcaHand class. This is not properly implemented yet, please ignore. +## High-level layout - * **`hardware/`**: Modules for interacting with specific hardware components. - * `dynamixel_client.py`: Implements the communication logic for Dynamixel servo motors. +- `orca_core/` + The package itself. +- `scripts/` + Operator and debugging entrypoints built on top of the package. +- `docs/` + MkDocs source for this documentation site. +- `tests/` + Unit and integration coverage for the refactored interfaces. - * **`models/`**: Stores configuration files specific to different hand models. Each sub-directory typically represents a specific hand version (or just left-right) or configuration and contains: - * `config.yaml`: Defines static parameters of the hand, such as motor IDs, joint IDs, control modes, calibration sequences, and neutral positions. - * `calibration.yaml`: Stores calibration data, such as motor limits and joint-to-motor ratios, generated during the calibration process. - - * **`utils/`**: Contains utility modules with helper functions. +## Core package modules -* **`scripts/`**: Contains standalone Python scripts for performing various operations with the *OrcaHand*, such as auto-calibration (*calibrate.py*), Tensioning (*tension.py*) etc.
[**Explore Scripts**](orca-core-scripts.md) +### `orca_core/hand_config.py` -* **`tests/`**: Includes unit tests and integration tests for the *orca_core* package to ensure code correctness and reliability (Under development). +Defines the configuration dataclasses: -* **`docs/`**: Contains the generated documentation website (built using MkDocs). Ingore this directory. - -* **`demo/`**: Contains demonstration files, such as example replay sequences. - * *kapangi_replay_sequence_20250504_002233.yaml*: An example YAML file defining a sequence of hand movements for replay. +- `BaseHandConfig` for shared joint-space metadata +- `OrcaHandConfig` for hardware-backed hands -* **`replay_sequences/`**: Stores YAML files that define sequences of joint positions or motor commands for the hand to replay. +This module is responsible for: -* `mkdocs.yml`: Configuration file for the MkDocs documentation generator. You can ingore this. \ No newline at end of file +- resolving model config paths +- loading `config.yaml` +- validating joint, motor, and calibration metadata +- normalizing signed motor mappings into absolute IDs plus inversion flags + +### `orca_core/calibration.py` + +Defines `CalibrationResult`, the immutable object that represents what has been learned about a physical hand after calibration. + +### `orca_core/joint_position.py` + +Defines `OrcaJointPositions`, the typed container used for joint-space commands and readback. + +### `orca_core/base_hand.py` + +Defines `BaseHand`, the backend-agnostic joint-space interface. + +This is where shared behavior lives: + +- clamping against ROM bounds +- accepting commands as typed objects, dicts, or numpy arrays +- interpolation between current and target pose +- named positions +- neutral and zero helpers + +### `orca_core/hardware_hand.py` + +Defines `OrcaHand`, the physical-hand implementation, plus `MockOrcaHand` for testing. + +This module owns the hardware lifecycle: + +- connect / disconnect +- torque control +- control mode selection +- current limits +- raw motor telemetry +- calibration +- motor-to-joint and joint-to-motor conversion +- maintenance helpers such as tensioning and jitter + +### `orca_core/hardware/` + +Defines the low-level motor client interface and concrete backends. + +- `motor_client.py`: abstract contract +- `dynamixel_client.py`: Dynamixel implementation +- `feetech_client.py`: Feetech implementation +- `mock_dynamixel_client.py`: in-memory test backend + +`OrcaHand` selects the correct backend based on `config.motor_type`. + +### `orca_core/models/` + +Bundled hand models and example configs. Each model directory contains: + +- `config.yaml` +- optionally `calibration.yaml` + +The refactored code treats these as versioned assets rather than ad hoc examples. + +## Practical mental model + +The package is easiest to understand as a pipeline: + +1. `config.yaml` becomes `OrcaHandConfig` +2. `calibration.yaml` becomes `CalibrationResult` +3. `OrcaHand` combines those objects with a `MotorClient` +4. application code sends joint-space commands through `BaseHand` / `OrcaHand` + +That split is the main conceptual improvement of the refactor: static model description, dynamic calibration state, and live hardware control are now distinct layers. diff --git a/docs/pages/orca-core-docs/orcahand-api.md b/docs/pages/orca-core-docs/orcahand-api.md index 63dab9f7..6a394f7b 100644 --- a/docs/pages/orca-core-docs/orcahand-api.md +++ b/docs/pages/orca-core-docs/orcahand-api.md @@ -1,23 +1,84 @@ -# API Reference +# ORCA Core API -## OrcaHand +This page documents the current Python-facing API that the refactored package is organized around. + +## Recommended entrypoint + +Most application code should start with `OrcaHand`: + +```python +from orca_core import OrcaHand + +hand = OrcaHand(config_path="orca_core/models/v2/orcahand_right/config.yaml") +success, message = hand.connect() + +if success: + hand.init_joints() + hand.set_joint_positions({"index_mcp": 20, "middle_mcp": 20}) +``` + +The main lifecycle is: + +1. construct `OrcaHand` +2. `connect()` +3. `init_joints()` +4. send joint-space commands +5. `disconnect()` + +## Public data objects + +### `OrcaHandConfig` + +`OrcaHandConfig` is the validated model description loaded from `config.yaml`. + +::: orca_core.OrcaHandConfig + +### `CalibrationResult` + +`CalibrationResult` is the immutable snapshot loaded from `calibration.yaml`. + +::: orca_core.CalibrationResult + +### `OrcaJointPositions` + +`OrcaJointPositions` is the typed container used to send and receive joint-space poses. + +::: orca_core.OrcaJointPositions + +## Hand classes + +### `OrcaHand` + +`OrcaHand` extends the shared joint-space interface with hardware lifecycle, calibration, telemetry, and maintenance tasks. ::: orca_core.OrcaHand ---- +### `BaseHand` + +`BaseHand` provides the backend-agnostic joint-space API used by real and test hands. + +::: orca_core.base_hand.BaseHand + +## Internal extension point + +### `MotorClient` -## BaseHand +`MotorClient` is the low-level contract that concrete motor backends implement. Most users do not need it directly, but it is the right abstraction if you are extending the hardware layer. -::: orca_core.BaseHand +::: orca_core.hardware.motor_client.MotorClient ---- +## Notes on units -## OrcaJointPosition +- Joint-space commands follow the units used in the hand config. Bundled models use degrees. +- Raw motor telemetry is reported in radians. +- Motor current is reported in mA. -::: orca_core.OrcaJointPosition +## Notes on accepted command shapes ---- +`set_joint_positions(...)` accepts: -## HandConfig +- `OrcaJointPositions` +- `dict[str, float | None]` +- `numpy.ndarray` -::: orca_core.HandConfig +This makes it easy to use the same API from typed workflows, quick scripts, and numerical code. diff --git a/mkdocs.yml b/mkdocs.yml index db88c4f7..efacd3a6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,7 +24,7 @@ plugins: default_handler: python handlers: python: - paths: ["orca_core.core"] + paths: ["."] options: show_source: false show_root_heading: true @@ -48,16 +48,18 @@ markdown_extensions: permalink: true nav: - - About Orca: index.md - - Getting Started with ORCA Hand: + - Overview: index.md + - Getting Started: - Setting Up Dynamixels: pages/getting-started-docs/setting-up-dynamixels.md - Setting Up Config: pages/getting-started-docs/setting-up-config.md - - Quickstart with Core Package: pages/getting-started-docs/quickstart-with-core-package.md - - Calibration and Tensioning: pages/getting-started-docs/initial-tensioning-and-calibration.md + - Tensioning and Calibration: pages/getting-started-docs/initial-tensioning-and-calibration.md + - Quickstart: pages/getting-started-docs/quickstart-with-core-package.md - ORCA Core: - - Structure: pages/orca-core-docs/orca-core-structure.md - - OrcaHand Class API: pages/orca-core-docs/orcahand-api.md + - Repository Structure: pages/orca-core-docs/orca-core-structure.md + - Python API: pages/orca-core-docs/orcahand-api.md - Scripts: pages/orca-core-docs/orca-core-scripts.md + - Motor Client Layer: pages/orca-core-docs/motor-client-api.md + - Dynamixel Latency: pages/orca-core-docs/latency-optimization.md extra: social: @@ -69,4 +71,4 @@ extra: link: https://github.com/orca-hand extra_css: - - css/overrides.css \ No newline at end of file + - css/overrides.css