Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
1c2e82a
bump speed
Jepson2k Dec 17, 2025
4fa2c68
add claude instructions
Jepson2k Dec 17, 2025
3ff4edc
add accel, blending and ping decoding
Jepson2k Dec 18, 2025
3d6b827
fixed delay
Jepson2k Dec 18, 2025
fe94056
bug fixes and tests
Jepson2k Dec 18, 2025
ff2cd82
added reset command, doc strings, and mesh simplification
Jepson2k Dec 27, 2025
4c64028
swap spatialmath usage for sophuspy
Jepson2k Dec 29, 2025
de45403
added toppra, ruckig, scurve, quintic, and trapezoid
Jepson2k Jan 5, 2026
971413a
dependency fix and switch to pre-commit for lint and type runners
Jepson2k Jan 5, 2026
dfee9ec
add additional robotics toolbox links
Jepson2k Jan 8, 2026
376adff
separate joint and cartesian motion profiles
Jepson2k Jan 8, 2026
46adb5a
fix mypy errors: wrap numpy scalars with float()
Jepson2k Jan 8, 2026
c50296f
Add limit enforcement to LINEAR motion profile
Jepson2k Jan 8, 2026
6bf196b
Fix pre-commit: ruff formatting, mypy type errors, add sophuspy dep, …
Jepson2k Jan 9, 2026
d7f8987
Fix E402: move deferred imports to top, add noqa for circular import
Jepson2k Jan 9, 2026
26d1509
Fix memoryview assignment for cross-platform compatibility (use numpy…
Jepson2k Jan 9, 2026
8f420a1
Fix unit test failures: MockState and ResetCommand
Jepson2k Jan 9, 2026
cff8774
Use forked sophuspy with numpy>=2.0 pin to fix segfaults
Jepson2k Jan 10, 2026
5117c20
Increase IK worker test timeouts for CI reliability
Jepson2k Jan 10, 2026
ec98b63
Unify motion command parameter format across all command types
Jepson2k Jan 11, 2026
a70db9e
remove python 3.10 support
Jepson2k Jan 11, 2026
b246164
remove incorrect teensy info
Jepson2k Jan 11, 2026
e2129bb
further reduced unnecessary allocations
Jepson2k Jan 12, 2026
423ec3a
Update pyproject.toml
Jepson2k Jan 12, 2026
be7af93
Update pyproject.toml
Jepson2k Jan 12, 2026
7ef691f
Update pyproject.toml
Jepson2k Jan 12, 2026
94034bc
updated rtb urls
Jepson2k Jan 12, 2026
8d94ac3
bugfixes
Jepson2k Jan 13, 2026
555333a
Add subprocess error handling and logging for MockSerial
Jepson2k Jan 13, 2026
9ff104c
Add more diagnostic logging for MockSerial subprocess
Jepson2k Jan 13, 2026
6da6957
Increase MockSerial subprocess timeout to 10s for slow CI spawn
Jepson2k Jan 13, 2026
99d0e2a
Increase test fixture timeout to 30s for JIT + subprocess startup
Jepson2k Jan 13, 2026
c92e85b
Refactor IK worker to use numba SE3 operations
Jepson2k Jan 15, 2026
4c8b92e
Fix RuntimeError in client close when event loop is closed
Jepson2k Jan 15, 2026
3e0f71e
Refactor controller into modular components
Jepson2k Jan 15, 2026
1b0626f
more stable control rate and simulator
Jepson2k Jan 20, 2026
eb3db6f
removed problematic move and increase timeout for slow macos runners
Jepson2k Jan 20, 2026
4cf39fe
jitclass doesn't cache so changing back to regular class
Jepson2k Jan 20, 2026
cfa467c
make homing faster for testing since it only uses sim
Jepson2k Jan 20, 2026
35dafc4
reduced sim execution time by 25x
Jepson2k Jan 21, 2026
718b4bb
further optimized simulation
Jepson2k Jan 21, 2026
b966f04
fix simulation jit warmup
Jepson2k Jan 21, 2026
8308531
removed unnecessary timeout
Jepson2k Jan 22, 2026
d1187df
reduce default control loop rate to 100Hz
Jepson2k Jan 23, 2026
4e44d99
yield before closing UDP transport to let pending writes complete
Jepson2k Jan 24, 2026
fed870c
Zero-allocation optimizations for control loop hot paths
Jepson2k Jan 24, 2026
b6f1724
convert ascii transport to binary msgpack
Jepson2k Jan 28, 2026
79718eb
extend test time and fix multicast loopback on macos
Jepson2k Jan 28, 2026
c01e389
bump robotics toolbox, add windows arm testing, and fixed flaky test
Jepson2k Jan 29, 2026
8b2e782
bump roboticstoolbox-python to v1.3.0, add Windows ARM64 wheels, drop…
Jepson2k Jan 29, 2026
67904a9
remove windows-arm CI runner (llvmlite unsupported), relax movecart t…
Jepson2k Jan 29, 2026
fe7aade
replace roboticstoolbox with pinokin, zero-allocation IK cleanup
Jepson2k Feb 2, 2026
263d5f6
fix all mypy errors, unify client return types to int
Jepson2k Feb 11, 2026
cd0bb3f
removed more unnecessary allocations, added examples and categories t…
Jepson2k Feb 12, 2026
f23b2e6
structured error catalog, motion pipeline, and type fixes
Jepson2k Feb 18, 2026
b36a628
update tests for error catalog and motion pipeline
Jepson2k Feb 18, 2026
70dc7af
bump pinokin dependency to v0.1.4
Jepson2k Feb 18, 2026
65dfc15
fix blend test race: terminate chain with r=0 for immediate flush
Jepson2k Feb 18, 2026
13d814a
use wait_command_complete for move commands instead of wait_motion_co…
Jepson2k Feb 18, 2026
79fd90d
add workflow_dispatch trigger to CI
Jepson2k Feb 18, 2026
1069317
fall back to unicast when multicast socket creation fails
Jepson2k Feb 18, 2026
c671f50
rewrite README: update API references, add architecture and internals…
Jepson2k Feb 19, 2026
619514c
added tools
Jepson2k Mar 6, 2026
ac755f9
annotate _ToolBase.key for ty type checker
Jepson2k Mar 6, 2026
b666aae
fix 3 failing tests: ActionState enum, moveP waypoint reachability
Jepson2k Mar 6, 2026
6ebd437
fix moveP test IK failure on CI, fix ty type error in trajectory.py
Jepson2k Mar 6, 2026
2d2c100
fix orphaned resource_tracker on dirty shutdown
Jepson2k Mar 6, 2026
dd256b6
disable set_pdeathsig on Windows to fix CI crash
Jepson2k Mar 7, 2026
80a7d80
add tool variants and concurrent tool commands, tune motion limits, c…
Jepson2k Mar 22, 2026
f7dd951
fix servoL clamping: correct CSE position every clamped tick to preve…
Jepson2k Mar 22, 2026
b031af6
jogL: send JOG command with velocity instead of MOVE, fix servoL IK o…
Jepson2k Mar 23, 2026
d43fb4d
fix jogL jitter, restore servoL CSE speed matching, adaptive overbudg…
Jepson2k Mar 24, 2026
841c584
increase cart jog test duration for slow CI runners
Jepson2k Mar 26, 2026
8a9735a
fix ty 0.0.25 suppression syntax, servoL IK recovery, path tolerance
Jepson2k Mar 26, 2026
559dd1e
bump version to 0.2.0, update README and examples
Jepson2k Mar 29, 2026
e7691d3
update readme and add versioning
Jepson2k Mar 30, 2026
acba9c7
client API cleanup: snake_case methods, noun queries, verb mutations
Jepson2k Mar 31, 2026
3e3eeea
ci: add waldoctl branch-matching for cross-repo dev branches
Jepson2k Mar 31, 2026
da3f51a
add tcp_offset commands and fix planner tool sync on RESET
Jepson2k Apr 7, 2026
5f8d26f
fix ty 0.0.29 invalid-return-type in serial_transport
Jepson2k Apr 7, 2026
8dc895f
Merge branch 'api-cleanup' into main
Jepson2k Apr 7, 2026
7096a90
quiet startup logs and clean up shutdown noise
Jepson2k Apr 8, 2026
58f6aee
docs: forbid compound cd commands in CLAUDE.md
Jepson2k Apr 8, 2026
7c4c787
bump version to 0.2.1, pin waldoctl v0.2.0
Jepson2k Apr 9, 2026
9f5c676
fix: auto-bind tools in RobotClient/AsyncRobotClient constructors
Jepson2k Apr 9, 2026
1cf3b93
examples: add tool.calibrate() after select_tool()
Jepson2k Apr 9, 2026
f297ae1
add tbump config for one-command releases
Jepson2k Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: release

on:
release:
types: [published]

permissions:
contents: write

jobs:
bump-version:
name: Bump version to match release tag
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.target_commitish }}

- name: Extract version from tag
id: version
run: |
TAG="${{ github.event.release.tag_name }}"
# Strip leading 'v' if present (v0.2.0 -> 0.2.0)
VERSION="${TAG#v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Update version in pyproject.toml
run: |
sed -i "s/^version = \".*\"/version = \"${{ steps.version.outputs.version }}\"/" pyproject.toml

- name: Verify version
run: |
grep "^version = \"${{ steps.version.outputs.version }}\"" pyproject.toml

- name: Commit and push version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add pyproject.toml
git diff --cached --quiet && echo "No changes to commit" && exit 0
git commit -m "bump version to ${{ steps.version.outputs.version }}"
git push
35 changes: 22 additions & 13 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: tests

on:
workflow_dispatch:
push:
paths:
- '**'
Expand All @@ -10,7 +11,7 @@ on:

jobs:
lint:
name: Lint (ruff + mypy)
name: Lint (pre-commit)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand All @@ -19,18 +20,17 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dev dependencies
- name: Install dependencies
shell: bash
run: |
python -m pip install --upgrade pip
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
if git ls-remote --heads https://github.com/Jepson2k/waldoctl.git "$BRANCH" 2>/dev/null | grep -q .; then
pip install "waldoctl @ git+https://github.com/Jepson2k/waldoctl.git@${BRANCH}"
fi
pip install -e ".[dev]"
- name: Ruff (lint)
run: ruff check parol6
- name: Ruff (format check)
run: ruff format --check parol6
- name: MyPy
run: mypy parol6
- name: Run pre-commit
uses: pre-commit/action@v3.0.1
test:
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
Expand All @@ -39,7 +39,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11']
python-version: ['3.11', '3.12', '3.13', '3.14']

steps:
- name: Checkout repository (with submodules)
Expand All @@ -52,17 +52,26 @@ jobs:
cache: pip
cache-dependency-path: pyproject.toml

- name: Upgrade pip and install dependencies
- name: Install cross-repo dependencies
shell: bash
run: |
python -m pip install --upgrade pip
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
# waldoctl: try matching branch, fall back to tagged version
if git ls-remote --heads https://github.com/Jepson2k/waldoctl.git "$BRANCH" 2>/dev/null | grep -q .; then
pip install "waldoctl @ git+https://github.com/Jepson2k/waldoctl.git@${BRANCH}"
fi

- name: Install package
run: |
pip install -e ".[dev]" pytest-timeout

- name: Show environment
run: |
python -V
pip list

- name: Run tests (skip hardware)
- name: Run tests
env:
PYTHONUNBUFFERED: '1'
PYTHONUTF8: '1'
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@ cython_debug/
# VS Code
.vscode/

serial_port.txt
serial_port.txt
test-results.xml
24 changes: 8 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
default_language_version:
python: python3.11

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v6.0.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-added-large-files

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
rev: v0.14.10
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
- repo: local
hooks:
- id: mypy
name: mypy (parol6)
- id: ty
name: ty (parol6)
entry: ty check parol6
language: system
files: ^parol6/
pass_filenames: false
args: ["parol6"]
stages: [commit, push]
additional_dependencies:
- numpy==1.26.4
- spatialmath-python==1.1.14
- scipy==1.11.4
stages: [pre-commit, pre-push]
154 changes: 154 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

PAROL6 Python API is a lightweight client and controller for PAROL6 6-DOF robot arms. It provides:
- **AsyncRobotClient**: Async UDP client for robot control
- **RobotClient**: Synchronous wrapper for imperative scripts
- **Controller**: Fixed-rate control loop with serial/simulator transport

## Architecture

```
┌─────────────────────────────────────────┐
│ Client Application │
│ (AsyncRobotClient / RobotClient) │
└──────────────────┬──────────────────────┘
UDP commands (port 5001)
Multicast status (239.255.0.101:50510)
┌──────────────────▼──────────────────────┐
│ Controller │
│ (parol6/server/controller.py) │
│ - 100 Hz control loop (configurable) │
│ - Command queue & execution │
│ - Status multicast broadcasting │
└──────────────────┬──────────────────────┘
Serial (3 Mbaud) or MockSerial
┌──────────────────▼──────────────────────┐
│ Robot Hardware / Simulator │
└─────────────────────────────────────────┘
```

## Build & Test Commands

```bash
# Development setup
pip install -e .[dev]
pre-commit install

# Linting & formatting
ruff check .
ruff format .
ty check parol6/

# Run all pre-commit hooks
pre-commit run -a

# Testing (simulator used by default via conftest.py — do NOT prefix with env vars)
pytest

# Run specific test file
pytest tests/unit/test_wire.py -v
```

**IMPORTANT: Do NOT prefix `pytest` commands with environment variables like `PAROL6_FAKE_SERIAL=1 pytest ...`. The conftest.py already configures `PAROL6_FAKE_SERIAL=1`. Just run `pytest` directly.**

## Controller CLI

```bash
# Start controller
parol6-server --log-level=INFO

# With explicit serial port
parol6-server --serial=/dev/ttyUSB0 --log-level=DEBUG

# Verbosity shortcuts: -v (INFO), -vv (DEBUG), -vvv (TRACE)
```

## Key Modules

- **`parol6/client/async_client.py`**: Primary API - async UDP client with motion commands, queries, and status streaming
- **`parol6/server/controller.py`**: Controller with fixed-rate loop and command execution
- **`parol6/commands/`**: Polymorphic command classes using `@register_command(CmdType.XXX)` decorator
- **`parol6/protocol/wire.py`**: Msgpack message encoding/decoding, command structs (tagged union)
- **`parol6/PAROL6_ROBOT.py`**: Robot kinematics config, DH parameters, joint limits, tool transforms

## Adding a New Command

1. Create a class in `parol6/commands/` and decorate with `@register_command(CmdType.YOUR_CMD)`
2. Define a `PARAMS_TYPE` msgspec Struct for wire validation
3. Implement `do_setup(state)` and `execute_step(state)` lifecycle methods
4. For motion commands, set `streamable=True` if supporting high-rate streaming

## Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `PAROL6_CONTROL_RATE_HZ` | 100 | Control loop frequency |
| `PAROL6_STATUS_RATE_HZ` | 50 | Status broadcast rate (tests use 20 Hz) |
| `PAROL6_FAKE_SERIAL` | 0 | Enable simulator (no hardware) |
| `PAROL6_COM_PORT` | auto | Serial port override |
| `PAROL_TRACE` | 0 | Enable TRACE logging |

## Simulator Mode

- Uses `MockSerialTransport` to emulate robot dynamics without hardware
- Toggle via client: `simulator(True)` / `simulator(False)`
- Controller updates `PAROL6_FAKE_SERIAL` and reinitializes transport seamlessly
- **Important**: Simulator cannot guarantee hardware success—motor/current limits may cause failures on real robot

## Kinematics Notes

- Uses numerical IK via pinokin (C++/Pinocchio with nanobind Python bindings)
- J4 is particularly sensitive—some cartesian targets may fail to solve
- Update `PAROL6_ROBOT.py` for modified hardware (gear ratios, limits, DH params)

## Test Markers

- `@pytest.mark.unit`: Isolated component tests
- `@pytest.mark.integration`: Component interaction tests (uses simulator)

## Performance Warnings

If you see `Control loop avg period degraded by +XX%`:
- Reduce `PAROL6_CONTROL_RATE_HZ`
- Disable TRACE logging (significant overhead)
- Avoid heavy background tasks during motion

## Hot Path Rules

`execute_step()` and `tick()` run at 100 Hz. **No heap allocations** in these methods.

For streamable commands (`streamable = True`), `do_setup()` also runs at high frequency (50 Hz from UI) via `assign_params()` + `do_setup()` fast-path. Treat it as a hot path too.

- No `list(...)`, `[x for x in ...]`, `dict(...)`, or other container construction
- No string formatting or f-strings (except in error/stop paths that run once)
- Pre-allocate all buffers in `__init__`
- `ndarray[:] = list` is fine — numpy writes into existing buffer in-place
- Use `dest[:] = src` for array copies. Only use `np.copyto(dest, src, casting=...)` when casting is needed — it's slower otherwise

## Code Style

- **Comments**: Describe the final code state, not what changed. Avoid "changed X to Y" or "added this because..." comments.
- **Git commits/PRs**: No emoji, no "Generated by..." footers, no co-author boilerplate.
- **Type annotations**: Fix type errors properly instead of using `# type: ignore`. Prefer:
- `@overload` decorators for functions with different return types based on input
- `assert` statements to narrow types after None checks
- `cast()` from typing when the type is known but the checker can't infer it
- `np.atleast_1d()` or similar to guarantee array returns from numpy functions
- Only use `# type: ignore` as a last resort for genuine type checker limitations (e.g., numpy's `ArrayLike` being too broad)

## Shell Command Style

- **Do NOT use compound `cd` commands** like `cd foo && git status` or `cd foo && pytest`. Compound commands that cross directories require explicit user approval and slow things down. Instead, issue a standalone `cd foo` command first, then run the action as a separate command. The Bash tool's working directory persists between calls.

## Testing Guidelines

**When CI tests fail, fix them.** Don't waste time analyzing whether failures are "related to your changes" — just fix all failing tests. The goal is a green CI, not attribution.

Prefer fewer, comprehensive integration tests that mimic manual testing over a large number of unit tests. We have no code coverage requirements—the goal is working features, not metrics.
- **Test results are in `test-results.xml`.** Pytest writes JUnit XML to `test-results.xml` automatically. When diagnosing failures, read this file — it contains test names, durations, failure messages, and captured output. This is more reliable than parsing console output.
- **NEVER run parol6 and web commander test suites in parallel** — no proper isolation, they share resources and have timing issues when resource-constrained. Always run sequentially.
- **NEVER allow subagents to run tests.** Many tests are timing-sensitive and the system doesn't have enough resources for agents and tests to run simultaneously. Only the main conversation should run tests, and only after all agents have completed.
Loading
Loading