COPP C ABI
C interface for COPP trajectory optimization
Loading...
Searching...
No Matches
example_common.h
Go to the documentation of this file.
1#ifndef COPP_C_EXAMPLE_COMMON_H
2#define COPP_C_EXAMPLE_COMMON_H
3
4/*
5 * Shared helpers for the C tutorial examples.
6 *
7 * The repository examples all start from the same deterministic 3-axis
8 * Lissajous path:
9 *
10 * q0(s) = sin(2*pi*s + 0.0)
11 * q1(s) = sin(3*pi*s + 0.3)
12 * q2(s) = sin(5*pi*s + 0.7)
13 *
14 * The C API can model the same path without discretization error by creating
15 * a callback-backed `CoppPath`. The callbacks below provide q, dq/ds,
16 * d2q/ds2, and d3q/ds3 directly. `copp_path_from_parametric` is also available
17 * when users prefer writing q(s) with `CoppJet3` helpers and automatic
18 * derivative propagation. Keeping this common setup in one header lets each
19 * example focus on the solver-specific problem descriptor and options.
20 *
21 * For ordinary C users this callback path is only one construction option.
22 * If your path is tabulated instead of analytic, pass waypoint columns to
23 * `copp_path_from_waypoints` instead. COPP builds a spline path, and the same
24 * `copp_robot_sample_path_2nd` / `copp_robot_sample_path_3rd` calls used by
25 * these examples can sample derivatives from that spline.
26 */
27
28#include <float.h>
29#include <math.h>
30#include <stdio.h>
31
32#include "copp/copp.h"
33
34enum
35{
38};
39
40static int example_expect_ok(enum CoppStatus status, const char *call)
41{
42 if (status != COPP_STATUS_OK)
43 {
44 fprintf(stderr, "%s failed: %s\n", call, copp_status_message(status));
45 return 1;
46 }
47 return 0;
48}
49
50static void example_fill_stations(double *s, size_t n)
51{
52 for (size_t j = 0; j < n; ++j)
53 {
54 s[j] = (double)j / (double)(n - 1);
55 }
56}
57
58static void example_eval_lissajous_sample(double s,
59 double *q,
60 double *dq,
61 double *ddq,
62 double *dddq)
63{
64 const double pi = 3.14159265358979323846;
65 const double freq[EXAMPLE_DIM] = {2.0 * pi, 3.0 * pi, 5.0 * pi};
66 const double phase[EXAMPLE_DIM] = {0.0, 0.3, 0.7};
67
68 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
69 {
70 const double w = freq[i];
71 const double x = s * w + phase[i];
72 const double sin_x = sin(x);
73 const double cos_x = cos(x);
74 const double w_sq = w * w;
75
76 q[i] = sin_x;
77 dq[i] = cos_x * w;
78 ddq[i] = -sin_x * w_sq;
79 if (dddq != NULL)
80 {
81 dddq[i] = (-cos_x * w_sq) * w;
82 }
83 }
84}
85
86static enum CoppStatus example_evaluate_path_2nd(void *user_data,
87 size_t dim,
88 size_t n,
89 const double *s,
90 double *q,
91 double *dq,
92 double *ddq)
93{
94 (void)user_data;
95 if (dim != EXAMPLE_DIM)
96 {
98 }
99 if (n > 0 && (s == NULL || q == NULL || dq == NULL || ddq == NULL))
100 {
102 }
103
104 for (size_t j = 0; j < n; ++j)
105 {
106 double q_col[EXAMPLE_DIM];
107 double dq_col[EXAMPLE_DIM];
108 double ddq_col[EXAMPLE_DIM];
109 example_eval_lissajous_sample(s[j], q_col, dq_col, ddq_col, NULL);
110 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
111 {
112 q[i + j * dim] = q_col[i];
113 dq[i + j * dim] = dq_col[i];
114 ddq[i + j * dim] = ddq_col[i];
115 }
116 }
117
118 return COPP_STATUS_OK;
119}
120
121static enum CoppStatus example_evaluate_path_3rd(void *user_data,
122 size_t dim,
123 size_t n,
124 const double *s,
125 double *q,
126 double *dq,
127 double *ddq,
128 double *dddq)
129{
130 (void)user_data;
131 if (dim != EXAMPLE_DIM)
132 {
134 }
135 if (n > 0 && (s == NULL || q == NULL || dq == NULL || ddq == NULL || dddq == NULL))
136 {
138 }
139
140 for (size_t j = 0; j < n; ++j)
141 {
142 double q_col[EXAMPLE_DIM];
143 double dq_col[EXAMPLE_DIM];
144 double ddq_col[EXAMPLE_DIM];
145 double dddq_col[EXAMPLE_DIM];
146 example_eval_lissajous_sample(s[j], q_col, dq_col, ddq_col, dddq_col);
147 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
148 {
149 q[i + j * dim] = q_col[i];
150 dq[i + j * dim] = dq_col[i];
151 ddq[i + j * dim] = ddq_col[i];
152 dddq[i + j * dim] = dddq_col[i];
153 }
154 }
155
156 return COPP_STATUS_OK;
157}
158
159static int example_create_analytic_path_2nd(struct CoppPath **out_path)
160{
161 *out_path = NULL;
164 0.0,
165 1.0,
166 example_evaluate_path_2nd,
167 NULL,
168 out_path);
169 return example_expect_ok(status, "copp_path_from_evaluator_2nd");
170}
171
172static int example_create_analytic_path_3rd(struct CoppPath **out_path)
173{
174 *out_path = NULL;
177 0.0,
178 1.0,
179 example_evaluate_path_2nd,
180 example_evaluate_path_3rd,
181 NULL,
182 out_path);
183 return example_expect_ok(status, "copp_path_from_evaluator_3rd");
184}
185
186static int example_add_second_order_limits(struct CoppRobot *robot, size_t n)
187{
188 double vel_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
189 double vel_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
190 double acc_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
191 double acc_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
192
194 robot,
195 0,
196 n,
197 (struct CoppSliceF64){vel_max, EXAMPLE_DIM},
198 (struct CoppSliceF64){vel_min, EXAMPLE_DIM});
199 if (example_expect_ok(status, "copp_add_axial_velocity_limits"))
200 {
201 return 1;
202 }
203
205 robot,
206 0,
207 n,
208 (struct CoppSliceF64){acc_max, EXAMPLE_DIM},
209 (struct CoppSliceF64){acc_min, EXAMPLE_DIM});
210 return example_expect_ok(status, "copp_add_axial_acceleration_limits");
211}
212
213static int example_add_third_order_limits(struct CoppRobot *robot, size_t n)
214{
215 double jerk_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
216 double jerk_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
217
218 if (example_add_second_order_limits(robot, n))
219 {
220 return 1;
221 }
222
224 robot,
225 0,
226 n,
227 (struct CoppSliceF64){jerk_max, EXAMPLE_DIM},
228 (struct CoppSliceF64){jerk_min, EXAMPLE_DIM});
229 return example_expect_ok(status, "copp_add_axial_jerk_limits");
230}
231
232static int example_create_robot_2nd(const struct CoppPath *path,
233 const double *s,
234 size_t n,
235 struct CoppRobot **out_robot)
236{
237 *out_robot = NULL;
238 enum CoppStatus status = copp_robot_create(EXAMPLE_DIM, n, out_robot);
239 if (example_expect_ok(status, "copp_robot_create"))
240 {
241 return 1;
242 }
243
244 status = copp_robot_append_s(*out_robot, (struct CoppSliceF64){s, n});
245 if (example_expect_ok(status, "copp_robot_append_s"))
246 {
247 copp_robot_free(*out_robot);
248 *out_robot = NULL;
249 return 1;
250 }
251
252 status = copp_robot_sample_path_2nd(*out_robot, path, 0, n);
253 if (example_expect_ok(status, "copp_robot_sample_path_2nd") ||
254 example_add_second_order_limits(*out_robot, n))
255 {
256 copp_robot_free(*out_robot);
257 *out_robot = NULL;
258 return 1;
259 }
260
261 return 0;
262}
263
264static int example_create_robot_3rd(const struct CoppPath *path,
265 const double *s,
266 size_t n,
267 struct CoppRobot **out_robot)
268{
269 *out_robot = NULL;
270 enum CoppStatus status = copp_robot_create(EXAMPLE_DIM, n, out_robot);
271 if (example_expect_ok(status, "copp_robot_create"))
272 {
273 return 1;
274 }
275
276 status = copp_robot_append_s(*out_robot, (struct CoppSliceF64){s, n});
277 if (example_expect_ok(status, "copp_robot_append_s"))
278 {
279 copp_robot_free(*out_robot);
280 *out_robot = NULL;
281 return 1;
282 }
283
284 status = copp_robot_sample_path_3rd(*out_robot, path, 0, n);
285 if (example_expect_ok(status, "copp_robot_sample_path_3rd") ||
286 example_add_third_order_limits(*out_robot, n))
287 {
288 copp_robot_free(*out_robot);
289 *out_robot = NULL;
290 return 1;
291 }
292
293 return 0;
294}
295
296static int example_solve_topp2_seed(struct CoppRobot *robot, size_t n, struct CoppVecF64 *out_a)
297{
298 struct Topp2RaOptions options;
299 enum CoppStatus status = topp2_ra_default_options(&options);
300 if (example_expect_ok(status, "topp2_ra_default_options"))
301 {
302 return 1;
303 }
304
305 struct Topp2Problem problem = {robot, 0, n - 1, 0.0, 0.0};
306 status = topp2_ra(problem, options, out_a);
307 if (example_expect_ok(status, "topp2_ra"))
308 {
309 return 1;
310 }
311 if (out_a->data == NULL || out_a->len != n)
312 {
313 fprintf(stderr, "unexpected TOPP2 seed length\n");
314 return 1;
315 }
316 return 0;
317}
318
319static int example_seed_third_order_problem(struct CoppRobot *robot,
320 size_t n,
321 struct CoppVecF64 *out_a_seed)
322{
323 if (example_solve_topp2_seed(robot, n, out_a_seed))
324 {
325 return 1;
326 }
327
328 enum CoppStatus status =
329 copp_robot_amax_substitute(robot, (struct CoppSliceF64){out_a_seed->data, out_a_seed->len}, 0);
330 return example_expect_ok(status, "copp_robot_amax_substitute");
331}
332
333static int example_time_from_second_order(const double *s,
334 size_t n,
335 struct CoppVecF64 a,
336 double *out_t_final,
337 struct CoppVecF64 *out_t_s)
338{
339 enum CoppStatus status = copp_s_to_t_2nd(
340 (struct CoppSliceF64){s, n},
341 (struct CoppSliceF64){a.data, a.len},
342 0.0,
343 out_t_final,
344 out_t_s);
345 if (example_expect_ok(status, "copp_s_to_t_2nd"))
346 {
347 return 1;
348 }
349 if (!isfinite(*out_t_final) || *out_t_final <= 0.0 || *out_t_final >= DBL_MAX)
350 {
351 fprintf(stderr, "invalid second-order time profile\n");
352 return 1;
353 }
354 return 0;
355}
356
357static int example_time_from_third_order(const double *s,
358 size_t n,
359 struct CoppProfile3rd profile,
360 double *out_t_final,
361 struct CoppVecF64 *out_t_s)
362{
363 enum CoppStatus status = copp_s_to_t_3rd(
364 (struct CoppSliceF64){s, n},
365 (struct CoppSliceF64){profile.a.data, profile.a.len},
366 (struct CoppSliceF64){profile.b.data, profile.b.len},
367 profile.num_stationary_start,
368 profile.num_stationary_end,
369 0.0,
370 out_t_final,
371 out_t_s);
372 if (example_expect_ok(status, "copp_s_to_t_3rd"))
373 {
374 return 1;
375 }
376 if (!isfinite(*out_t_final) || *out_t_final <= 0.0 || *out_t_final >= DBL_MAX)
377 {
378 fprintf(stderr, "invalid third-order time profile\n");
379 return 1;
380 }
381 return 0;
382}
383
384static int example_interpolate_second_order(const double *s,
385 size_t n,
386 struct CoppVecF64 a,
387 struct CoppVecF64 t_s,
388 struct CoppVecF64 *out_s_t)
389{
391 (struct CoppSliceF64){s, n},
392 (struct CoppSliceF64){a.data, a.len},
393 (struct CoppSliceF64){t_s.data, t_s.len},
394 0.0,
395 1e-3,
396 true,
397 out_s_t);
398 return example_expect_ok(status, "copp_t_to_s_uniform_2nd");
399}
400
401static int example_interpolate_third_order(const double *s,
402 size_t n,
403 struct CoppProfile3rd profile,
404 struct CoppVecF64 t_s,
405 struct CoppVecF64 *out_s_t)
406{
408 (struct CoppSliceF64){s, n},
409 (struct CoppSliceF64){profile.a.data, profile.a.len},
410 (struct CoppSliceF64){profile.b.data, profile.b.len},
411 profile.num_stationary_start,
412 profile.num_stationary_end,
413 (struct CoppSliceF64){t_s.data, t_s.len},
414 0.0,
415 1e-3,
416 true,
417 out_s_t);
418 return example_expect_ok(status, "copp_t_to_s_uniform_3rd");
419}
420
421#endif /* COPP_C_EXAMPLE_COMMON_H */
@ EXAMPLE_NUM_POINTS
@ EXAMPLE_DIM
const char * copp_status_message(enum CoppStatus status)
Return a short static message for a COPP status code.
CoppStatus
C ABI status code returned by COPP FFI functions.
Definition core.h:34
@ COPP_STATUS_NULL_POINTER
A required pointer argument was null.
Definition core.h:42
@ COPP_STATUS_INVALID_ARGUMENT
An unsupported enum value or option was provided through the C ABI.
Definition core.h:54
@ COPP_STATUS_OK
Operation completed successfully.
Definition core.h:38
enum CoppStatus copp_s_to_t_3rd(struct CoppSliceF64 s, struct CoppSliceF64 a, struct CoppSliceF64 b, size_t num_stationary_start, size_t num_stationary_end, double t0, double *out_t_final, struct CoppVecF64 *out_t_s)
Compute cumulative TOPP3/COPP3 time profile t(s) from node profiles a(s) = dot{s}^2 and b(s) = ddot{s...
enum CoppStatus copp_t_to_s_uniform_3rd(struct CoppSliceF64 s, struct CoppSliceF64 a, struct CoppSliceF64 b, size_t num_stationary_start, size_t num_stationary_end, struct CoppSliceF64 t_s, double t0, double dt, bool include_final, struct CoppVecF64 *out_s_t)
Interpolate s(t) from TOPP3/COPP3 profiles using a uniform time grid.
enum CoppStatus copp_t_to_s_uniform_2nd(struct CoppSliceF64 s, struct CoppSliceF64 a, struct CoppSliceF64 t_s, double t0, double dt, bool include_final, struct CoppVecF64 *out_s_t)
Interpolate s(t) from TOPP2 profiles using a uniform time grid.
enum CoppStatus copp_s_to_t_2nd(struct CoppSliceF64 s, struct CoppSliceF64 a, double t0, double *out_t_final, struct CoppVecF64 *out_t_s)
Compute cumulative TOPP2 time profile t(s) from node profile a(s).
struct CoppPath CoppPath
Opaque C handle for a library-owned Path.
Definition path.h:36
enum CoppStatus copp_path_from_evaluator_2nd(size_t dim, double s_min, double s_max, CoppPathEvaluate2ndFn evaluate_2nd, void *user_data, struct CoppPath **out_path)
Build a path from a C callback that provides derivatives up to 2nd order.
enum CoppStatus copp_path_from_evaluator_3rd(size_t dim, double s_min, double s_max, CoppPathEvaluate2ndFn evaluate_2nd, CoppPathEvaluate3rdFn evaluate_3rd, void *user_data, struct CoppPath **out_path)
Build a path from C callbacks that provide derivatives up to 3rd order.
struct CoppRobot CoppRobot
Opaque C handle for a library-owned robot and constraint buffer.
Definition robot.h:35
void copp_robot_free(struct CoppRobot *robot)
Release a robot handle created by copp_robot_create.
enum CoppStatus copp_robot_amax_substitute(struct CoppRobot *robot, struct CoppSliceF64 amax, size_t idx_s)
Overwrite first-order bounds from a profile.
enum CoppStatus copp_add_axial_acceleration_limits(struct CoppRobot *robot, size_t start_idx_s, size_t len, struct CoppSliceF64 acceleration_max, struct CoppSliceF64 acceleration_min)
Add broadcast axial acceleration limits over an existing robot station interval.
enum CoppStatus copp_robot_append_s(struct CoppRobot *robot, struct CoppSliceF64 s)
Append strictly increasing station samples to a robot.
enum CoppStatus copp_robot_create(size_t dim, size_t capacity, struct CoppRobot **out_robot)
Create a library-owned robot handle for C callers.
enum CoppStatus copp_add_axial_velocity_limits(struct CoppRobot *robot, size_t start_idx_s, size_t len, struct CoppSliceF64 velocity_max, struct CoppSliceF64 velocity_min)
Add broadcast axial velocity limits over an existing robot station interval.
enum CoppStatus copp_add_axial_jerk_limits(struct CoppRobot *robot, size_t start_idx_s, size_t len, struct CoppSliceF64 jerk_max, struct CoppSliceF64 jerk_min)
Add broadcast axial jerk limits over an existing robot station interval.
enum CoppStatus copp_robot_sample_path_2nd(struct CoppRobot *robot, const struct CoppPath *path, size_t idx_s_from, size_t idx_s_to)
Sample a path over an existing robot station interval and store second-order derivatives.
enum CoppStatus copp_robot_sample_path_3rd(struct CoppRobot *robot, const struct CoppPath *path, size_t idx_s_from, size_t idx_s_to)
Sample a path over an existing robot station interval and store third-order derivatives.
enum CoppStatus topp2_ra_default_options(struct Topp2RaOptions *out_options)
Write default TOPP2-RA options into out_options.
enum CoppStatus topp2_ra(struct Topp2Problem problem, struct Topp2RaOptions options, struct CoppVecF64 *out_a)
Solve a TOPP2-RA problem.
Owned third-order profile returned by TOPP3/COPP3 C ABI solvers.
struct CoppVecF64 b
Node-based profile b[k] = ddot{s}_k.
size_t num_stationary_end
Effective stationary interval count at the end.
struct CoppVecF64 a
Node-based profile a[k] = dot{s}_k^2.
size_t num_stationary_start
Effective stationary interval count at the start.
Borrowed immutable f64 slice passed from C to COPP.
Definition core.h:781
Library-owned f64 vector returned to C.
Definition core.h:828
size_t len
Number of initialized elements.
Definition core.h:836
double * data
Pointer to the first element, or null for an empty vector.
Definition core.h:832
Borrowed value descriptor for a TOPP2 problem solved from C.
const struct CoppRobot * robot
Robot handle that owns the station-indexed constraint storage.
Options for TOPP2-RA reachability analysis.
Definition topp2.h:31