View Javadoc
1   /*
2    * Copyright (C) 2016 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.ar.slam;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.algebra.WrongSizeException;
20  import com.irurueta.geometry.InhomogeneousPoint3D;
21  import com.irurueta.geometry.Point3D;
22  import com.irurueta.geometry.Quaternion;
23  
24  /**
25   * Utility class to predict device state (position, orientation, linear velocity
26   * and angular velocity) assuming a constant velocity model (acceleration is
27   * assumed zero under no external force).
28   */
29  @SuppressWarnings("DuplicatedCode")
30  public class ConstantVelocityModelStatePredictor {
31  
32      /**
33       * Number of components on angular speed.
34       */
35      public static final int ANGULAR_SPEED_COMPONENTS = 3;
36  
37      /**
38       * Number of components of speed.
39       */
40      public static final int SPEED_COMPONENTS = 3;
41  
42      /**
43       * Number of components of constant velocity model state.
44       */
45      public static final int STATE_COMPONENTS = 13;
46  
47      /**
48       * Number of components of constant velocity model control signal.
49       */
50      public static final int CONTROL_COMPONENTS = 6;
51  
52      /**
53       * Number of components of constant velocity model state with position
54       * adjustment.
55       */
56      public static final int STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS = 13;
57  
58      /**
59       * Number of components of constant velocity model with position adjustment
60       * control signal.
61       */
62      public static final int CONTROL_WITH_POSITION_ADJUSTMENT_COMPONENTS = 9;
63  
64      /**
65       * Number of components of constant velocity model state with rotation
66       * adjustment.
67       */
68      public static final int STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS = 13;
69  
70      /**
71       * Number of components of constant velocity model with rotation adjustment
72       * control signal.
73       */
74      public static final int CONTROL_WITH_ROTATION_ADJUSTMENT_COMPONENTS = 10;
75  
76      /**
77       * Number of components of constant velocity model state with position and
78       * rotation adjustment.
79       */
80      public static final int STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS = 13;
81  
82      /**
83       * Number of components of constant velocity model with position and
84       * rotation adjustment control signal.
85       */
86      public static final int CONTROL_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS = 13;
87  
88      /**
89       * Constructor.
90       */
91      private ConstantVelocityModelStatePredictor() {
92      }
93  
94      /**
95       * Updates the system model (position, orientation, linear velocity and
96       * angular velocity) assuming a constant velocity model (without
97       * acceleration) when no velocity control signal is present.
98       *
99       * @param x         initial system state containing: position-x, position-y,
100      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
101      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
102      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
103      *                  length 13.
104      * @param u         linear and angular velocity perturbations or controls:
105      *                  linear-velocity-change-x, linear-velocity-change-y,
106      *                  linear-velocity-change-z, angular-velocity-change-x,
107      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length 6.
108      * @param dt        time interval to compute prediction expressed in seconds.
109      * @param result    instance where updated system model will be stored. Must
110      *                  have length 13.
111      * @param jacobianX jacobian wrt system state. Must be 13x13.
112      * @param jacobianU jacobian wrt control. Must be 13x6.
113      * @throws IllegalArgumentException if system state array, control array,
114      *                                  result or jacobians do not have proper size.
115      * @see <a href="https://github.com/joansola/slamtb">constVel.m at https://github.com/joansola/slamtb</a>
116      */
117     public static void predict(
118             final double[] x, final double[] u, final double dt, final double[] result, final Matrix jacobianX,
119             final Matrix jacobianU) {
120         if (x.length != STATE_COMPONENTS) {
121             // x must have length 13
122             throw new IllegalArgumentException();
123         }
124         if (u.length != CONTROL_COMPONENTS) {
125             // u must have length 6
126             throw new IllegalArgumentException();
127         }
128         if (result.length != STATE_COMPONENTS) {
129             // result must have length 13
130             throw new IllegalArgumentException();
131         }
132         if (jacobianX != null && (jacobianX.getRows() != STATE_COMPONENTS
133                 || jacobianX.getColumns() != STATE_COMPONENTS)) {
134             // jacobian wrt x must be 13x13
135             throw new IllegalArgumentException();
136         }
137         if (jacobianU != null && (jacobianU.getRows() != STATE_COMPONENTS
138                 || jacobianU.getColumns() != CONTROL_COMPONENTS)) {
139             // jacobian wrt u must be 13x6
140             throw new IllegalArgumentException();
141         }
142 
143         // position
144         final var r = new InhomogeneousPoint3D(x[0], x[1], x[2]);
145 
146         // orientation
147         var q = new Quaternion(x[3], x[4], x[5], x[6]);
148 
149         // linear velocity
150         var vx = x[7];
151         var vy = x[8];
152         var vz = x[9];
153 
154         // angular velocity
155         var wx = x[10];
156         var wy = x[11];
157         var wz = x[12];
158 
159         // linear velocity change (control)
160         final var uvx = u[0];
161         final var uvy = u[1];
162         final var uvz = u[2];
163 
164         // angular velocity change (control)
165         final var uwx = u[3];
166         final var uwy = u[4];
167         final var uwz = u[5];
168 
169         try {
170             // update position
171             Matrix rr = null;
172             Matrix rv = null;
173             if (jacobianX != null) {
174                 rr = new Matrix(Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
175                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH);
176                 rv = new Matrix(Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH, SPEED_COMPONENTS);
177             }
178             PositionPredictor.predict(r, vx, vy, vz, dt, r, rr, rv, null);
179 
180             // update orientation
181             Matrix qq = null;
182             Matrix qw = null;
183             if (jacobianX != null) {
184                 qq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
185                 qw = new Matrix(Quaternion.N_PARAMS, ANGULAR_SPEED_COMPONENTS);
186             }
187             q = QuaternionPredictor.predict(q, wx, wy, wz, dt, true, qq, qw);
188 
189             // apply control signals
190             vx += uvx;
191             vy += uvy;
192             vz += uvz;
193 
194             wx += uwx;
195             wy += uwy;
196             wz += uwz;
197 
198             // set new state
199             result[0] = r.getInhomX();
200             result[1] = r.getInhomY();
201             result[2] = r.getInhomZ();
202 
203             result[3] = q.getA();
204             result[4] = q.getB();
205             result[5] = q.getC();
206             result[6] = q.getD();
207 
208             result[7] = vx;
209             result[8] = vy;
210             result[9] = vz;
211 
212             result[10] = wx;
213             result[11] = wy;
214             result[12] = wz;
215 
216             // jacobians
217             if (jacobianX != null) {
218                 // [Rr   0   Rv  0  ]
219                 // [0    Qq  0   Qw ]
220                 // [0    0   eye 0  ]
221                 // [0    0   0   eye]
222                 jacobianX.initialize(0.0);
223                 jacobianX.setSubmatrix(0, 0, 2, 2, rr);
224 
225                 jacobianX.setSubmatrix(3, 3, 6, 6, qq);
226 
227                 jacobianX.setSubmatrix(0, 7, 2, 9, rv);
228 
229                 for (int i = 7; i < STATE_COMPONENTS; i++) {
230                     jacobianX.setElementAt(i, i, 1.0);
231                 }
232 
233                 jacobianX.setSubmatrix(3, 10, 6, 12, qw);
234             }
235 
236             if (jacobianU != null) {
237                 jacobianU.initialize(0.0);
238 
239                 for (int i = 7, j = 0; i < STATE_COMPONENTS; i++, j++) {
240                     jacobianU.setElementAt(i, j, 1.0);
241                 }
242             }
243         } catch (final WrongSizeException ignore) {
244             // never thrown
245         }
246     }
247 
248     /**
249      * Updates the system model (position, orientation, linear velocity and
250      * angular velocity) assuming a constant velocity model (without
251      * acceleration).
252      *
253      * @param x      initial system state containing: position-x, position-y,
254      *               position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
255      *               linear-velocity-x, linear-velocity-y, linear-velocity-z,
256      *               angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
257      *               length 13.
258      * @param u      linear and angular velocity perturbations or controls:
259      *               linear-velocity-change-x, linear-velocity-change-y,
260      *               linear-velocity-change-z, angular-velocity-change-x,
261      *               angular-velocity-change-y, angular-velocity-change-z. Must have length 6.
262      * @param dt     time interval to compute prediction expressed in seconds.
263      * @param result instance where updated system model will be stored. Must
264      *               have length 13.
265      * @throws IllegalArgumentException if system state array or control array
266      *                                  or result do not have proper size.
267      * @see <a href="https://github.com/joansola/slamtb">constVel.m at https://github.com/joansola/slamtb</a>
268      */
269     public static void predict(final double[] x, final double[] u, final double dt, final double[] result) {
270         predict(x, u, dt, result, null, null);
271     }
272 
273     /**
274      * Updates the system model (position, orientation, linear velocity and
275      * angular velocity) assuming a constant velocity model (without
276      * acceleration).
277      *
278      * @param x         initial system state containing: position-x, position-y,
279      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
280      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
281      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
282      *                  length 13.
283      * @param u         linear and angular velocity perturbations or controls:
284      *                  linear-velocity-change-x, linear-velocity-change-y,
285      *                  linear-velocity-change-z, angular-velocity-change-x,
286      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length 6.
287      * @param dt        time interval to compute prediction expressed in seconds.
288      * @param jacobianX jacobian wrt system state. Must be 13x13.
289      * @param jacobianU jacobian wrt control. Must be 13x6.
290      * @return instance where updated system model will be stored.
291      * @throws IllegalArgumentException if system state array, control array or
292      *                                  jacobians do not have proper size.
293      * @see <a href="https://github.com/joansola/slamtb">constVel.m at https://github.com/joansola/slamtb</a>
294      */
295     public static double[] predict(final double[] x, final double[] u, final double dt, final Matrix jacobianX,
296                                    final Matrix jacobianU) {
297         final var result = new double[STATE_COMPONENTS];
298         predict(x, u, dt, result, jacobianX, jacobianU);
299         return result;
300     }
301 
302     /**
303      * Updates the system model (position, orientation, linear velocity and
304      * angular velocity) assuming a constant velocity model (without
305      * acceleration).
306      *
307      * @param x  initial system state containing: position-x, position-y,
308      *           position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
309      *           linear-velocity-x, linear-velocity-y, linear-velocity-z,
310      *           angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
311      *           length 13.
312      * @param u  linear and angular velocity perturbations or controls:
313      *           linear-velocity-change-x, linear-velocity-change-y,
314      *           linear-velocity-change-z, angular-velocity-change-x,
315      *           angular-velocity-change-y, angular-velocity-change-z. Must have length 6.
316      * @param dt time interval to compute prediction expressed in seconds.
317      * @return a new instance containing the updated system state.
318      * @throws IllegalArgumentException if system state array, control array or
319      *                                  jacobians do not have proper size.
320      * @see <a href="https://github.com/joansola/slamtb">constVel.m at https://github.com/joansola/slamtb</a>
321      */
322     public static double[] predict(final double[] x, final double[] u, final double dt) {
323         final var result = new double[STATE_COMPONENTS];
324         predict(x, u, dt, result);
325         return result;
326     }
327 
328     /**
329      * Updates the system model (position, orientation, linear velocity and
330      * angular velocity) assuming a constant velocity model (without
331      * acceleration) when no velocity control signal is present.
332      *
333      * @param x         initial system state containing: position-x, position-y,
334      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
335      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
336      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
337      *                  length 13.
338      * @param u         linear ang angular velocity perturbations or controls and
339      *                  position perturbations or controls: position-change-x, position-change-y,
340      *                  position-change-z, linear-velocity-change-x, linear-velocity-change-y,
341      *                  linear-velocity-change-z, angular-velocity-change-x,
342      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length 9.
343      * @param dt        time interval to compute prediction expressed in seconds.
344      * @param result    instance where updated system model will be stored. Must
345      *                  have length 13.
346      * @param jacobianX jacobian wrt system state. Must be 13x13.
347      * @param jacobianU jacobian wrt control. Must be 13x9.
348      * @throws IllegalArgumentException if system state array, control array,
349      *                                  result or jacobians do not have proper size.
350      */
351     public static void predictWithPositionAdjustment(
352             final double[] x, final double[] u, final double dt, final double[] result, final Matrix jacobianX,
353             final Matrix jacobianU) {
354         if (x.length != STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS) {
355             // x must have length 13
356             throw new IllegalArgumentException();
357         }
358         if (u.length != CONTROL_WITH_POSITION_ADJUSTMENT_COMPONENTS) {
359             // u must have length 9
360             throw new IllegalArgumentException();
361         }
362         if (result.length != STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS) {
363             // result must have length 13
364             throw new IllegalArgumentException();
365         }
366         if (jacobianX != null && (jacobianX.getRows() != STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS ||
367                 jacobianX.getColumns() != STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS)) {
368             // jacobian wrt x must be 13x13
369             throw new IllegalArgumentException();
370         }
371         if (jacobianU != null && (jacobianU.getRows() != STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS ||
372                 jacobianU.getColumns() != CONTROL_WITH_POSITION_ADJUSTMENT_COMPONENTS)) {
373             // jacobian wrt u must be 13x9
374             throw new IllegalArgumentException();
375         }
376 
377         // position
378         final var r = new InhomogeneousPoint3D(x[0], x[1], x[2]);
379 
380         // orientation
381         var q = new Quaternion(x[3], x[4], x[5], x[6]);
382 
383         // linear velocity
384         var vx = x[7];
385         var vy = x[8];
386         var vz = x[9];
387 
388         // angular velocity
389         var wx = x[10];
390         var wy = x[11];
391         var wz = x[12];
392 
393         // position change (control)
394         final var drx = u[0];
395         final var dry = u[1];
396         final var drz = u[2];
397 
398         // linear velocity change (control)
399         final var uvx = u[3];
400         final var uvy = u[4];
401         final var uvz = u[5];
402 
403         // angular velocity change (control)
404         final var uwx = u[6];
405         final var uwy = u[7];
406         final var uwz = u[8];
407 
408         try {
409             // update position
410             Matrix rr = null;
411             Matrix rv = null;
412             if (jacobianX != null) {
413                 rr = new Matrix(
414                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
415                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH);
416                 rv = new Matrix(
417                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
418                         SPEED_COMPONENTS);
419             }
420             PositionPredictor.predictWithPositionAdjustment(r, drx, dry, drz, vx, vy, vz, 0.0, 0.0, 0.0,
421                     dt, r, rr, null, rv, null);
422 
423             // update orientation
424             Matrix qq = null;
425             Matrix qw = null;
426             if (jacobianX != null) {
427                 qq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
428                 qw = new Matrix(Quaternion.N_PARAMS, ANGULAR_SPEED_COMPONENTS);
429             }
430             q = QuaternionPredictor.predict(q, wx, wy, wz, dt, true, qq, qw);
431 
432             // apply control signals
433             vx += uvx;
434             vy += uvy;
435             vz += uvz;
436 
437             wx += uwx;
438             wy += uwy;
439             wz += uwz;
440 
441             // set new state
442             result[0] = r.getInhomX();
443             result[1] = r.getInhomY();
444             result[2] = r.getInhomZ();
445 
446             result[3] = q.getA();
447             result[4] = q.getB();
448             result[5] = q.getC();
449             result[6] = q.getD();
450 
451             result[7] = vx;
452             result[8] = vy;
453             result[9] = vz;
454 
455             result[10] = wx;
456             result[11] = wy;
457             result[12] = wz;
458 
459             // jacobians
460             if (jacobianX != null) {
461                 // [Rr   0   Rv  0  ]
462                 // [0    Qq  0   Qw ]
463                 // [0    0   eye 0  ]
464                 // [0    0   0   eye]
465                 jacobianX.initialize(0.0);
466                 jacobianX.setSubmatrix(0, 0, 2, 2, rr);
467 
468                 jacobianX.setSubmatrix(3, 3, 6, 6, qq);
469 
470                 jacobianX.setSubmatrix(0, 7, 2, 9, rv);
471 
472                 for (int i = 7; i < STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS; i++) {
473                     jacobianX.setElementAt(i, i, 1.0);
474                 }
475 
476                 jacobianX.setSubmatrix(3, 10, 6, 12, qw);
477             }
478 
479             if (jacobianU != null) {
480                 jacobianU.initialize(0.0);
481                 // variation of position
482                 for (var i = 0; i < Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH; i++) {
483                     jacobianU.setElementAt(i, i, 1.0);
484                 }
485                 // variation of linear and angular speed
486                 for (int i = 7, j = Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH;
487                      i < STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS; i++, j++) {
488                     jacobianU.setElementAt(i, j, 1.0);
489                 }
490             }
491         } catch (final WrongSizeException ignore) {
492             // never thrown
493         }
494     }
495 
496     /**
497      * Updates the system model (position, orientation, linear velocity and
498      * angular velocity) assuming a constant velocity model (without
499      * acceleration) when no velocity control signal is present.
500      *
501      * @param x      initial system state containing: position-x, position-y,
502      *               position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
503      *               linear-velocity-x, linear-velocity-y, linear-velocity-z,
504      *               angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
505      *               length 13.
506      * @param u      linear ang angular velocity perturbations or controls and
507      *               position perturbations or controls: position-change-x, position-change-y,
508      *               position-change-z, linear-velocity-change-x, linear-velocity-change-y,
509      *               linear-velocity-change-z, angular-velocity-change-x,
510      *               angular-velocity-change-y, angular-velocity-change-z. Must have length 9.
511      * @param dt     time interval to compute prediction expressed in seconds.
512      * @param result instance where updated system model will be stored. Must
513      *               have length 13.
514      * @throws IllegalArgumentException if system state array, control array
515      *                                  or result array do not have proper size.
516      */
517     public static void predictWithPositionAdjustment(
518             final double[] x, final double[] u, final double dt, final double[] result) {
519         predictWithPositionAdjustment(x, u, dt, result, null, null);
520     }
521 
522     /**
523      * Updates the system model (position, orientation, linear velocity and
524      * angular velocity) assuming a constant velocity model (without
525      * acceleration) when no velocity control signal is present.
526      *
527      * @param x         initial system state containing: position-x, position-y,
528      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
529      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
530      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
531      *                  length 13.
532      * @param u         linear ang angular velocity perturbations or controls and
533      *                  position perturbations or controls: position-change-x, position-change-y,
534      *                  position-change-z, linear-velocity-change-x, linear-velocity-change-y,
535      *                  linear-velocity-change-z, angular-velocity-change-x,
536      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length 9.
537      * @param dt        time interval to compute prediction expressed in seconds.
538      * @param jacobianX jacobian wrt system state. Must be 13x13.
539      * @param jacobianU jacobian wrt control. Must be 13x9.
540      * @return a new instance containing updated system model.
541      * @throws IllegalArgumentException if system state array, control array
542      *                                  or jacobians do not have proper size.
543      */
544     public static double[] predictWithPositionAdjustment(
545             final double[] x, final double[] u, final double dt, final Matrix jacobianX, final Matrix jacobianU) {
546         final var result = new double[STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS];
547         predictWithPositionAdjustment(x, u, dt, result, jacobianX, jacobianU);
548         return result;
549     }
550 
551     /**
552      * Updates the system model (position, orientation, linear velocity and
553      * angular velocity) assuming a constant velocity model (without
554      * acceleration) when no velocity control signal is present.
555      *
556      * @param x  initial system state containing: position-x, position-y,
557      *           position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
558      *           linear-velocity-x, linear-velocity-y, linear-velocity-z,
559      *           angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
560      *           length 13.
561      * @param u  linear ang angular velocity perturbations or controls and
562      *           position perturbations or controls: position-change-x, position-change-y,
563      *           position-change-z, linear-velocity-change-x, linear-velocity-change-y,
564      *           linear-velocity-change-z, angular-velocity-change-x,
565      *           angular-velocity-change-y, angular-velocity-change-z. Must have length 9.
566      * @param dt time interval to compute prediction expressed in seconds.
567      * @return a new instance containing updated system model.
568      * @throws IllegalArgumentException if system state or control array do not
569      *                                  have proper size.
570      */
571     public static double[] predictWithPositionAdjustment(final double[] x, final double[] u, final double dt) {
572         final var result = new double[STATE_WITH_POSITION_ADJUSTMENT_COMPONENTS];
573         predictWithPositionAdjustment(x, u, dt, result);
574         return result;
575     }
576 
577     /**
578      * Updates the system model (position, orientation, linear velocity and
579      * angular velocity) assuming a constant velocity model (without
580      * acceleration) when no velocity control signal is present.
581      *
582      * @param x         initial system state containing: position-x, position-y,
583      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
584      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
585      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
586      *                  length 13.
587      * @param u         linear and angular velocity perturbations or controls, and
588      *                  rotation perturbations or controls: quaternion-change-a,
589      *                  quaternion-change-b, quaternion-change-c, quaternion-change-d,
590      *                  linear-velocity-change-x, linear-velocity-change-y,
591      *                  linear-velocity-change-z, angular-velocity-change-x,
592      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length
593      *                  10.
594      * @param dt        time interval to compute prediction expressed in seconds.
595      * @param result    instance where updated system model will be stored. Must
596      *                  have length 13.
597      * @param jacobianX jacobian wrt system state. Must be 13x13.
598      * @param jacobianU jacobian wrt control. Must be 13x10.
599      * @throws IllegalArgumentException if system state array, control array,
600      *                                  result or jacobians do not have proper size.
601      */
602     public static void predictWithRotationAdjustment(
603             final double[] x, final double[] u, final double dt, final double[] result, final Matrix jacobianX,
604             final Matrix jacobianU) {
605         if (x.length != STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS) {
606             throw new IllegalArgumentException("x must have length 13");
607         }
608         if (u.length != CONTROL_WITH_ROTATION_ADJUSTMENT_COMPONENTS) {
609             throw new IllegalArgumentException("u must have length 10");
610         }
611         if (result.length != STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS) {
612             throw new IllegalArgumentException("result must have length 13");
613         }
614         if (jacobianX != null && (jacobianX.getRows() != STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS ||
615                 jacobianX.getColumns() != STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS)) {
616             throw new IllegalArgumentException("jacobian wrt x must be 13x13");
617         }
618         if (jacobianU != null && (jacobianU.getRows() != STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS ||
619                 jacobianU.getColumns() != CONTROL_WITH_ROTATION_ADJUSTMENT_COMPONENTS)) {
620             throw new IllegalArgumentException("jacobian wrt u must be 13x10");
621         }
622 
623         // position
624         final var r = new InhomogeneousPoint3D(x[0], x[1], x[2]);
625 
626         // orientation
627         var q = new Quaternion(x[3], x[4], x[5], x[6]);
628 
629         // linear velocity
630         var vx = x[7];
631         var vy = x[8];
632         var vz = x[9];
633 
634         // linear acceleration
635 
636         // angular velocity
637         var wx = x[10];
638         var wy = x[11];
639         var wz = x[12];
640 
641         // rotation change (control)
642         final var dq = new Quaternion(u[0], u[1], u[2], u[3]);
643 
644         // linear velocity change (control)
645         final var uvx = u[4];
646         final var uvy = u[5];
647         final var uvz = u[6];
648 
649         // angular velocity change (control)
650         final var uwx = u[7];
651         final var uwy = u[8];
652         final var uwz = u[9];
653 
654         try {
655             // update position
656             Matrix rr = null;
657             Matrix rv = null;
658             if (jacobianX != null) {
659                 rr = new Matrix(
660                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
661                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH);
662                 rv = new Matrix(
663                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
664                         SPEED_COMPONENTS);
665             }
666             PositionPredictor.predict(r, vx, vy, vz, dt, r, rr, rv, null);
667 
668             // update orientation
669             Matrix qq = null;
670             Matrix qdq = null;
671             Matrix qw = null;
672             if (jacobianX != null) {
673                 qq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
674                 qw = new Matrix(Quaternion.N_PARAMS, ANGULAR_SPEED_COMPONENTS);
675             }
676             if (jacobianU != null) {
677                 qdq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
678             }
679             q = QuaternionPredictor.predictWithRotationAdjustment(q, dq, wx, wy, wz, dt, qq, qdq, qw);
680 
681             // apply control signals
682             vx += uvx;
683             vy += uvy;
684             vz += uvz;
685 
686             wx += uwx;
687             wy += uwy;
688             wz += uwz;
689 
690             // set new state
691             result[0] = r.getInhomX();
692             result[1] = r.getInhomY();
693             result[2] = r.getInhomZ();
694 
695             result[3] = q.getA();
696             result[4] = q.getB();
697             result[5] = q.getC();
698             result[6] = q.getD();
699 
700             result[7] = vx;
701             result[8] = vy;
702             result[9] = vz;
703 
704             result[10] = wx;
705             result[11] = wy;
706             result[12] = wz;
707 
708             // jacobians
709             if (jacobianX != null) {
710                 // [Rr   0   Rv  0  ]
711                 // [0    Qq  0   Qw ]
712                 // [0    0   eye 0  ]
713                 // [0    0   0   eye]
714                 jacobianX.initialize(0.0);
715                 jacobianX.setSubmatrix(0, 0, 2, 2, rr);
716 
717                 jacobianX.setSubmatrix(3, 3, 6, 6, qq);
718 
719                 jacobianX.setSubmatrix(0, 7, 2, 9, rv);
720 
721                 for (var i = 7; i < STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS; i++) {
722                     jacobianX.setElementAt(i, i, 1.0);
723                 }
724 
725                 jacobianX.setSubmatrix(3, 10, 6, 12, qw);
726             }
727 
728             if (jacobianU != null) {
729                 jacobianU.initialize(0.0);
730 
731                 // variation of rotation
732                 jacobianU.setSubmatrix(3, 0, 6, 3, qdq);
733 
734                 // variation of linear and angular speed
735                 for (int i = 7, j = Quaternion.N_PARAMS;
736                      i < STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS; i++, j++) {
737                     jacobianU.setElementAt(i, j, 1.0);
738                 }
739             }
740 
741         } catch (final WrongSizeException ignore) {
742             // never thrown
743         }
744     }
745 
746     /**
747      * Updates the system model (position, orientation, linear velocity and
748      * angular velocity) assuming a constant velocity model (without
749      * acceleration) when no velocity control signal is present.
750      *
751      * @param x      initial system state containing: position-x, position-y,
752      *               position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
753      *               linear-velocity-x, linear-velocity-y, linear-velocity-z,
754      *               angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
755      *               length 13.
756      * @param u      linear and angular velocity perturbations or controls, and
757      *               rotation perturbations or controls: quaternion-change-a,
758      *               quaternion-change-b, quaternion-change-c, quaternion-change-d,
759      *               linear-velocity-change-x, linear-velocity-change-y,
760      *               linear-velocity-change-z, angular-velocity-change-x,
761      *               angular-velocity-change-y, angular-velocity-change-z. Must have length
762      *               10.
763      * @param dt     time interval to compute prediction expressed in seconds.
764      * @param result instance where updated system model will be stored. Must
765      *               have length 13.
766      * @throws IllegalArgumentException if system state array, control array or
767      *                                  result do not have proper length.
768      */
769     public static void predictWithRotationAdjustment(
770             final double[] x, final double[] u, final double dt, final double[] result) {
771         predictWithRotationAdjustment(x, u, dt, result, null, null);
772     }
773 
774     /**
775      * Updates the system model (position, orientation, linear velocity and
776      * angular velocity) assuming a constant velocity model (without
777      * acceleration) when no velocity control signal is present.
778      *
779      * @param x         initial system state containing: position-x, position-y,
780      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
781      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
782      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
783      *                  length 13.
784      * @param u         linear and angular velocity perturbations or controls, and
785      *                  rotation perturbations or controls: quaternion-change-a,
786      *                  quaternion-change-b, quaternion-change-c, quaternion-change-d,
787      *                  linear-velocity-change-x, linear-velocity-change-y,
788      *                  linear-velocity-change-z, angular-velocity-change-x,
789      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length
790      *                  10.
791      * @param dt        time interval to compute prediction expressed in seconds.
792      * @param jacobianX jacobian wrt system state. Must be 13x13.
793      * @param jacobianU jacobian wrt control. Must be 13x10.
794      * @return a new array containing updated system model.
795      * @throws IllegalArgumentException if system state array, control array
796      *                                  or jacobians do not have proper size.
797      */
798     public static double[] predictWithRotationAdjustment(
799             final double[] x, final double[] u, final double dt, final Matrix jacobianX, final Matrix jacobianU) {
800         final var result = new double[STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS];
801         predictWithRotationAdjustment(x, u, dt, result, jacobianX, jacobianU);
802         return result;
803     }
804 
805     /**
806      * Updates the system model (position, orientation, linear velocity and
807      * angular velocity) assuming a constant velocity model (without
808      * acceleration) when no velocity control signal is present.
809      *
810      * @param x  initial system state containing: position-x, position-y,
811      *           position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
812      *           linear-velocity-x, linear-velocity-y, linear-velocity-z,
813      *           angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
814      *           length 13.
815      * @param u  linear and angular velocity perturbations or controls, and
816      *           rotation perturbations or controls: quaternion-change-a,
817      *           quaternion-change-b, quaternion-change-c, quaternion-change-d,
818      *           linear-velocity-change-x, linear-velocity-change-y,
819      *           linear-velocity-change-z, angular-velocity-change-x,
820      *           angular-velocity-change-y, angular-velocity-change-z. Must have length
821      *           10.
822      * @param dt time interval to compute prediction expressed in seconds.
823      * @return a new array containing updated system model.
824      * @throws IllegalArgumentException if system state array or control array
825      *                                  do not have proper size.
826      */
827     public static double[] predictWithRotationAdjustment(final double[] x, final double[] u, final double dt) {
828         final var result = new double[STATE_WITH_ROTATION_ADJUSTMENT_COMPONENTS];
829         predictWithRotationAdjustment(x, u, dt, result);
830         return result;
831     }
832 
833     /**
834      * Updates the system model (position, orientation, linear velocity and
835      * angular velocity) assuming a constant velocity model (without
836      * acceleration) when no velocity control signal is present.
837      *
838      * @param x         initial system state containing: position-x, position-y,
839      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
840      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
841      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
842      *                  length 13.
843      * @param u         linear and angular velocity perturbations or controls, position
844      *                  perturbations or controls and rotation perturbation or control:
845      *                  position-change-x, position-change-y, position-change-z,
846      *                  quaternion-change-a, quaternion-change-b, quaternion-change-c,
847      *                  quaternion-change-d, linear-velocity-change-x, linear-velocity-change-y,
848      *                  linear-velocity-change-z, angular-velocity-change-x,
849      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length
850      *                  12.
851      * @param dt        time interval to compute prediction expressed in seconds.
852      * @param result    instance where updated system model will be stored. Must
853      *                  have length 13.
854      * @param jacobianX jacobian wrt system state. Must be 13x13.
855      * @param jacobianU jacobian wrt control. Must be 13x13.
856      * @throws IllegalArgumentException if system state array, control array,
857      *                                  result or jacobians do not have proper size.
858      */
859     public static void predictWithPositionAndRotationAdjustment(
860             final double[] x, final double[] u, final double dt, final double[] result, final Matrix jacobianX,
861             final Matrix jacobianU) {
862         if (x.length != STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS) {
863             throw new IllegalArgumentException("x must have length 13");
864         }
865         if (u.length != CONTROL_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS) {
866             throw new IllegalArgumentException("u must have length 13");
867         }
868         if (result.length != STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS) {
869             throw new IllegalArgumentException("result must have length 13");
870         }
871         if (jacobianX != null && (jacobianX.getRows() != STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS
872                 || jacobianX.getColumns() != STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS)) {
873             throw new IllegalArgumentException("jacobian wrt x must be 13x13");
874         }
875         if (jacobianU != null && (jacobianU.getRows() != STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS
876                 || jacobianU.getColumns() != CONTROL_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS)) {
877             throw new IllegalArgumentException("jacobian wrt u must be 13x13");
878         }
879 
880         // position
881         final var r = new InhomogeneousPoint3D(x[0], x[1], x[2]);
882 
883         // orientation
884         var q = new Quaternion(x[3], x[4], x[5], x[6]);
885 
886         // linear velocity
887         var vx = x[7];
888         var vy = x[8];
889         var vz = x[9];
890 
891         // angular velocity
892         var wx = x[10];
893         var wy = x[11];
894         var wz = x[12];
895 
896         // position change (control)
897         final var drx = u[0];
898         final var dry = u[1];
899         final var drz = u[2];
900 
901         // rotation change (control)
902         final var dq = new Quaternion(u[3], u[4], u[5], u[6]);
903 
904         // linear velocity change (control)
905         final var uvx = u[7];
906         final var uvy = u[8];
907         final var uvz = u[9];
908 
909         // angular velocity change (control)
910         final var uwx = u[10];
911         final var uwy = u[11];
912         final var uwz = u[12];
913 
914         try {
915             // update position
916             Matrix rr = null;
917             Matrix rv = null;
918             if (jacobianX != null) {
919                 rr = new Matrix(Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH,
920                         Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH);
921                 rv = new Matrix(Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH, SPEED_COMPONENTS);
922             }
923             PositionPredictor.predictWithPositionAdjustment(r, drx, dry, drz, vx, vy, vz, 0.0, 0.0, 0.0,
924                     dt, r, rr, null, rv, null);
925 
926             // update orientation
927             Matrix qq = null;
928             Matrix qdq = null;
929             Matrix qw = null;
930             if (jacobianX != null) {
931                 qq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
932                 qw = new Matrix(Quaternion.N_PARAMS, ANGULAR_SPEED_COMPONENTS);
933             }
934             if (jacobianU != null) {
935                 qdq = new Matrix(Quaternion.N_PARAMS, Quaternion.N_PARAMS);
936             }
937             q = QuaternionPredictor.predictWithRotationAdjustment(q, dq, wx, wy, wz, dt, qq, qdq, qw);
938 
939             // apply control signals
940             vx += uvx;
941             vy += uvy;
942             vz += uvz;
943 
944             wx += uwx;
945             wy += uwy;
946             wz += uwz;
947 
948             // set new state
949             result[0] = r.getInhomX();
950             result[1] = r.getInhomY();
951             result[2] = r.getInhomZ();
952 
953             result[3] = q.getA();
954             result[4] = q.getB();
955             result[5] = q.getC();
956             result[6] = q.getD();
957 
958             result[7] = vx;
959             result[8] = vy;
960             result[9] = vz;
961 
962             result[10] = wx;
963             result[11] = wy;
964             result[12] = wz;
965 
966             // jacobians
967             if (jacobianX != null) {
968                 // [Rr   0   Rv  0  ]
969                 // [0    Qq  0   Qw ]
970                 // [0    0   eye 0  ]
971                 // [0    0   0   eye]
972                 jacobianX.initialize(0.0);
973                 jacobianX.setSubmatrix(0, 0, 2, 2, rr);
974 
975                 jacobianX.setSubmatrix(3, 3, 6, 6, qq);
976 
977                 jacobianX.setSubmatrix(0, 7, 2, 9, rv);
978 
979                 for (int i = 7; i < STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS; i++) {
980                     jacobianX.setElementAt(i, i, 1.0);
981                 }
982 
983                 jacobianX.setSubmatrix(3, 10, 6, 12, qw);
984             }
985 
986             if (jacobianU != null) {
987                 jacobianU.initialize(0.0);
988                 // variation of position
989                 for (var i = 0; i < Point3D.POINT3D_INHOMOGENEOUS_COORDINATES_LENGTH; i++) {
990                     jacobianU.setElementAt(i, i, 1.0);
991                 }
992 
993                 // variation of rotation
994                 jacobianU.setSubmatrix(3, 3, 6, 6, qdq);
995 
996                 // variation of linear and angular speed
997                 for (var i = 7; i < STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS; i++) {
998                     jacobianU.setElementAt(i, i, 1.0);
999                 }
1000             }
1001 
1002         } catch (final WrongSizeException ignore) {
1003             // never thrown
1004         }
1005     }
1006 
1007     /**
1008      * Updates the system model (position, orientation, linear velocity and
1009      * angular velocity) assuming a constant velocity model (without
1010      * acceleration) when no velocity control signal is present.
1011      *
1012      * @param x      initial system state containing: position-x, position-y,
1013      *               position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
1014      *               linear-velocity-x, linear-velocity-y, linear-velocity-z,
1015      *               angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
1016      *               length 13.
1017      * @param u      linear and angular velocity perturbations or controls, position
1018      *               perturbations or controls and rotation perturbation or control:
1019      *               position-change-x, position-change-y, position-change-z,
1020      *               quaternion-change-a, quaternion-change-b, quaternion-change-c,
1021      *               quaternion-change-d, linear-velocity-change-x, linear-velocity-change-y,
1022      *               linear-velocity-change-z, angular-velocity-change-x,
1023      *               angular-velocity-change-y, angular-velocity-change-z. Must have length
1024      *               12.
1025      * @param dt     time interval to compute prediction expressed in seconds.
1026      * @param result instance where updated system model will be stored. Must
1027      *               have length 13.
1028      * @throws IllegalArgumentException if system state array, control array,
1029      *                                  result or jacobians do not have proper size.
1030      */
1031     public static void predictWithPositionAndRotationAdjustment(
1032             final double[] x, final double[] u, final double dt, final double[] result) {
1033         predictWithPositionAndRotationAdjustment(x, u, dt, result, null, null);
1034     }
1035 
1036     /**
1037      * Updates the system model (position, orientation, linear velocity and
1038      * angular velocity) assuming a constant velocity model (without
1039      * acceleration) when no velocity control signal is present.
1040      *
1041      * @param x         initial system state containing: position-x, position-y,
1042      *                  position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
1043      *                  linear-velocity-x, linear-velocity-y, linear-velocity-z,
1044      *                  angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
1045      *                  length 13.
1046      * @param u         linear and angular velocity perturbations or controls, position
1047      *                  perturbations or controls and rotation perturbation or control:
1048      *                  position-change-x, position-change-y, position-change-z,
1049      *                  quaternion-change-a, quaternion-change-b, quaternion-change-c,
1050      *                  quaternion-change-d, linear-velocity-change-x, linear-velocity-change-y,
1051      *                  linear-velocity-change-z, angular-velocity-change-x,
1052      *                  angular-velocity-change-y, angular-velocity-change-z. Must have length
1053      *                  12.
1054      * @param dt        time interval to compute prediction expressed in seconds.
1055      * @param jacobianX jacobian wrt system state. Must be 13x13.
1056      * @param jacobianU jacobian wrt control. Must be 13x13.
1057      * @return a new array containing updated system model.
1058      * @throws IllegalArgumentException if system state array, control array
1059      *                                  or jacobians do not have proper size.
1060      */
1061     public static double[] predictWithPositionAndRotationAdjustment(
1062             final double[] x, final double[] u, final double dt, final Matrix jacobianX, final Matrix jacobianU) {
1063         final var result = new double[STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS];
1064         predictWithPositionAndRotationAdjustment(x, u, dt, result, jacobianX, jacobianU);
1065         return result;
1066     }
1067 
1068     /**
1069      * Updates the system model (position, orientation, linear velocity and
1070      * angular velocity) assuming a constant velocity model (without
1071      * acceleration) when no velocity control signal is present.
1072      *
1073      * @param x  initial system state containing: position-x, position-y,
1074      *           position-z, quaternion-a, quaternion-b, quaternion-c, quaternion-d,
1075      *           linear-velocity-x, linear-velocity-y, linear-velocity-z,
1076      *           angular-velocity-x, angular-velocity-y, angular-velocity-z. Must have
1077      *           length 13.
1078      * @param u  linear and angular velocity perturbations or controls, position
1079      *           perturbations or controls and rotation perturbation or control:
1080      *           position-change-x, position-change-y, position-change-z,
1081      *           quaternion-change-a, quaternion-change-b, quaternion-change-c,
1082      *           quaternion-change-d, linear-velocity-change-x, linear-velocity-change-y,
1083      *           linear-velocity-change-z, angular-velocity-change-x,
1084      *           angular-velocity-change-y, angular-velocity-change-z. Must have length
1085      *           12.
1086      * @param dt time interval to compute prediction expressed in seconds.
1087      * @return a new array containing updated system model. Must have length 13.
1088      * @throws IllegalArgumentException if system state array, control array or
1089      *                                  result do not have proper size.
1090      */
1091     public static double[] predictWithPositionAndRotationAdjustment(
1092             final double[] x, final double[] u, final double dt) {
1093         final var result = new double[STATE_WITH_POSITION_AND_ROTATION_ADJUSTMENT_COMPONENTS];
1094         predictWithPositionAndRotationAdjustment(x, u, dt, result);
1095         return result;
1096     }
1097 }