Metadata-Version: 2.2
Name: reforge-core
Version: 2.0.18
Summary: Reforge Core SDK by Reforge Robotics.
Author-Email: Reforge Robotics <info@reforgerobotics.com>
License: MIT
Project-URL: Homepage, https://docs.reforgerobotics.com/sdk-reference/sdk-introduction
Requires-Python: <3.12,>=3.11
Requires-Dist: anytree==2.13.0
Requires-Dist: bleak==0.22.2
Requires-Dist: cmeel-eigen==3.4.1
Requires-Dist: cmeel-urdfdom-headers==3.0.0
Requires-Dist: coal<4,>=3.0.3
Requires-Dist: control==0.10.2
Requires-Dist: matplotlib==3.10.5
Requires-Dist: muse-api==2.0.0
Requires-Dist: numpy<3,>=2
Requires-Dist: ompl; platform_system != "Darwin"
Requires-Dist: clarabel
Requires-Dist: pin==4.0.0
Requires-Dist: pyserial==3.5
Requires-Dist: requests
Requires-Dist: scipy<2,>=1.15
Requires-Dist: torch==2.3.1
Provides-Extra: dev
Requires-Dist: black==24.1.1; extra == "dev"
Requires-Dist: mypy==2.1.0; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pytorch-kinematics; extra == "dev"
Requires-Dist: pytorch-minimize; extra == "dev"
Requires-Dist: viser[urdf]; extra == "dev"
Requires-Dist: PyYAML; extra == "dev"
Requires-Dist: yourdfpy; extra == "dev"
Requires-Dist: sounddevice; extra == "dev"
Requires-Dist: control==0.10.2; extra == "dev"
Requires-Dist: osqp; extra == "dev"
Provides-Extra: kinecal
Requires-Dist: pytorch-kinematics; extra == "kinecal"
Requires-Dist: pytorch-minimize; extra == "kinecal"
Requires-Dist: viser[urdf]; extra == "kinecal"
Requires-Dist: PyYAML; extra == "kinecal"
Requires-Dist: yourdfpy; extra == "kinecal"
Requires-Dist: sounddevice; extra == "kinecal"
Provides-Extra: joint-tracker
Provides-Extra: joint-tracker-reference
Requires-Dist: control==0.10.2; extra == "joint-tracker-reference"
Requires-Dist: osqp; extra == "joint-tracker-reference"
Provides-Extra: all
Requires-Dist: pytorch-kinematics; extra == "all"
Requires-Dist: pytorch-minimize; extra == "all"
Requires-Dist: viser[urdf]; extra == "all"
Requires-Dist: PyYAML; extra == "all"
Requires-Dist: yourdfpy; extra == "all"
Requires-Dist: sounddevice; extra == "all"
Description-Content-Type: text/markdown

# Reforge SDK (`reforge-core`)

Reforge SDK is the independently built Python package that powers Reforge calibration and model-based vibration control.

This README is intended for PyPI distribution of `reforge-core`.

## What This Package Provides

### Calibration module (`reforge_core.calibration`)

The calibration module provides the cloud interface used after a robot calibration run:

- Uploads calibration data artifacts
- Triggers identification or fine-tuning jobs in Reforge Cloud API
- Polls job status and downloads generated model artifacts
- Extracts returned model files for control use

Primary entry point:

- `reforge_core.calibration.api.ReforgeAPIManager`

### Control module (`reforge_core.control`)

The control module provides vibration-aware command shaping and model-based
joint tracking for robot trajectories:

- Loads per-axis model files generated by calibration/identification
- Computes shaping parameters from current robot state
- Shapes single commands or full trajectories
- Applies native C++ Joint Tracker compensation to complete
  trajectories and appendable streams
- Returns shaped positions, velocities, and accelerations for execution

Primary entry points:

- `reforge_core.control.python.covalent_wrapper.ShaperInterface`
- `reforge_core.control.python.covalent_wrapper.RobotState`
- `reforge_core.control.joint_tracker.JointTrackerInterface`
- `reforge_core.control.joint_tracker.JointCalibrationCompensator`

## Interface with `reforge-interface` (`src/robot`)

Reforge SDK is designed to be consumed by the `reforge-interface` [repository](https://github.com/reforge-robotics/reforge-interface), where robot-specific integration lives.

Expected responsibilities in `reforge-interface/src/robot`:

- Robot transport and SDK communication loop
- Sensor acquisition (joint encoders, TCP accelerometer)
- Calibration routine execution and local data storage
- Invocation of Reforge SDK calibration + control APIs

Typical artifact flow:

1. `src/robot/run.py` runs calibration and stores local data (for example under `src/robot/data/<date>`).
2. `ReforgeAPIManager` uploads the data and requests model generation.
3. Returned model artifacts are saved for runtime control (commonly under `src/robot/models/current`).
4. `ShaperInterface` loads those models and the robot URDF to shape outgoing joint commands before they are sent through the robot driver in `src/robot`.

In this architecture, `reforge-interface/src/robot` owns robot I/O and execution, while `reforge-core` owns calibration-cloud orchestration and shaping logic.

## Usage

1. Ensure you have the requirements:
- An accelerometer/IMU located at the tool center point (TCP) that can measure data in the x-, y-, and z-coordinates of the end-effector’s inertial frame of reference (or the robot base’s inertial frame).
- Encoders in each joint that can accurately measure the current joint position of the robot at a rate of 200 Hz or higher.
- A real-time SDK to access data from IMU and encoders and to command the joint motors with time-domain angular motor positions.
- A Universal Robot Description File (URDF) that describes the robot’s kinematics and dynamics (dynamics optional but preferred).

2. Integrate the robot’s SDK/URDF and build the project.
- Pull the Reforge repository from Github and add your robot's SDK to `requirements.txt`
```bash
git clone https://github.com/reforge-robotics/reforge-interface.git
cd reforge-interface
```
- Add the robot's URDF to `src/robot/urdf`
- Build the project
```bash
python3.11 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install --no-cache-dir .
```

3. Integrate your robot's SDK in `src/robot/robot_interface.py`

4. Test robot connection
```bash
python3 -m robot.run connect_test <robot_ip> --local_ip <local_ip> --sdk_token <robot_sdk_token>
```

5. Run the calibration and identification of models
- Run with automatic identification
```bash
python3 -m robot.run calibrate <robot_ip> --local_ip <local_ip> --sdk_token <robot_sdk_token> --robot_id <reforge_robot_id> --freq 250 --identify <reforge_api_token>
```
- Run joint-tracker calibration and save joint models
```bash
python3 -m robot.run calibrate <robot_ip> --type joint_tracker --local_ip <local_ip> --sdk_token <robot_sdk_token> --robot_id <reforge_robot_id> --identify <reforge_api_token>
```
- Run shaper calibration with an compensated joint-tracker prepass, then stop before shaper identification
```bash
python3 -m robot.run calibrate <robot_ip> --type shaper --with_joint_tracker_mpc --local_ip <local_ip> --sdk_token <robot_sdk_token> --robot_id <reforge_robot_id> --joint_tracker_api_token <joint_tracker_api_token>
```
- Run shaper calibration with an MPC-compensated joint-tracker prepass, then run shaper identification with the same API token
```bash
python3 -m robot.run calibrate <robot_ip> --type shaper --with_joint_tracker_mpc --local_ip <local_ip> --sdk_token <robot_sdk_token> --robot_id <reforge_robot_id> --identify <shared_api_token>
```
- Run shaper calibration with an MPC-compensated joint-tracker prepass, then run shaper identification with separate API tokens
```bash
python3 -m robot.run calibrate <robot_ip> --type shaper --with_joint_tracker_mpc --local_ip <local_ip> --sdk_token <robot_sdk_token> --robot_id <reforge_robot_id> --joint_tracker_api_token <joint_tracker_api_token> --identify <shaper_api_token>
```
- Run calibration first, then run identification
```bash
python3 -m robot.run calibrate <robot_ip> --local_ip <local_ip> --sdk_token <robot_sdk_token>
python3 -m robot.run identify <reforge_api_token> <reforge_robot_id> <local_data_location>
```

1. Run test to verify the calibration
```bash
python3 -m robot.run vibration_test <robot_ip> <local_data_location> --local_ip <local_ip> --sdk_token <robot_sdk_token>
```
The robot will go through a random series of motion pairs, one uncompensated and one compensated, store the accelerometer data from the motion tests, and print out a log with the test results.

### Shaper Calibration Rollout Notes

Shaper calibration records base-joint grid sweeps by default. Existing six-DOF
robots still use `--base_joints=1` unless a different value is passed, but that
one-base default now records `j0` base-angle coverage in addition to the normal
full-axis shaper sweeps.

For multi-base robots, pass `--base_joints <count>` to configure the number of
consecutive base joints starting at joint index `0`. Base-joint limits are read
from the URDF when available. If a requested base joint has no URDF limit, pass
one `--base_joint_limits LOWER,UPPER` value for that joint. Calibration setup
fails if the requested base-joint axes do not match the expected
world-z-parallel base-joint definition.

Use `--test-mode` to traverse the planned calibration poses without running sine
sweeps or writing acquisition artifacts. The CLI prints the planned run count
before motion; review it because calibration duration grows quickly as
`--base_joints` increases. Data recorded above 500 Hz is saved to calibration
CSV artifacts downsampled to 500 Hz.

New shaper datasets and model bundles are schema-versioned. New one-base models
use the runtime feature schema `j0_rad;v_deg;r_mm;inertia`; multi-base models
add one base-angle feature per consecutive base joint before `v_deg`, `r_mm`,
and `inertia`. Older model bundles without feature metadata continue to load
through the legacy `[v_deg, r_mm, inertia]` fallback.

When resuming calibration from a later pose, preserve the earlier pose artifacts
in the same data folder. Existing prior-pose CSV artifacts are treated as
completed run data during rollout validation, even if the resumed manifest only
marks later runs as completed.

## Minimal Usage Sketch

```python
from reforge_core.calibration.api import ReforgeAPIManager
from reforge_core.control.python.covalent_wrapper import ShaperInterface, RobotState
from reforge_core.control.joint_tracker import (
    JointTrackerConfig,
    JointTrackerInterface,
    JointTrackerOptimizerOptions,
)

# Calibration/model generation
api = ReforgeAPIManager(reforge_api_token="<token>", robot_id="<robot_id>")
api.run_cloud_model_generation(data_folder="src/robot/data/<YYYY-MM-DD>")

# Runtime shaping
shaper = ShaperInterface(
    sample_time=0.005,
    model_directory="src/robot/models/current",
    urdf_filepath="src/robot/urdf/<robot>.urdf",
    num_axes=3,
    num_joints=6,
    tcp_payload_mass_kg=2.4,  # Attached tool/workpiece mass [kg].
)

state = RobotState(joint_angles=...)  # numpy array
shaped = shaper.shape_sample(..., state)

# Update the existing Shaper before inference after a payload change.
shaper.set_tcp_payload_mass_kg(1.1)

# Native-backed Joint Tracker compensation
tracker = JointTrackerInterface(
    JointTrackerConfig(
        sample_time_s=0.004,
        num_joints=6,
        modeled_axis_indices=[0, 1, 2, 3, 4, 5],
        model_directory="src/robot/models/current",
        optimizer_options=JointTrackerOptimizerOptions(lam_u=0.01),
    )
)

tracked = tracker.process_trajectory(command=positions_rad)
```

The payload must be finite and nonnegative. A numeric
`process_trajectory(..., tcp_payload_mass_kg=...)` override is applied before
that trajectory and persists for later calls; omitting it preserves the current
payload.

## Installation

```bash
pip install reforge-core
```

For supported PyPI wheels, public Joint Tracker usage is C++ native-backed by
default through `reforge_core.control.joint_tracker.JointTrackerInterface`.
Wheel users do not need Rust, CMake, pybind11, nlohmann, compilers, or other
runtime build tools to import or run the native Joint Tracker path. Unsupported
platform or Python combinations must not silently fall back to the internal
pure-Python reference implementation.

The public native-backed Joint Tracker path is configured with
`JointTrackerConfig` and `JointTrackerOptimizerOptions`, including optimizer
settings such as `lam_u`. The internal pure-Python reference/oracle path is
kept at 1:1 feature and controller-output parity for the supported API.
`JointCalibrationCompensator` remains a public calibration API and is backed by
native C++ compensation in the default product wheel. Backend selection and
direct `NativeJointTrackerInterface` usage are internal implementation details.

Joint Tracker deliberately diverges from the Shaper backend naming and fallback
model in this first native PyPI release. Shaper exposes backend-specific public
names for historical compatibility. Joint Tracker's public facade is native by
default and fails closed when the required native extension is missing or
incompatible.

## Build From Source With the Complete Native Shaper Backend

Use this path when developing `reforge-core` locally or when
`ShaperInterface` should default to the complete native backend. The complete
backend is only available when the installed `_native_shaper` extension was
built with the native solver/backend targets enabled.

Run these commands from the repository root, not from `src/core_sdk`:

Use Python 3.11 for this repository. Do not uninstall or modify an
Ubuntu-owned system Python 3.12 installation; create the repository `.venv`
from Python 3.11 instead. Native source builds also require CMake 3.25 or
newer. The local source-build path below gets Eigen through
`cmeel-eigen==3.4.1`; CI and Debian packaging workflows install Eigen through
`libeigen3-dev`.

```bash
python3.11 -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip setuptools wheel
python -m pip install \
  --index-url https://download.pytorch.org/whl/cpu \
  --extra-index-url https://pypi.org/simple \
  torch==2.3.1+cpu
python -m pip install \
  scikit-build-core \
  cmeel-eigen==3.4.1 \
  cmeel-urdfdom-headers==3.0.0 \
  pybind11 \
  nlohmann-json==3.12.0 \
  pin==4.0.0
```

The CPU Torch wheel is intentional. A CUDA Torch wheel can make native CMake
Torch discovery fail on machines that do not have the matching CUDA libraries.

Install `rustup` if it is not already on `PATH`, then install the exact Rust
toolchain required by the locked Clarabel native solver wrapper:

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
rustup toolchain install 1.84.1
```

Force a clean editable rebuild of the SDK package. The `-e src/core_sdk`
argument is intentional; installing from the repository root does not build the
`reforge-core` package.

```bash
RUSTUP_TOOLCHAIN=1.84.1 \
python -m pip install \
  --no-cache-dir \
  --force-reinstall \
  --no-build-isolation \
  --no-deps \
  --config-settings=build-dir=/tmp/reforge-core-sdk-native-build \
  -e src/core_sdk
```

Use a fresh build directory if you repeat the build after changing native
sources or CMake options.

Verify that the installed extension exposes the complete backend:

```bash
python - <<'PY'
from reforge_core.control import _native_shaper

print("complete_backend_available:", _native_shaper.complete_backend_available)
print("has NativeShaper:", hasattr(_native_shaper, "NativeShaper"))
PY
```

The expected output is:

```text
complete_backend_available: True
has NativeShaper: True
```

If `complete_backend_available` is `False`, the active environment is still
using a partial `_native_shaper` build. Re-run the editable install with a fresh
`--config-settings=build-dir=...` value and confirm that `rustc +1.84.1
--version` reports Rust `1.84.1`.

## Optional Extras

`reforge-core` keeps the base install focused on the shared calibration and
control stack. Optional feature dependencies are exposed through extras:

- `pip install reforge-core[kinecal]` installs the additional packages required
  for the `reforge_core.kinecal` package.
- `pip install reforge-core[joint_tracker]` is a compatibility no-op for the
  public native-backed `reforge_core.control.joint_tracker` package.
- `pip install reforge-core[joint_tracker_reference]` installs the Python
  reference/oracle Joint Tracker packages used for internal parity tests and fixture
  regeneration.
- `pip install reforge-core[all]` installs all optional runtime feature
  dependencies currently defined by this package.
- `pip install reforge-core[dev]` installs development tooling plus the same
  optional runtime dependencies included by `all`, plus internal oracle
  dependencies needed by the repository test suite.
