Skip to content

Commit d1187df

Browse files
committed
reduce default control loop rate to 100Hz
- change CONTROL_RATE_HZ default from 200 to 100 - document performance characteristics and tuning options - note that tests use 20Hz status rate
1 parent 8308531 commit d1187df

3 files changed

Lines changed: 39 additions & 15 deletions

File tree

‎CLAUDE.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PAROL6 Python API is a lightweight client and controller for PAROL6 6-DOF robot
2121
┌──────────────────▼──────────────────────┐
2222
│ Controller │
2323
│ (parol6/server/controller.py) │
24-
│ - 250 Hz control loop (configurable) │
24+
│ - 100 Hz control loop (configurable) │
2525
│ - Command queue & execution │
2626
│ - Status multicast broadcasting │
2727
└──────────────────┬──────────────────────┘
@@ -87,8 +87,8 @@ parol6-server --serial=/dev/ttyUSB0 --log-level=DEBUG
8787

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

‎README.md‎

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,38 @@ Adding a command (overview):
8181
- *If necessary* Add client method to `async_client.py` and `sync_client.py`.
8282

8383
## Control rate and performance
84-
- Default control loop: `PAROL6_CONTROL_RATE_HZ=250` (chosen for consistent cross-platform behavior)
85-
- Higher control rates require stronger hardware and generally produce smoother motion, but there are diminishing returns—the motion will not become infinitely smooth just by increasing the rate
86-
- If the controller is falling behind you will see warnings like:
8784

88-
`Control loop avg period degraded by +XX% (avg=Ys target=Zs); latest overrun=Ws`
85+
The default control loop rate is **100 Hz** (`PAROL6_CONTROL_RATE_HZ=100`). Higher rates up to 250 Hz and even 500 Hz are achievable, but there are diminishing returns in motion smoothness as you go higher.
8986

90-
If you see this, or motion feels inconsistent, reduce `PAROL6_CONTROL_RATE_HZ`
91-
- TRACE logging impacts performance; enable only when necessary (set `PAROL_TRACE=1` or use `--log-level=TRACE`)
87+
### Performance characteristics
88+
89+
The library has been optimized for real-time performance:
90+
- **Numba JIT compilation** for hot conversion functions (steps↔radians, degrees)
91+
- **NumPy vectorization** with pre-allocated buffers to minimize allocations
92+
- **C++ robotics-toolbox backend** for kinematics computations
93+
- **Very few hot-loop allocations** - most buffers are reused
94+
95+
Even under complete IK failure (worst-case computation), the control loop typically completes in **under 2ms**. However, consistent high-rate performance requires consideration of OS scheduling—the operating system commonly interrupts user-space processes, which can cause jitter at higher rates.
96+
97+
**Note:** Rates above 250 Hz may require increasing IK solving tolerance, as the distance moved per tick becomes smaller and numerical precision becomes a factor.
98+
99+
### Tuning for higher rates
100+
101+
For consistent high-rate performance:
102+
1. **Elevate process priority**: On Linux, use `nice -n -20` or `chrt -f 50` for real-time scheduling
103+
2. **Disable logging**: TRACE and DEBUG logging add significant overhead
104+
3. **Reduce background load**: Heavy background tasks compete for CPU time
105+
4. **Consider CPU isolation**: Pin the controller to dedicated cores with `taskset`
106+
107+
### Monitoring performance
108+
109+
If the controller is falling behind you will see warnings like:
110+
111+
```
112+
Control loop avg period degraded by +XX% (avg=Ys target=Zs); latest overrun=Ws
113+
```
114+
115+
If you see this consistently, or motion feels jittery, reduce `PAROL6_CONTROL_RATE_HZ` or address scheduling issues.
92116

93117
## Streaming mode
94118
- Streaming (`STREAM|ON` / `STREAM|OFF`; `client.stream_on()` / `client.stream_off()`) is intended for high-rate jogging and continuous updates
@@ -122,7 +146,7 @@ flowchart TB
122146
QUEUE["Command Queue<br/>(max 100)"]
123147
end
124148
125-
subgraph MainLoop["Main Control Loop (250 Hz)"]
149+
subgraph MainLoop["Main Control Loop (100 Hz)"]
126150
direction TB
127151
RX_SERIAL["1. Read Serial Frame"]
128152
ESTOP["2. E-Stop Check"]
@@ -145,7 +169,7 @@ flowchart TB
145169
JPATH["JointPath<br/>(N, 6) radians"]
146170
147171
BUILDER["TrajectoryBuilder"]
148-
TRAJ["Trajectory<br/>(M, 6) steps @ 250 Hz"]
172+
TRAJ["Trajectory<br/>(M, 6) steps @ 100 Hz"]
149173
end
150174
151175
subgraph Online["Online (real-time per-tick)"]
@@ -212,7 +236,7 @@ flowchart TB
212236
### Component summary
213237

214238
- **Client** (`parol6.client`): `AsyncRobotClient` (async UDP), `RobotClient` (sync wrapper), `ServerManager` (subprocess lifecycle)
215-
- **Controller** (`parol6.server.controller`): UDP command server, 250 Hz control loop, command queue, status broadcasting
239+
- **Controller** (`parol6.server.controller`): UDP command server, 100 Hz control loop, command queue, status broadcasting
216240
- **Motion pipeline** (`parol6.motion`): Offline trajectory generation (TOPP-RA, Ruckig, etc.) and online streaming executors
217241
- **Transports** (`parol6.server.transports`): `SerialTransport` (hardware), `MockSerialProcessAdapter` (simulator via shared memory)
218242
- **Status subscription** (`parol6.client.status_subscriber`): Multicast/unicast listener for push-based status updates
@@ -329,8 +353,8 @@ Add a new tool by extending `TOOL_CONFIGS` with a name, description, and `transf
329353

330354

331355
## Environment variables
332-
- `PAROL6_CONTROL_RATE_HZ` — control loop frequency in Hz (default 250)
333-
- `PAROL6_STATUS_RATE_HZ` — STATUS broadcast rate in Hz (default 50)
356+
- `PAROL6_CONTROL_RATE_HZ` — control loop frequency in Hz (default 100)
357+
- `PAROL6_STATUS_RATE_HZ` — STATUS broadcast rate in Hz (default 50; tests use 20 Hz to reduce CI load)
334358
- `PAROL6_STATUS_STALE_S` — skip broadcast if cache is older than this (default 0.2)
335359
- `PAROL6_MCAST_GROUP` — multicast group for status (default 239.255.0.101)
336360
- `PAROL6_MCAST_PORT` — multicast port for status (default 50510)

‎parol6/config.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _trace(self, msg, *args, **kwargs):
3535
logger = logging.getLogger(__name__)
3636

3737
# Default control/sample rates (Hz)
38-
CONTROL_RATE_HZ: float = float(os.getenv("PAROL6_CONTROL_RATE_HZ", "200"))
38+
CONTROL_RATE_HZ: float = float(os.getenv("PAROL6_CONTROL_RATE_HZ", "100"))
3939

4040
DEFAULT_ACCEL_PERCENT: float = 100.0
4141

0 commit comments

Comments
 (0)