Skip to content

Commit 7878796

Browse files
Added jerk capacities through cox-deboor algorithm (up to n-th derivative) (#44)
* Added jerk through compute_basis_derivative() * Merge errors fixed * fixed Bspline Vel + Acc test * Modified test slightly * Commented out demo3 in cmake list
1 parent 1e1bdad commit 7878796

8 files changed

Lines changed: 421 additions & 159 deletions

File tree

blast/blast_manipulator.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct ManipulatorTempData {
152152
*
153153
* @note Use the constructor to fully specify at least limits and kinematics.
154154
*/
155+
// todo: Add templating with number of joints instead of a global MAX_JOINTS
155156
struct Manipulator {
156157
u32 n_joints = 0;
157158

@@ -180,7 +181,7 @@ struct Manipulator {
180181
Vec3 p_j0 = {0, 0, 0};
181182
std::array<Vec3, MAX_JOINTS> dv{}; // to next joint
182183
std::array<Vec3, MAX_JOINTS> ev{}; // axis dirs
183-
std::array<Mat3, MAX_JOINTS + 1> Q_static{}; // static rotations
184+
std::array<Mat3, MAX_JOINTS + 1> Q_static{}; // static rotations todo: Why not Q_j0 as well instead of random bigger vector?
184185

185186
// Link dynamics
186187
std::array<real, MAX_JOINTS> m{};

blast/blast_trajectory.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct Bspline {
2929
Matrix basis_p; // n_ctrl x n_points
3030
Matrix basis_v; // n_ctrl x n_points
3131
Matrix basis_a; // n_ctrl x n_points
32+
std::vector<Matrix> basis; // n_ctrl x n_points, storing for derivatives up to desired "d"
3233
u32 n_joints;
3334
u32 n_points;
3435
u32 n_ctrl;
@@ -58,6 +59,7 @@ struct Bspline {
5859
inline blast_fn u32 x_len(const Matrix& task) const;
5960

6061
inline blast_fn void compute_basis();
62+
inline blast_fn void compute_basis_derivative(int d);
6163
inline blast_fn void compute_basis_open();
6264
inline blast_fn void compute_control(const Array& x, const Matrix& task, real* dst) const;
6365
};

blast/optimization/optimization.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct Result {
1919
bool success = false;
2020
bool success_false = false;
2121
real compute_time = 0;
22-
Optimization* opt; // todo: fix this (pointer becomes out of bounds fast)
22+
Optimization* opt; //todo: fix this (pointer becomes out of bounds fast)
2323
Array x;
2424
Array x0;
2525
nlopt_result nlopt_exit_criteria = NLOPT_SUCCESS;
@@ -430,13 +430,13 @@ inline void n_con_with_segments(Optimization* opt) {
430430
}
431431

432432
inline Result optimize_with_segments(Optimization* opt, u32 output_steps_ms = 1 /*ms*/) {
433-
auto T1 = get_tick_us();
434-
433+
auto T1 = get_tick_us();
434+
435435
// Initialization
436436
// configure_internal_data(opt); // todo: Ensure we can remove
437437
initialize_optimization_with_segments(opt);
438438
n_con_with_segments(opt);
439-
439+
440440
Result result(opt); // todo: this is expensive
441441
result.opt->task = opt->task;
442442

blast/trajectory/bspline.hpp

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,129 @@ inline blast_fn void Bspline::compute_basis() {
110110
}
111111
}
112112

113+
inline int uniformClampedSpan(real u, int n_ctrl, int p) {
114+
if (u <= 0.0) {
115+
return p;
116+
} else if (u >= 1.0) {
117+
return n_ctrl - 1;
118+
} else {
119+
const int n_spans = n_ctrl - p;
120+
int span = p + int(u * n_spans);
121+
return std::min(span, n_ctrl - 1);
122+
}
123+
}
124+
125+
// Computes Basis functions up to d, with d = 0 -> position, d = 1 -> velocity, etc.
126+
inline blast_fn void Bspline::compute_basis_derivative(int d) {
127+
basis.resize(d+1, Matrix(n_ctrl, n_points));
128+
129+
u32 m = n_ctrl + p;
130+
Array knots(m + 1);
131+
{
132+
for (u32 i = m; i > m - p - 1; i--)
133+
knots[i] = 1.0f;
134+
const real du = 1.0f / (real) (m + 1 - 2 * (p + 1) + 1);
135+
for (u32 i = p + 1; i < m - p; i++)
136+
knots[i] = knots[i - 1] + du;
137+
}
138+
139+
Array N(m * (p + 1)); // triangle basis function
140+
const real du = 1.0f / (n_points - 1);
141+
142+
for (u32 point = 0; point < n_points; point++) {
143+
const real u = point * du;
144+
145+
// note: could save if statements if we hard-coded span calculation since we know u is always within [0, 1]
146+
const int span = uniformClampedSpan(u, n_ctrl, p);
147+
const int first = span - p;
148+
149+
// --- Algorithm A2.2 (ndu table) ---
150+
Matrix ndu(p + 1, p + 1);
151+
Array left(p + 1), right(p + 1);
152+
153+
ndu(0, 0) = 1.0;
154+
155+
for (int j = 1; j <= p; ++j) {
156+
left[j] = u - knots[span + 1 - j];
157+
right[j] = knots[span + j] - u;
158+
159+
real saved = 0.0;
160+
for (int r = 0; r < j; ++r) {
161+
ndu(j, r) = right[r + 1] + left[j - r];
162+
real temp = ndu(r, j - 1) / ndu(j, r);
163+
164+
ndu(r, j) = saved + right[r + 1] * temp;
165+
saved = left[j - r] * temp;
166+
}
167+
ndu(j, j) = saved;
168+
}
169+
170+
// --- Derivative computation (Algorithm A2.3) ---
171+
Matrix ders(d + 1, p + 1);
172+
for (int j = 0; j <= p; ++j)
173+
ders(0, j) = ndu(j, p);
174+
175+
// Working array a[2][p+1]
176+
Matrix a(2, p + 1);
177+
178+
for (int r = 0; r <= p; ++r) {
179+
int s1 = 0, s2 = 1;
180+
a(0, 0) = 1.0;
181+
182+
for (int k = 1; k <= d; ++k) {
183+
real d = 0.0;
184+
int rk = r - k;
185+
int pk = p - k;
186+
187+
int j1;
188+
int j2;
189+
190+
if (r >= k) {
191+
a(s2, 0) = a(s1, 0) / ndu(pk + 1, rk);
192+
d = a(s2, 0) * ndu(rk, pk);
193+
}
194+
if (rk >= -1) {
195+
j1 = 1;
196+
} else {
197+
j1 = -rk;
198+
}
199+
200+
if (r - 1 <= pk) {
201+
j2 = k - 1;
202+
} else {
203+
j2 = p - r;
204+
}
205+
206+
for (int j = j1; j <= j2; j++) {
207+
a(s2, j) = (a(s1, j) - a(s1, j - 1)) / ndu(pk + 1, rk + j);
208+
d += a(s2, j) * ndu(rk + j, pk);
209+
}
210+
211+
if (r <= pk) {
212+
a(s2, k) = -a(s1, k - 1) / ndu(pk + 1, r);
213+
d += a(s2, k) * ndu(r, pk);
214+
}
215+
216+
ders(k, r) = d;
217+
std::swap(s1, s2);
218+
}
219+
}
220+
221+
// --- Multiply by factorial terms ---
222+
real factor = real(p);
223+
for (int k = 1; k <= d; ++k) {
224+
for (int j = 0; j <= p; ++j)
225+
ders(k, j) *= factor;
226+
factor *= real(p - k);
227+
}
228+
229+
// --- Scatter into global result ---
230+
for (int k = 0; k <= d; ++k)
231+
for (int j = 0; j <= p; ++j)
232+
basis[k](first + j, point) = ders(k, j);
233+
}
234+
}
235+
113236
inline blast_fn void Bspline::compute_basis_open() {
114237
u32 m = n_ctrl + p;
115238

@@ -259,4 +382,122 @@ inline blast_fn void Bspline::compute_trajectory(const Array& x, const Matrix& t
259382
}
260383
}
261384

385+
// Algorithm A2.3 – Basis Function Derivatives (Piegl & Tiller)
386+
// Returns the deriv_order-th derivative of all non-zero B-spline basis functions
387+
// aligned with the control point indices.
388+
// Input
389+
// u : Point of evaluation, within [0, 1] (u)
390+
// i : u is between knot i and knot i+1 (span)
391+
// p : Bspline degree (p)
392+
// n : Derivative degree (deriv_order)
393+
// U : Knot vector (knots)
394+
// Output
395+
// ders : Basis functions for derivative (result)
396+
inline Array BsplineDerivative_book(real u, int n_ctrl, int p, int deriv_order) {
397+
ZoneScoped;
398+
399+
Array result(n_ctrl);
400+
401+
if (deriv_order > p)
402+
return result;
403+
404+
// --- Uniform clamped knot vector ---
405+
Array knots(n_ctrl + p + 1);
406+
for (int i = 0; i <= p; ++i)
407+
knots[i] = 0.0;
408+
for (int i = p + 1; i < n_ctrl; ++i)
409+
knots[i] = real(i - p) / real(n_ctrl - p);
410+
for (int i = n_ctrl; i <= n_ctrl + p; ++i)
411+
knots[i] = 1.0;
412+
413+
const int span = uniformClampedSpan(u, n_ctrl, p);
414+
const int first = span - p;
415+
416+
// --- Algorithm A2.2 (ndu table) ---
417+
Matrix ndu(p + 1, p + 1);
418+
Array left(p + 1), right(p + 1);
419+
420+
ndu(0, 0) = 1.0;
421+
422+
for (int j = 1; j <= p; ++j) {
423+
left[j] = u - knots[span + 1 - j];
424+
right[j] = knots[span + j] - u;
425+
426+
real saved = 0.0;
427+
for (int r = 0; r < j; ++r) {
428+
ndu(j, r) = right[r + 1] + left[j - r];
429+
real temp = ndu(r, j - 1) / ndu(j, r);
430+
431+
ndu(r, j) = saved + right[r + 1] * temp;
432+
saved = left[j - r] * temp;
433+
}
434+
ndu(j, j) = saved;
435+
}
436+
437+
// --- Derivative computation (Algorithm A2.3) ---
438+
Matrix ders(deriv_order + 1, p + 1);
439+
for (int j = 0; j <= p; ++j)
440+
ders(0, j) = ndu(j, p);
441+
442+
// Working array a[2][p+1]
443+
Matrix a(2, p + 1);
444+
445+
for (int r = 0; r <= p; ++r) {
446+
int s1 = 0, s2 = 1;
447+
a(0, 0) = 1.0;
448+
449+
for (int k = 1; k <= deriv_order; ++k) {
450+
real d = 0.0;
451+
int rk = r - k;
452+
int pk = p - k;
453+
454+
int j1;
455+
int j2;
456+
457+
if (r >= k) {
458+
a(s2, 0) = a(s1, 0) / ndu(pk + 1, rk);
459+
d = a(s2, 0) * ndu(rk, pk);
460+
}
461+
if (rk >= -1) {
462+
j1 = 1;
463+
} else {
464+
j1 = -rk;
465+
}
466+
467+
if (r - 1 <= pk) {
468+
j2 = k - 1;
469+
} else {
470+
j2 = p - r;
471+
}
472+
473+
for (int j = j1; j <= j2; j++) {
474+
a(s2, j) = (a(s1, j) - a(s1, j - 1)) / ndu(pk + 1, rk + j);
475+
d += a(s2, j) * ndu(rk + j, pk);
476+
}
477+
478+
if (r <= pk) {
479+
a(s2, k) = -a(s1, k - 1) / ndu(pk + 1, r);
480+
d += a(s2, k) * ndu(r, pk);
481+
}
482+
483+
ders(k, r) = d;
484+
std::swap(s1, s2);
485+
}
486+
}
487+
488+
// --- Multiply by factorial terms ---
489+
real factor = real(p);
490+
for (int k = 1; k <= deriv_order; ++k) {
491+
for (int j = 0; j <= p; ++j)
492+
ders(k, j) *= factor;
493+
factor *= real(p - k);
494+
}
495+
496+
// --- Scatter into global result ---
497+
for (int j = 0; j <= p; ++j)
498+
result[first + j] = ders(deriv_order, j);
499+
500+
return result;
501+
}
502+
262503
} // namespace blast

examples/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ set(EXAMPLES
1111
get_bspline_and_basis_functions get_bspline_and_basis_functions.cpp
1212
get_start_and_end_pos get_start_and_end_pos.cpp
1313

14+
# Demo 3
15+
# demo3_demo2_tasks_gen3 Demo3/demo2_tasks_gen3.cpp
16+
# demo3_demo2_tasks_link6 Demo3/demo2_tasks_link6.cpp
17+
# demo3_kitchen_tasks_link6 Demo3/kitchen_tasks_link6.cpp
18+
1419
# example_demo1 demo1_link6.cpp
1520
# example_demo2 demo2.cpp
1621
# example_demo2_link6 demo2_link6.cpp

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ set(TESTS
7272
# World
7373
# test_primitives world/test_primitives.cpp
7474
# Trajectory
75-
# test_bspline trajectory/test_bsplines.cpp
75+
test_bspline trajectory/test_bsplines.cpp
7676
)
7777

7878
# Loop through and add benchmarks

0 commit comments

Comments
 (0)