Metadata-Version: 2.2
Name: reforge-core
Version: 2.0.15
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.11
Requires-Dist: anytree==2.13.0
Requires-Dist: bleak==0.22.2
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
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: 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
Requires-Dist: osqp; extra == "joint-tracker"
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"
Requires-Dist: control==0.10.2; extra == "all"
Requires-Dist: osqp; 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 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
- 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`

## 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 MPC-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>
```

6. 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

# 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,
)

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

## Installation

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

## 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`:

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

python -m pip install --upgrade pip setuptools wheel
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
```

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]` installs the additional packages
  required for the `reforge_core.control.joint_tracker` package.
- `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`.
