copp::Robot is the high-level way to turn path samples and physical limits into solver constraints. copp::Constraints is the lower-level mathematical container for users who already know the TOPP/COPP constraint rows they want.
Include
or:
Constraint Variables
The second-order state is:
\[a(s) = \dot{s}^2, \qquad b(s) = \ddot{s}.
\]
The third-order control convention is:
\[c(s) = \frac{s^{(3)}}{\dot{s}},
\qquad s^{(3)} = \frac{d^3s}{dt^3}.
\]
Raw constraints follow these sampled forms:
\[0 \leq a_k \leq a_{\max,k},
\]
\[R^\mathrm{acc}_{a,k} a_k + R^\mathrm{acc}_{b,k} b_k
\leq R^\mathrm{acc}_{\max,k},
\]
\[\sqrt{a_k}\,
\left(
R^\mathrm{jerk}_{a,k} a_k +
R^\mathrm{jerk}_{b,k} b_k +
R^\mathrm{jerk}_{c,k} c_k +
R^\mathrm{jerk}_{d,k}
\right)
\leq R^\mathrm{jerk}_{\max,k}.
\]
Robot Workflow
Use Robot when you have physical joint-space limits. The robot stores the station grid, sampled path derivatives, and converted constraints.
return std::vector<copp::Jet3>{
};
},
0.0,
1.0);
std::vector<double> s{0.0, 0.25, 0.5, 0.75, 1.0};
std::vector<double> upper{3.0, 3.0};
std::vector<double> lower{-3.0, -3.0};
robot.append_s(s)
.set_q_from_path_3rd(path, 0, s.size())
.add_velocity_limits(upper, lower, 0, s.size())
.add_acceleration_limits(upper, lower, 0, s.size())
.add_jerk_limits(upper, lower, 0, s.size());
static Path from_parametric(PathParametric parametric, double s_min, double s_max)
Build a path from a scalar-parametric formula.
Robot facade backed by Rust Robot<CppRobotModel>.
Jet3 sin(Jet3 x) noexcept
Jet3 powi(Jet3 x, int n) noexcept
Third-order forward-mode automatic-differentiation scalar.
Broadcast limit vectors have length dim. Matrix limits have shape (dim x n_samples):
{3.0, 3.0, 3.0},
{2.0, 2.0, 2.0},
});
{-3.0, -3.0, -3.0},
{-2.0, -2.0, -2.0},
});
robot.add_velocity_limits(upper_matrix.view(), lower_matrix.view(), 0);
static Matrix from_rows(std::initializer_list< std::initializer_list< double > > rows)
Build a matrix from row-major initializer-list notation.
robot.constraints() returns a borrowed ConstraintsRef for solver problems. The Robot must outlive the solver call.
Inverse Dynamics
Torque limits and torque objectives use the installed inverse-dynamics callback. Without a callback, COPP uses point-mass dynamics:
\[\tau = \ddot{q}.
\]
2,
tau[0] = 1.5 * ddq[0] + 0.1 * dq[0] + std::sin(q[0]);
tau[1] = 0.8 * ddq[1] + 0.05 * dq[1] + 0.5 * std::sin(q[1]);
},
s.size());
robot.append_s(s)
.set_q_from_path_2nd(path, 0, s.size());
std::vector<double> tau_upper{5.0, 5.0};
std::vector<double> tau_lower{-5.0, -5.0};
robot.add_torque_limits(tau_upper, tau_lower, 0, s.size());
Non-owning view of a contiguous one-dimensional array.
The callback is owned by Robot. If it captures references, those referenced objects must outlive the robot or at least the torque-related operation.
Standalone Constraints
Use Constraints directly when you already have mathematical rows:
constraints.append_s(s)
.add_constraint_1st(std::vector<double>{1.0, 0.8, 1.0}, 0);
{0.0, 0.0, 0.0},
{0.0, 0.0, 0.0},
});
{1.0, 1.0, 1.0},
{-1.0, -1.0, -1.0},
});
{2.0, 2.0, 2.0},
{2.0, 2.0, 2.0},
});
constraints.add_constraint_2nd(acc_a.view(), acc_b.view(), acc_max.view(), 0);
Owning raw constraint-buffer facade.
This mirrors the Rust/Python split: use Robot for physical limits, and use Constraints for direct mathematical control.
Tutorial Sources