BowlerKernel
RotationNRLegacy.java
Go to the documentation of this file.
1 package com.neuronrobotics.sdk.addons.kinematics.math;
2 
3 import com.neuronrobotics.sdk.common.Log;
4 
5 import Jama.Matrix;
6 
7 // TODO: Auto-generated Javadoc
16 public class RotationNRLegacy {
17 
19  double[][] rotationMatrix = new double[][] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
20 
24  public RotationNRLegacy() {
25  }
26 
37  // create a new object with the given simplified rotations
38  public RotationNRLegacy(double tilt, double elevation, double azumeth) {
39  if (Double.isNaN(tilt))
40  throw new NumberFormatException("Value can not be NaN");
41  if (Double.isNaN(azumeth))
42  throw new NumberFormatException("Value can not be NaN");
43  if (Double.isNaN(elevation))
44  throw new NumberFormatException("Value can not be NaN");
45  if (elevation >= 90 || elevation <= -90)
46  throw new NumberFormatException("Elevation must be between 90 and -90");
47  loadFromAngles(tilt, azumeth, elevation);
48  if (Double.isNaN(getRotationMatrix2QuaturnionW()) || Double.isNaN(getRotationMatrix2QuaturnionX())
49  || Double.isNaN(getRotationMatrix2QuaturnionY()) || Double.isNaN(getRotationMatrix2QuaturnionZ())) {
50  // System.err.println("Failing to set proper angle, jittering");
51  loadFromAngles(tilt + Math.random() * .02 + .001, azumeth + Math.random() * .02 + .001,
52  elevation + Math.random() * .02 + .001);
53  }
54 
55  }
56 
57  private void loadFromAngles(double tilt, double azumeth, double elevation) {
58  double attitude = Math.toRadians(elevation);
59  double heading = Math.toRadians(azumeth);
60  double bank = Math.toRadians(tilt);
61  double w, x, y, z;
62  // Assuming the angles are in radians.
63  double c1 = Math.cos(heading / 2);
64  // if(Double.isNaN(c1))
65  //
66  double s1 = Math.sin(heading / 2);
67  double c2 = Math.cos(attitude / 2);
68  double s2 = Math.sin(attitude / 2);
69  double c3 = Math.cos(bank / 2);
70  double s3 = Math.sin(bank / 2);
71  double c1c2 = c1 * c2;
72  double s1s2 = s1 * s2;
73  // System.out.println("C1 ="+c1+" S1 ="+s1+" |C2 ="+c2+" S2 ="+s2+" |C3
74  // ="+c3+" S3 ="+s3);
75  w = c1c2 * c3 - s1s2 * s3;
76  x = c1c2 * s3 + s1s2 * c3;
77  y = s1 * c2 * c3 + c1 * s2 * s3;
78  z = c1 * s2 * c3 - s1 * c2 * s3;
79  // System.out.println("W ="+w+" x ="+x+" y ="+y+" z ="+z);
80  quaternion2RotationMatrix(w, x, y, z);
81  }
82 
89  public RotationNRLegacy(double[][] rotationMatrix) {
90  loadRotations(rotationMatrix);
91  }
92 
99  public RotationNRLegacy(double[] values) {
100  this(values[0], values[1], values[2], values[3]);
101  }
102 
110  public static RotationNRLegacy getRotationX(double rotationAngleDegrees) {
111  double[][] rotation = new double[3][3];
112  double rotationAngleRadians = Math.PI / 180 * rotationAngleDegrees;
113 
114  // Rotation matrix, 1st column
115  rotation[0][0] = 1;
116  rotation[1][0] = 0;
117  rotation[2][0] = 0;
118  // Rotation matrix, 2nd column
119  rotation[0][1] = 0;
120  rotation[1][1] = Math.cos(rotationAngleRadians);
121  rotation[2][1] = Math.sin(rotationAngleRadians);
122  // Rotation matrix, 3rd column
123  rotation[0][2] = 0;
124  rotation[1][2] = -Math.sin(rotationAngleRadians);
125  rotation[2][2] = Math.cos(rotationAngleRadians);
126 
127  return new RotationNRLegacy(rotation);
128  }
129 
137  public static RotationNRLegacy getRotationY(double rotationAngleDegrees) {
138  double[][] rotation = new double[3][3];
139  double rotationAngleRadians = Math.PI / 180 * rotationAngleDegrees;
140 
141  // Rotation matrix, 1st column
142  rotation[0][0] = Math.cos(rotationAngleRadians);
143  rotation[1][0] = 0;
144  rotation[2][0] = -Math.sin(rotationAngleRadians);
145  // Rotation matrix, 2nd column
146  rotation[0][1] = 0;
147  rotation[1][1] = 1;
148  rotation[2][1] = 0;
149  // Rotation matrix, 3rd column
150  rotation[0][2] = Math.sin(rotationAngleRadians);
151  rotation[1][2] = 0;
152  rotation[2][2] = Math.cos(rotationAngleRadians);
153 
154  return new RotationNRLegacy(rotation);
155  }
156 
164  public static RotationNRLegacy getRotationZ(double rotationAngleDegrees) {
165  double[][] rotation = new double[3][3];
166  double rotationAngleRadians = Math.PI / 180 * rotationAngleDegrees;
167 
168  // Rotation matrix, 1st column
169  rotation[0][0] = Math.cos(rotationAngleRadians);
170  rotation[1][0] = Math.sin(rotationAngleRadians);
171  rotation[2][0] = 0;
172  // Rotation matrix, 2nd column
173  rotation[0][1] = -Math.sin(rotationAngleRadians);
174  rotation[1][1] = Math.cos(rotationAngleRadians);
175  rotation[2][1] = 0;
176  // Rotation matrix, 3rd column
177  rotation[0][2] = 0;
178  rotation[1][2] = 0;
179  rotation[2][2] = 1;
180 
181  return new RotationNRLegacy(rotation);
182  }
183 
196  // create a new object with the given components
197  public RotationNRLegacy(double w, double x, double y, double z) {
198  quaternion2RotationMatrix(w, x, y, z);
199  }
200 
207  public RotationNRLegacy(Matrix m) {
208  double[][] rotation = new double[3][3];
209  for (int i = 0; i < 3; i++) {
210  for (int j = 0; j < 3; j++) {
211  rotation[i][j] = m.get(i, j);
212  }
213  }
214  loadRotations(rotation);
215  }
216 
223  private void loadRotations(double[][] rotM) {
224  if (rotM.length != 3)
225  throw new RuntimeException("Must be 3x3 rotation matrix");
226  for (int i = 0; i < 3; i++) {
227  if (rotM[i].length != 3) {
228  throw new RuntimeException("Must be 3x3 rotation matrix");
229  }
230  }
231  for (int i = 0; i < 3; i++) {
232  for (int j = 0; j < 3; j++) {
233  // if(rotM[i][j]>1){
234  // rotM[i][j]=0;//normalization
235  // }
236  rotationMatrix[i][j] = rotM[i][j];
237  }
238  }
239  }
240 
246  public double[][] getRotationMatrix() {
247  double[][] b = new double[3][3];
248  for (int i = 0; i < 3; i++) {
249  for (int j = 0; j < 3; j++) {
250  b[i][j] = rotationMatrix[i][j];
251  }
252  }
253  return b;
254  }
255 
256  /*
257  * (non-Javadoc)
258  *
259  * @see java.lang.Object#toString()
260  */
261  // return a string representation of the invoking object
262  public String toString() {
263  String s = "[\n";
264  double[][] m = getRotationMatrix();
265  for (int i = 0; i < 3; i++) {
266  s += "[ ";
267  for (int j = 0; j < 3; j++) {
268  s += m[i][j] + "\t\t";
269  }
270  s += " ]\n";
271  }
272  s += "]";
273  return "Quaturnion: " + "W=" + getRotationMatrix2QuaturnionW() + ", " + "x=" + getRotationMatrix2QuaturnionX()
274  + ", " + "y=" + getRotationMatrix2QuaturnionY() + ", " + "z=" + getRotationMatrix2QuaturnionZ() + "\t"
275  + "Rotation angle (degrees): " + "Azimuth=" + getRotationAzimuth() + ", " + "Elevation=" + getRotationElevation() + ", " + "Tilt="
276  + getRotationTilt() + "";
277  }
278 
286  // return a string representation of the invoking object
287  public String toString(double[][] array) {
288  String s = "[\n";
289  for (int i = 0; i < 3; i++) {
290  s += "[ ";
291  for (int j = 0; j < 3; j++) {
292  s += array[i][j] + "\t\t";
293  }
294  s += " ]\n";
295  }
296  s += "]";
297  return "Matrix = " + s;
298  }
299 
312  protected void quaternion2RotationMatrix(double w, double x, double y, double z) {
313  if (Double.isNaN(w))
314  throw new NumberFormatException("Value can not be NaN");
315  if (Double.isNaN(x))
316  throw new NumberFormatException("Value can not be NaN");
317  if (Double.isNaN(y))
318  throw new NumberFormatException("Value can not be NaN");
319  if (Double.isNaN(z))
320  throw new NumberFormatException("Value can not be NaN");
321  double norm = Math.sqrt(w * w + x * x + y * y + z * z);
322  // we explicitly test norm against one here, saving a division
323  // at the cost of a test and branch. Is it worth it?
324  double s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0;
325  // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
326  // will be used 2-4 times each.
327  double xs = x * s;
328  double ys = y * s;
329  double zs = z * s;
330  double xx = x * xs;
331  double xy = x * ys;
332  double xz = x * zs;
333  double xw = w * xs;
334  double yy = y * ys;
335  double yz = y * zs;
336  double yw = w * ys;
337  double zz = z * zs;
338  double zw = w * zs;
339 
340  // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
341  rotationMatrix[0][0] = 1 - (yy + zz);
342  rotationMatrix[0][1] = (xy - zw);
343  rotationMatrix[0][2] = (xz + yw);
344 
345  rotationMatrix[1][0] = (xy + zw);
346  rotationMatrix[1][1] = 1 - (xx + zz);
347  rotationMatrix[1][2] = (yz - xw);
348 
349  rotationMatrix[2][0] = (xz - yw);
350  rotationMatrix[2][1] = (yz + xw);
351  rotationMatrix[2][2] = 1 - (xx + yy);
352 
353  toString(rotationMatrix);
354  }
355 
356  // /**
357  // * This requires a pure rotation matrix 'm' as input. from
358  // *
359  // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/
360  // *
361  // * @return the double[]
362  // */
363  // public double[] toAxisAngle() {
364  // double angle, x, y, z; // variables for result
365  // double epsilon = 0.01; // margin to allow for rounding errors
366  // double epsilon2 = 0.1; // margin to distinguish between 0 and 180
367  // // degrees
368  // // optional check that input is pure rotation, 'isRotationMatrix' is
369  // // defined at:
370  // //
371  // http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/
372  // if (((Math.abs(rotationMatrix[0][1]) - Math.abs(rotationMatrix[1][0])) <
373  // epsilon)
374  // && ((Math.abs(rotationMatrix[0][2]) - Math.abs(rotationMatrix[2][0])) <
375  // epsilon)
376  // && ((Math.abs(rotationMatrix[1][2]) - Math.abs(rotationMatrix[2][1])) <
377  // epsilon)) {
378  // // singularity found
379  // // first check for identity matrix which must have +1 for all terms
380  // // in leading diagonaland zero in other terms
381  // if ((Math.abs(rotationMatrix[0][1]) + Math.abs(rotationMatrix[1][0])) <
382  // epsilon2
383  // && (Math.abs(rotationMatrix[0][2]) + Math.abs(rotationMatrix[2][0])) <
384  // epsilon2
385  // && (Math.abs(rotationMatrix[1][2]) + Math.abs(rotationMatrix[2][1])) <
386  // epsilon2
387  // && (Math.abs(rotationMatrix[0][0]) + Math.abs(rotationMatrix[1][1]) +
388  // Math.abs(rotationMatrix[2][2])
389  // - 3) < epsilon2) {
390  // // this singularity is identity matrix so angle = 0
391  // return new double[] { 0, 1, 0, 0 }; // zero angle, arbitrary
392  // // axis
393  // }
394  // // otherwise this singularity is angle = 180
395  // angle = Math.PI;
396  // double xx = (rotationMatrix[0][0] + 1) / 2;
397  // double yy = (rotationMatrix[1][1] + 1) / 2;
398  // double zz = (rotationMatrix[2][2] + 1) / 2;
399  // double xy = (rotationMatrix[0][1] + rotationMatrix[1][0]) / 4;
400  // double xz = (rotationMatrix[0][2] + rotationMatrix[2][0]) / 4;
401  // double yz = (rotationMatrix[1][2] + rotationMatrix[2][1]) / 4;
402  // if ((xx > yy) && (xx > zz)) { // m[0][0] is the largest diagonal
403  // // term
404  // if (xx < epsilon) {
405  // x = 0;
406  // y = 0.7071;
407  // z = 0.7071;
408  // } else {
409  // x = Math.sqrt(xx);
410  // y = xy / x;
411  // z = xz / x;
412  // }
413  // } else if (yy > zz) { // m[1][1] is the largest diagonal term
414  // if (yy < epsilon) {
415  // x = 0.7071;
416  // y = 0;
417  // z = 0.7071;
418  // } else {
419  // y = Math.sqrt(yy);
420  // x = xy / y;
421  // z = yz / y;
422  // }
423  // } else { // m[2][2] is the largest diagonal term so base result on
424  // // this
425  // if (zz < epsilon) {
426  // x = 0.7071;
427  // y = 0.7071;
428  // z = 0;
429  // } else {
430  // z = Math.sqrt(zz);
431  // x = xz / z;
432  // y = yz / z;
433  // }
434  // }
435  // return new double[] { angle, x, y, z }; // return 180 deg rotation
436  // }
437  // // as we have reached here there are no singularities so we can handle
438  // // normally
439  // double s = Math
440  // .sqrt((rotationMatrix[2][1] - rotationMatrix[1][2]) *
441  // (rotationMatrix[2][1] - rotationMatrix[1][2])
442  // + (rotationMatrix[0][2] - rotationMatrix[2][0]) * (rotationMatrix[0][2] -
443  // rotationMatrix[2][0])
444  // + (rotationMatrix[1][0] - rotationMatrix[0][1])
445  // * (rotationMatrix[1][0] - rotationMatrix[0][1])); // used
446  // // to
447  // // normalise
448  // if (Math.abs(s) < 0.001)
449  // s = 1;
450  // // prevent divide by zero, should not happen if matrix is orthogonal and
451  // // should be
452  // // caught by singularity test above, but I've left it in just in case
453  // angle = Math.acos((rotationMatrix[0][0] + rotationMatrix[1][1] +
454  // rotationMatrix[2][2] - 1) / 2);
455  // x = (rotationMatrix[2][1] - rotationMatrix[1][2]) / s;
456  // y = (rotationMatrix[0][2] - rotationMatrix[2][0]) / s;
457  // z = (rotationMatrix[1][0] - rotationMatrix[0][1]) / s;
458  // return new double[] { angle, x, y, z };
459  // }
460 
472  public static boolean bound(double low, double high, double n) {
473  return n >= low && n <= high;
474  }
475 
483  private double getRotAngle(int index) {
484  double w, x, y, z, tilt, elev, azumeth;
489  double sqw = w * w;
490  double sqx = x * x;
491  double sqy = y * y;
492  double sqz = z * z;
493  double unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise
494  // is correction factor
495  double test = x * y + z * w;
496  double testingValue = (0.5 - Double.MIN_VALUE) * unit;// this is a far
497  // more robust
498  // bound
499  // checking
500  // using the
501  // min value of
502  // the data type
503  if (test > testingValue) { // singularity at north pole
504  Log.warning("North pole singularity ");
505  elev = 2 * Math.atan2(x, w);
506  azumeth = Math.PI / 2;
507  tilt = 0;
508 
509  } else if (test < -testingValue) { // singularity at south pole
510  Log.warning("South pole singularity");
511  elev = -2 * Math.atan2(x, w);
512  azumeth = -Math.PI / 2;
513  tilt = 0;
514 
515  } else {
516  elev = Math.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw);
517  azumeth = Math.asin(2 * test / unit);
518  tilt = Math.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw);
519  }
520 
521  switch (index) {
522  case 0:
523  return tilt;
524  case 1:
525  return elev;
526  case 2:
527  return azumeth;
528  default:
529  return 0;
530  }
531 
532  }
533 
534  // public double getRotationBank() {
535  //
536  // return getRotAngle(0) ;
537  //
538  // }
539 
540  // public double getRotationAttitude() {
541  //
542  // return getRotAngle(2);
543  // }
544  //
545  // public double getRotationHeading() {
546  //
547  // return getRotAngle(1) ;
548  // }
549 
555  public double getRotationTilt() {
556 
557  return getRotAngle(0);
558 
559  }
560 
566  public double getRotationElevation() {
567 
568  return getRotAngle(1);
569  }
570 
576  public double getRotationAzimuth() {
577 
578  return getRotAngle(2);
579  }
580 
586 // @Deprecated // use getRotationBank()
587 // public double getRotationX() {
588 //
589 // return getRotAngle(0);
590 //
591 // }
592 
598 // @Deprecated // use getRotationAttitude()
599 // public double getRotationY() {
600 //
601 // return getRotAngle(2);
602 // }
603 
609 // @Deprecated // use getRotationHeading()
610 // public double getRotationZ() {
611 //
612 // return getRotAngle(1);
613 // }
614 
621  double temp = 0.5 * Math.sqrt(1 + rotationMatrix[0][0] + rotationMatrix[1][1] + rotationMatrix[2][2]);
622  if (temp > 1)
623  throw new RuntimeException("Matrix needs normalization");
624  return temp;
625  }
626 
633  double temp = 0.5 * Math.sqrt(1 + rotationMatrix[0][0] + rotationMatrix[1][1] + rotationMatrix[2][2]);
634  return (rotationMatrix[2][1] - rotationMatrix[1][2]) * 0.25 / temp;
635  }
636 
643  double temp = 0.5 * Math.sqrt(1 + rotationMatrix[0][0] + rotationMatrix[1][1] + rotationMatrix[2][2]);
644  return (rotationMatrix[0][2] - rotationMatrix[2][0]) * 0.25 / temp;
645  }
646 
653  double temp = 0.5 * Math.sqrt(1 + rotationMatrix[0][0] + rotationMatrix[1][1] + rotationMatrix[2][2]);
654  return (rotationMatrix[1][0] - rotationMatrix[0][1]) * 0.25 / temp;
655  }
656 
657 }
static RotationNRLegacy getRotationX(double rotationAngleDegrees)
void quaternion2RotationMatrix(double w, double x, double y, double z)
void loadFromAngles(double tilt, double azumeth, double elevation)
static boolean bound(double low, double high, double n)
static RotationNRLegacy getRotationY(double rotationAngleDegrees)
static RotationNRLegacy getRotationZ(double rotationAngleDegrees)
RotationNRLegacy(double tilt, double elevation, double azumeth)
static void warning(String message)
Definition: Log.java:101