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 }