Quickstart¶
The shortest reliable Python workflow is:
Build a geometric path
q(s).Sample the path on a station grid
sand store path derivatives in aRobotorConstraintsbuffer.Add physical limits.
Build a TOPP or COPP problem descriptor.
Run a solver and convert the path-domain profile into time-domain samples.
The second-order state used by TOPP2/COPP2 is
TOPP2/COPP2 solvers return the node profile a. The helper
s_to_t_topp2 integrates it into arrival times, and t_to_s_topp2_uniform
or t_to_s_topp2_samples inverts the mapping for controller or plotting
samples.
Minimal TOPP2-RA Example¶
The following example constructs an analytic path, applies symmetric velocity
and acceleration limits, solves TOPP2-RA, and samples s(t) on a uniform
time grid.
1"""Use TOPP2-RA to convert a JAX-defined path into a second-order time-optimal trajectory.
2
3The example constrains each axis by velocity and acceleration limits in
4``[-1, 1]``, then converts the returned ``a(s)`` profile into ``t(s)`` and
5uniform ``s(t)`` samples.
6"""
7
8import numpy as np
9
10import copp_py as copp
11
12
13def main() -> None:
14 try:
15 import jax
16 import jax.numpy as jnp
17 except ImportError as exc:
18 raise SystemExit(
19 'Install JAX to run this example: python -m pip install "copp-py[jax]"'
20 ) from exc
21
22 jax.config.update("jax_enable_x64", True)
23
24 dim = 3
25 n = 1001
26 dt = 1.0e-3
27
28 # 1) Define q(s). Path.from_jax differentiates it up to third order.
29 def q_fn(s):
30 freq = jnp.array([2.0 * jnp.pi, 3.0 * jnp.pi, 5.0 * jnp.pi], dtype=jnp.float64)
31 phase = jnp.array([0.0, 0.3, 0.7], dtype=jnp.float64)
32 return jnp.sin(freq * s + phase)
33
34 path = copp.Path.from_jax(q_fn, 0.0, 1.0)
35 s = np.linspace(0.0, 1.0, n, dtype=np.float64)
36
37 # 2) Build robot constraints (3-axis), then apply symmetric limits
38 # velocity/acceleration = [-1, 1].
39 robot = copp.Robot(dim, capacity=n)
40 robot.append_s(s)
41 robot.set_q_from_path_2nd(path, 0, n)
42
43 upper = np.ones(dim, dtype=np.float64)
44 lower = -upper
45 robot.add_velocity_limits(upper, lower, start_idx_s=0, length=n)
46 robot.add_acceleration_limits(upper, lower, start_idx_s=0, length=n)
47
48 # 3) Solve TOPP2-RA with boundary values a(0) = 0 and a(1) = 0.
49 problem = copp.solver.topp2_ra.Problem(
50 robot.constraints,
51 idx_s_interval=(0, n - 1),
52 a_boundary=(0.0, 0.0),
53 )
54 options = copp.solver.topp2_ra.Options()
55 a_ra = copp.solver.topp2_ra.solve(problem, options)
56
57 # 4) Post-process TOPP2-RA results: a(s) -> t(s) -> s(t).
58 t_final, t_s = copp.interpolation.s_to_t_topp2(s, a_ra, 0.0)
59 s_t = copp.interpolation.t_to_s_topp2_uniform(
60 s,
61 a_ra,
62 t_s,
63 dt,
64 t0=0.0,
65 include_final=True,
66 )
67
68 # 5) Print the tutorial summary.
69 print("TOPP2-RA done.")
70 print(f"dim = {dim}, N = {n}")
71 print(f"t_final = {t_final:.6f} s")
72 print(f"a_profile.len() = {len(a_ra)}")
73 print(f"s(t) samples = {len(s_t)}")
74
75
76if __name__ == "__main__":
77 main()
Import Style¶
The package follows the Rust crate layout. Solver entry points live under
copp.solver with one module per algorithm:
import copp_py as copp
problem = copp.solver.topp2_ra.Problem(...)
options = copp.solver.topp2_ra.Options()
a_profile = copp.solver.topp2_ra.solve(problem, options)
Modeling helpers live in focused namespaces such as copp.path,
copp.robot, copp.constraints, copp.objective,
copp.interpolation, and copp.clarabel. A few common modeling types are
also re-exported at package root for interactive use, but solver-specific
classes and functions are intentionally kept in copp.solver.
Next Steps¶
Read Optimal Path Parameterization for the variables and constraint model, then choose a
solver with Solver Selection. The tutorial pages embed the
same runnable files stored in bindings/python/examples.