Machine cycle-time and motion-sequence planning for automated equipment: will this machine hold its target cycle time, worst case, not on a good day?
Project description
cadence
Machine cycle-time and motion-sequence planning for automated equipment: will this machine hold its target cycle time - worst case, not on a good day? Pure Python, standard library only.
Given a station's movements (point-to-point moves and timed dwells), their precedence, and the controller's timing characteristics, cadence:
- Profiles each move into a duration from distance and kinematic limits,
with a selectable profile per movement: closed-form trapezoidal
(default) or jerk-limited S-curve (7-phase,
jmax), rest-to-rest. - Schedules the cycle - what runs in parallel - under precedence and shared-actuator constraints (one axis does one thing at a time).
- Adds sensor + PLC scan latency wherever a step waits on a sensor: sensor response + input filter + fieldbus + I/O update + task scan, with a latched (touch-probe) option that skips the scan tax.
- Reports a nominal and a worst-case cycle, the critical path, and per-movement slack - and judges the target against the worst case, because that is what upholds takt.
- Proves its own answer where it can: every schedule carries a lower bound on the makespan, so a result is either provably optimal or honestly labelled "within X ms of best possible".
- Tells you which knob to turn when takt is missed
(
suggest_levers): raise vmax/amax on a critical move, latch a sensor, hand off commanded, shorten a dwell, speed up the PLC task - each suggestion quantified by a re-solve and ranked by saving. - Models how machines are actually built: per-move settle time and selectable hand-off release modes - on completion, with a set distance remaining, or a set time early (event-based scheduling).
- Analyses the repeating cycle (
steady_state, CLI--cyclic): the tightest period the solved pattern can repeat at (pipelined), a pattern-independent takt lower bound, lead time vs takt, and the bottleneck - with the same provable-optimality trust model.
Install
pip install cadence-cycletime
The distribution is named cadence-cycletime (the bare name is reserved by an
empty project on PyPI); the import name is simply cadence:
import cadence
Quickstart
from cadence import Cell, Controller, Movement, Resource, solve
cell = Cell(
controller=Controller(task_ms=4, io_ms=1, filter_ms=1, remote_io=True, bus_ms=0.25),
resources=(
Resource(id="x", name="Gantry X"),
Resource(id="y", name="Gantry Y"),
Resource(id="z", name="Gantry Z"),
),
movements=(
Movement(id="move_x", resource="x", kind="move", dist=300, vmax=1500, amax=10000),
Movement(id="move_y", resource="y", kind="move", dist=200, vmax=1200, amax=8000,
preds=("move_x",)),
Movement(id="move_z", resource="z", kind="move", dist=100, vmax=800, amax=5000,
preds=("move_y",), trigger="sensor", sensor_ms=1.5),
),
target_ms=1000.0,
)
result = solve(cell)
print(f"Worst-case cycle: {result.worst_ms:.1f} ms (nominal {result.nominal_ms:.1f} ms)")
print(f"Meets target: {result.meets_target}")
print(f"Scan jitter: {result.scan_jitter_ms:.1f} ms")
print(f"Critical path: {result.worst.critical_path}")
Units are consistent pairs: distance mm (or deg for rotary axes), velocity mm/s, acceleration mm/s², time ms.
CLI
Cells serialize to a strict, versioned JSON format (cell_to_dict /
cell_from_dict), and the CLI solves a cell file directly - headline figures,
a per-movement table, an ASCII Gantt, and ranked improvement levers:
python -m cadence solve examples/pick_place.json --levers
python -m cadence solve station.json --json # machine-readable result
As a CI check
Because solve() needs no web stack, a design can be regression-checked in CI.
--check exits with code 2 when the worst case misses the target:
python -m cadence solve station.json --target 6000 --check
The latency model
For a sensor-confirmed hand-off, the time from physical event to the next move being commanded:
fixed = sensor_ms + filter_ms + (2 * bus_ms if remote_io else 0)
nominal = fixed + io_ms/2 + task_ms/2 # event sampled mid-window, mid-scan
worst = fixed + io_ms + task_ms # event just missed: full I/O + scan
worst - nominal is the scan jitter you must budget per gated handshake
on the critical path. A latched (touch-probe) input timestamps at the drive
cycle: nominal = worst = sensor_ms + bus. A commanded (drive-coordinated)
hand-off costs nothing.
Consumed by
- toolbox-app - the Cycle Planner tool (installed editable).
Status & roadmap
v0.2 schedules with a deterministic, latency-aware list heuristic, and
every result carries a scheduler-independent lower bound: at typical
station scale the heuristic is usually provably optimal
(SolveResult.approximate is False); otherwise the gap to best-possible is
reported honestly. Candidates for later, in evidence order: an exact CP-SAT
model (OR-Tools) if real cells show a nonzero gap, jerk-limited (S-curve)
profiling and servo settle time behind profile_move(), and move-overlap /
early-release hand-off semantics.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cadence_cycletime-0.4.0.tar.gz.
File metadata
- Download URL: cadence_cycletime-0.4.0.tar.gz
- Upload date:
- Size: 59.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df52e7b3c640e5ff26e55ec3dd7040a46a8be466b68d0fc0c989e309a3996e5b
|
|
| MD5 |
13503bde71995c0811effe10ea0fb6aa
|
|
| BLAKE2b-256 |
0c7a5f8a4aa640137bce8c543420edab083a45366c5c142ff7e7ca8c3bce7a3b
|
Provenance
The following attestation bundles were made for cadence_cycletime-0.4.0.tar.gz:
Publisher:
publish.yml on Mech-eng-au/cadence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cadence_cycletime-0.4.0.tar.gz -
Subject digest:
df52e7b3c640e5ff26e55ec3dd7040a46a8be466b68d0fc0c989e309a3996e5b - Sigstore transparency entry: 2071657278
- Sigstore integration time:
-
Permalink:
Mech-eng-au/cadence@cc7fb6cd290d43b72d758990b15955cbee6e2d51 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/Mech-eng-au
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cc7fb6cd290d43b72d758990b15955cbee6e2d51 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cadence_cycletime-0.4.0-py3-none-any.whl.
File metadata
- Download URL: cadence_cycletime-0.4.0-py3-none-any.whl
- Upload date:
- Size: 36.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2eabbf7152cd1e41fd748dc09c7f299fbfb4af28c9853aeaf97df93b3476871d
|
|
| MD5 |
3336852b93aebcd14b98a7ecb2434586
|
|
| BLAKE2b-256 |
22bb00870ea3d6bd0919f6a8598ad95c10eb62f8c716b5f08854b7bb1b26503f
|
Provenance
The following attestation bundles were made for cadence_cycletime-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on Mech-eng-au/cadence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cadence_cycletime-0.4.0-py3-none-any.whl -
Subject digest:
2eabbf7152cd1e41fd748dc09c7f299fbfb4af28c9853aeaf97df93b3476871d - Sigstore transparency entry: 2071657289
- Sigstore integration time:
-
Permalink:
Mech-eng-au/cadence@cc7fb6cd290d43b72d758990b15955cbee6e2d51 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/Mech-eng-au
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cc7fb6cd290d43b72d758990b15955cbee6e2d51 -
Trigger Event:
push
-
Statement type: