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. Keeping this common setup in one header lets
17 * each example focus on the solver-specific problem descriptor and options.
18 *
19 * For ordinary C users this callback path is only one construction option.
20 * If you do not have analytic derivatives or automatic differentiation, pass
21 * waypoint columns to `copp_path_from_waypoints` instead. COPP builds a spline
22 * path, and the same `copp_robot_sample_path_2nd` / `copp_robot_sample_path_3rd`
23 * calls used by these examples can sample derivatives from that spline.
24 */
25
26#include <float.h>
27#include <math.h>
28#include <stdio.h>
29
30#include "copp/copp.h"
31
32enum
33{
36};
37
38static int example_expect_ok(enum CoppStatus status, const char *call)
39{
40 if (status != COPP_STATUS_OK)
41 {
42 fprintf(stderr, "%s failed: %s\n", call, copp_status_message(status));
43 return 1;
44 }
45 return 0;
46}
47
48static void example_fill_stations(double *s, size_t n)
49{
50 for (size_t j = 0; j < n; ++j)
51 {
52 s[j] = (double)j / (double)(n - 1);
53 }
54}
55
56static void example_eval_lissajous_sample(double s,
57 double *q,
58 double *dq,
59 double *ddq,
60 double *dddq)
61{
62 const double pi = 3.14159265358979323846;
63 const double freq[EXAMPLE_DIM] = {2.0 * pi, 3.0 * pi, 5.0 * pi};
64 const double phase[EXAMPLE_DIM] = {0.0, 0.3, 0.7};
65
66 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
67 {
68 const double w = freq[i];
69 const double x = s * w + phase[i];
70 const double sin_x = sin(x);
71 const double cos_x = cos(x);
72 const double w_sq = w * w;
73
74 q[i] = sin_x;
75 dq[i] = cos_x * w;
76 ddq[i] = -sin_x * w_sq;
77 if (dddq != NULL)
78 {
79 dddq[i] = (-cos_x * w_sq) * w;
80 }
81 }
82}
83
84static enum CoppStatus example_evaluate_path_2nd(void *user_data,
85 size_t dim,
86 size_t n,
87 const double *s,
88 double *q,
89 double *dq,
90 double *ddq)
91{
92 (void)user_data;
93 if (dim != EXAMPLE_DIM)
94 {
96 }
97 if (n > 0 && (s == NULL || q == NULL || dq == NULL || ddq == NULL))
98 {
100 }
101
102 for (size_t j = 0; j < n; ++j)
103 {
104 double q_col[EXAMPLE_DIM];
105 double dq_col[EXAMPLE_DIM];
106 double ddq_col[EXAMPLE_DIM];
107 example_eval_lissajous_sample(s[j], q_col, dq_col, ddq_col, NULL);
108 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
109 {
110 q[i + j * dim] = q_col[i];
111 dq[i + j * dim] = dq_col[i];
112 ddq[i + j * dim] = ddq_col[i];
113 }
114 }
115
116 return COPP_STATUS_OK;
117}
118
119static enum CoppStatus example_evaluate_path_3rd(void *user_data,
120 size_t dim,
121 size_t n,
122 const double *s,
123 double *q,
124 double *dq,
125 double *ddq,
126 double *dddq)
127{
128 (void)user_data;
129 if (dim != EXAMPLE_DIM)
130 {
132 }
133 if (n > 0 && (s == NULL || q == NULL || dq == NULL || ddq == NULL || dddq == NULL))
134 {
136 }
137
138 for (size_t j = 0; j < n; ++j)
139 {
140 double q_col[EXAMPLE_DIM];
141 double dq_col[EXAMPLE_DIM];
142 double ddq_col[EXAMPLE_DIM];
143 double dddq_col[EXAMPLE_DIM];
144 example_eval_lissajous_sample(s[j], q_col, dq_col, ddq_col, dddq_col);
145 for (size_t i = 0; i < EXAMPLE_DIM; ++i)
146 {
147 q[i + j * dim] = q_col[i];
148 dq[i + j * dim] = dq_col[i];
149 ddq[i + j * dim] = ddq_col[i];
150 dddq[i + j * dim] = dddq_col[i];
151 }
152 }
153
154 return COPP_STATUS_OK;
155}
156
157static int example_create_analytic_path_2nd(struct CoppPath **out_path)
158{
159 *out_path = NULL;
162 0.0,
163 1.0,
164 example_evaluate_path_2nd,
165 NULL,
166 out_path);
167 return example_expect_ok(status, "copp_path_from_evaluator_2nd");
168}
169
170static int example_create_analytic_path_3rd(struct CoppPath **out_path)
171{
172 *out_path = NULL;
175 0.0,
176 1.0,
177 example_evaluate_path_2nd,
178 example_evaluate_path_3rd,
179 NULL,
180 out_path);
181 return example_expect_ok(status, "copp_path_from_evaluator_3rd");
182}
183
184static int example_add_second_order_limits(struct CoppRobot *robot, size_t n)
185{
186 double vel_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
187 double vel_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
188 double acc_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
189 double acc_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
190
192 robot,
193 0,
194 n,
195 (struct CoppSliceF64){vel_max, EXAMPLE_DIM},
196 (struct CoppSliceF64){vel_min, EXAMPLE_DIM});
197 if (example_expect_ok(status, "copp_add_axial_velocity_limits"))
198 {
199 return 1;
200 }
201
203 robot,
204 0,
205 n,
206 (struct CoppSliceF64){acc_max, EXAMPLE_DIM},
207 (struct CoppSliceF64){acc_min, EXAMPLE_DIM});
208 return example_expect_ok(status, "copp_add_axial_acceleration_limits");
209}
210
211static int example_add_third_order_limits(struct CoppRobot *robot, size_t n)
212{
213 double jerk_max[EXAMPLE_DIM] = {1.0, 1.0, 1.0};
214 double jerk_min[EXAMPLE_DIM] = {-1.0, -1.0, -1.0};
215
216 if (example_add_second_order_limits(robot, n))
217 {
218 return 1;
219 }
220
222 robot,
223 0,
224 n,
225 (struct CoppSliceF64){jerk_max, EXAMPLE_DIM},
226 (struct CoppSliceF64){jerk_min, EXAMPLE_DIM});
227 return example_expect_ok(status, "copp_add_axial_jerk_limits");
228}
229
230static int example_create_robot_2nd(const struct CoppPath *path,
231 const double *s,
232 size_t n,
233 struct CoppRobot **out_robot)
234{
235 *out_robot = NULL;
236 enum CoppStatus status = copp_robot_create(EXAMPLE_DIM, n, out_robot);
237 if (example_expect_ok(status, "copp_robot_create"))
238 {
239 return 1;
240 }
241
242 status = copp_robot_append_s(*out_robot, (struct CoppSliceF64){s, n});
243 if (example_expect_ok(status, "copp_robot_append_s"))
244 {
245 copp_robot_free(*out_robot);
246 *out_robot = NULL;
247 return 1;
248 }
249
250 status = copp_robot_sample_path_2nd(*out_robot, path, 0, n);
251 if (example_expect_ok(status, "copp_robot_sample_path_2nd") ||
252 example_add_second_order_limits(*out_robot, n))
253 {
254 copp_robot_free(*out_robot);
255 *out_robot = NULL;
256 return 1;
257 }
258
259 return 0;
260}
261
262static int example_create_robot_3rd(const struct CoppPath *path,
263 const double *s,
264 size_t n,
265 struct CoppRobot **out_robot)
266{
267 *out_robot = NULL;
268 enum CoppStatus status = copp_robot_create(EXAMPLE_DIM, n, out_robot);
269 if (example_expect_ok(status, "copp_robot_create"))
270 {
271 return 1;
272 }
273
274 status = copp_robot_append_s(*out_robot, (struct CoppSliceF64){s, n});
275 if (example_expect_ok(status, "copp_robot_append_s"))
276 {
277 copp_robot_free(*out_robot);
278 *out_robot = NULL;
279 return 1;
280 }
281
282 status = copp_robot_sample_path_3rd(*out_robot, path, 0, n);
283 if (example_expect_ok(status, "copp_robot_sample_path_3rd") ||
284 example_add_third_order_limits(*out_robot, n))
285 {
286 copp_robot_free(*out_robot);
287 *out_robot = NULL;
288 return 1;
289 }
290
291 return 0;
292}
293
294static int example_solve_topp2_seed(struct CoppRobot *robot, size_t n, struct CoppVecF64 *out_a)
295{
296 struct Topp2RaOptions options;
297 enum CoppStatus status = topp2_ra_default_options(&options);
298 if (example_expect_ok(status, "topp2_ra_default_options"))
299 {
300 return 1;
301 }
302
303 struct Topp2Problem problem = {robot, 0, n - 1, 0.0, 0.0};
304 status = topp2_ra(problem, options, out_a);
305 if (example_expect_ok(status, "topp2_ra"))
306 {
307 return 1;
308 }
309 if (out_a->data == NULL || out_a->len != n)
310 {
311 fprintf(stderr, "unexpected TOPP2 seed length\n");
312 return 1;
313 }
314 return 0;
315}
316
317static int example_seed_third_order_problem(struct CoppRobot *robot,
318 size_t n,
319 struct CoppVecF64 *out_a_seed)
320{
321 if (example_solve_topp2_seed(robot, n, out_a_seed))
322 {
323 return 1;
324 }
325
326 enum CoppStatus status =
327 copp_robot_amax_substitute(robot, (struct CoppSliceF64){out_a_seed->data, out_a_seed->len}, 0);
328 return example_expect_ok(status, "copp_robot_amax_substitute");
329}
330
331static int example_time_from_second_order(const double *s,
332 size_t n,
333 struct CoppVecF64 a,
334 double *out_t_final,
335 struct CoppVecF64 *out_t_s)
336{
337 enum CoppStatus status = copp_s_to_t_2nd(
338 (struct CoppSliceF64){s, n},
339 (struct CoppSliceF64){a.data, a.len},
340 0.0,
341 out_t_final,
342 out_t_s);
343 if (example_expect_ok(status, "copp_s_to_t_2nd"))
344 {
345 return 1;
346 }
347 if (!isfinite(*out_t_final) || *out_t_final <= 0.0 || *out_t_final >= DBL_MAX)
348 {
349 fprintf(stderr, "invalid second-order time profile\n");
350 return 1;
351 }
352 return 0;
353}
354
355static int example_time_from_third_order(const double *s,
356 size_t n,
357 struct CoppProfile3rd profile,
358 double *out_t_final,
359 struct CoppVecF64 *out_t_s)
360{
361 enum CoppStatus status = copp_s_to_t_3rd(
362 (struct CoppSliceF64){s, n},
363 (struct CoppSliceF64){profile.a.data, profile.a.len},
364 (struct CoppSliceF64){profile.b.data, profile.b.len},
365 profile.num_stationary_start,
366 profile.num_stationary_end,
367 0.0,
368 out_t_final,
369 out_t_s);
370 if (example_expect_ok(status, "copp_s_to_t_3rd"))
371 {
372 return 1;
373 }
374 if (!isfinite(*out_t_final) || *out_t_final <= 0.0 || *out_t_final >= DBL_MAX)
375 {
376 fprintf(stderr, "invalid third-order time profile\n");
377 return 1;
378 }
379 return 0;
380}
381
382static int example_interpolate_second_order(const double *s,
383 size_t n,
384 struct CoppVecF64 a,
385 struct CoppVecF64 t_s,
386 struct CoppVecF64 *out_s_t)
387{
389 (struct CoppSliceF64){s, n},
390 (struct CoppSliceF64){a.data, a.len},
391 (struct CoppSliceF64){t_s.data, t_s.len},
392 0.0,
393 1e-3,
394 true,
395 out_s_t);
396 return example_expect_ok(status, "copp_t_to_s_uniform_2nd");
397}
398
399static int example_interpolate_third_order(const double *s,
400 size_t n,
401 struct CoppProfile3rd profile,
402 struct CoppVecF64 t_s,
403 struct CoppVecF64 *out_s_t)
404{
406 (struct CoppSliceF64){s, n},
407 (struct CoppSliceF64){profile.a.data, profile.a.len},
408 (struct CoppSliceF64){profile.b.data, profile.b.len},
409 profile.num_stationary_start,
410 profile.num_stationary_end,
411 (struct CoppSliceF64){t_s.data, t_s.len},
412 0.0,
413 1e-3,
414 true,
415 out_s_t);
416 return example_expect_ok(status, "copp_t_to_s_uniform_3rd");
417}
418
419#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:34
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