TOPP2-RA

TOPP2-RA is the recommended first solver to try. It solves a time-optimal second-order problem and returns the node profile

\[a(s) = \dot{s}^2.\]

The example uses an analytic Lissajous path, samples it on N stations, adds symmetric velocity and acceleration limits, and solves with boundary values a(0)=0 and a(1)=0.

Key Calls

Path.from_evaluator_2nd wraps a Python object that provides evaluate_up_to_2nd(s) -> (q, dq, ddq). Robot.set_q_from_path_2nd stores those derivatives in the constraint buffer. Robot.add_velocity_limits and Robot.add_acceleration_limits convert physical axis limits into sampled path-domain rows.

After solving, s_to_t_topp2 computes the arrival-time profile and t_to_s_topp2_uniform samples s(t) at a controller-friendly time step.

Runnable Example

 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()