diff --git a/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java b/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
index d86d141561e..f843fde7b9d 100644
--- a/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
+++ b/wpimath/src/main/java/edu/wpi/first/math/WPIMathJNI.java
@@ -76,6 +76,78 @@ public static native void dare(
*/
public static native void pow(double[] src, int rows, double exponent, double[] dst);
+ /**
+ * Obtain a Pose3d from a (constant curvature) velocity.
+ *
+ *
The double array returned is of the form [dx, dy, dz, qx, qy, qz].
+ *
+ * @param poseX The pose's translational X component.
+ * @param poseY The pose's translational Y component.
+ * @param poseZ The pose's translational Z component.
+ * @param poseQw The pose quaternion's W component.
+ * @param poseQx The pose quaternion's X component.
+ * @param poseQy The pose quaternion's Y component.
+ * @param poseQz The pose quaternion's Z component.
+ * @param twistDx The twist's dx value.
+ * @param twistDy The twist's dy value.
+ * @param twistDz The twist's dz value.
+ * @param twistRx The twist's rx value.
+ * @param twistRy The twist's ry value.
+ * @param twistRz The twist's rz value.
+ * @return The new pose as a double array.
+ */
+ public static native double[] expPose3d(
+ double poseX,
+ double poseY,
+ double poseZ,
+ double poseQw,
+ double poseQx,
+ double poseQy,
+ double poseQz,
+ double twistDx,
+ double twistDy,
+ double twistDz,
+ double twistRx,
+ double twistRy,
+ double twistRz);
+
+ /**
+ * Returns a Twist3d that maps the starting pose to the end pose.
+ *
+ *
The double array returned is of the form [dx, dy, dz, rx, ry, rz].
+ *
+ * @param startX The starting pose's translational X component.
+ * @param startY The starting pose's translational Y component.
+ * @param startZ The starting pose's translational Z component.
+ * @param startQw The starting pose quaternion's W component.
+ * @param startQx The starting pose quaternion's X component.
+ * @param startQy The starting pose quaternion's Y component.
+ * @param startQz The starting pose quaternion's Z component.
+ * @param endX The ending pose's translational X component.
+ * @param endY The ending pose's translational Y component.
+ * @param endZ The ending pose's translational Z component.
+ * @param endQw The ending pose quaternion's W component.
+ * @param endQx The ending pose quaternion's X component.
+ * @param endQy The ending pose quaternion's Y component.
+ * @param endQz The ending pose quaternion's Z component.
+ * @return The twist that maps start to end as a double array.
+ */
+ public static native double[] logPose3d(
+ double startX,
+ double startY,
+ double startZ,
+ double startQw,
+ double startQx,
+ double startQy,
+ double startQz,
+ double endX,
+ double endY,
+ double endZ,
+ double endQw,
+ double endQx,
+ double endQy,
+ double endQz);
+
/**
* Returns true if (A, B) is a stabilizable pair.
*
diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
index 9e46ddb064e..26a3d56d5a7 100644
--- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
+++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Pose3d.java
@@ -8,14 +8,8 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
-import edu.wpi.first.math.MatBuilder;
-import edu.wpi.first.math.Matrix;
-import edu.wpi.first.math.Nat;
-import edu.wpi.first.math.VecBuilder;
-import edu.wpi.first.math.Vector;
+import edu.wpi.first.math.WPIMathJNI;
import edu.wpi.first.math.interpolation.Interpolatable;
-import edu.wpi.first.math.numbers.N1;
-import edu.wpi.first.math.numbers.N3;
import java.util.Objects;
/** Represents a 3D pose containing translational and rotational elements. */
@@ -201,53 +195,28 @@ public Pose3d relativeTo(Pose3d other) {
* @return The new pose of the robot.
*/
public Pose3d exp(Twist3d twist) {
- // Implementation from Section 3.2 of https://ethaneade.org/lie.pdf
- final var u = VecBuilder.fill(twist.dx, twist.dy, twist.dz);
- final var rvec = VecBuilder.fill(twist.rx, twist.ry, twist.rz);
- final var omega = rotationVectorToMatrix(rvec);
- final var omegaSq = omega.times(omega);
- double theta = rvec.norm();
- double thetaSq = theta * theta;
-
- double A;
- double B;
- double C;
- if (Math.abs(theta) < 1E-7) {
- // Taylor Expansions around θ = 0
- // A = 1/1! - θ²/3! + θ⁴/5!
- // B = 1/2! - θ²/4! + θ⁴/6!
- // C = 1/3! - θ²/5! + θ⁴/7!
- // sources:
- // A:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D+at+x%3D0
- // B:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D+at+x%3D0
- // C:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D%2CPower%5Bx%2C2%5D%5D+at+x%3D0
- A = 1 - thetaSq / 6 + thetaSq * thetaSq / 120;
- B = 1 / 2.0 - thetaSq / 24 + thetaSq * thetaSq / 720;
- C = 1 / 6.0 - thetaSq / 120 + thetaSq * thetaSq / 5040;
- } else {
- // A = sin(θ)/θ
- // B = (1 - cos(θ)) / θ²
- // C = (1 - A) / θ²
- A = Math.sin(theta) / theta;
- B = (1 - Math.cos(theta)) / thetaSq;
- C = (1 - A) / thetaSq;
- }
-
- Matrix R = Matrix.eye(Nat.N3()).plus(omega.times(A)).plus(omegaSq.times(B));
- Matrix V = Matrix.eye(Nat.N3()).plus(omega.times(B)).plus(omegaSq.times(C));
- Matrix translation_component = V.times(u);
- final var transform =
- new Transform3d(
- new Translation3d(
- translation_component.get(0, 0),
- translation_component.get(1, 0),
- translation_component.get(2, 0)),
- new Rotation3d(R));
-
- return this.plus(transform);
+ var quaternion = this.getRotation().getQuaternion();
+ double[] resultArray =
+ WPIMathJNI.expPose3d(
+ this.getX(),
+ this.getY(),
+ this.getZ(),
+ quaternion.getW(),
+ quaternion.getX(),
+ quaternion.getY(),
+ quaternion.getZ(),
+ twist.dx,
+ twist.dy,
+ twist.dz,
+ twist.rx,
+ twist.ry,
+ twist.rz);
+ return new Pose3d(
+ resultArray[0],
+ resultArray[1],
+ resultArray[2],
+ new Rotation3d(
+ new Quaternion(resultArray[3], resultArray[4], resultArray[5], resultArray[6])));
}
/**
@@ -258,51 +227,31 @@ public Pose3d exp(Twist3d twist) {
* @return The twist that maps this to end.
*/
public Twist3d log(Pose3d end) {
- // Implementation from Section 3.2 of https://ethaneade.org/lie.pdf
- final var transform = end.relativeTo(this);
-
- final var rvec = transform.getRotation().getQuaternion().toRotationVector();
-
- final var omega = rotationVectorToMatrix(rvec);
- final var theta = rvec.norm();
- final var thetaSq = theta * theta;
-
- double C;
- if (Math.abs(theta) < 1E-7) {
- // Taylor Expansions around θ = 0
- // A = 1/1! - θ²/3! + θ⁴/5!
- // B = 1/2! - θ²/4! + θ⁴/6!
- // C = 1/6 * (1/2 + θ²/5! + θ⁴/7!)
- // sources:
- // A:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D+at+x%3D0
- // B:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D+at+x%3D0
- // C:
- // https://www.wolframalpha.com/input?i2d=true&i=series+expansion+of+Divide%5B1-Divide%5BDivide%5Bsin%5C%2840%29x%5C%2841%29%2Cx%5D%2C2Divide%5B1-cos%5C%2840%29x%5C%2841%29%2CPower%5Bx%2C2%5D%5D%5D%2CPower%5Bx%2C2%5D%5D+at+x%3D0
- C = 1 / 12.0 + thetaSq / 720 + thetaSq * thetaSq / 30240;
- } else {
- // A = sin(θ)/θ
- // B = (1 - cos(θ)) / θ²
- // C = (1 - A/(2*B)) / θ²
- double A = Math.sin(theta) / theta;
- double B = (1 - Math.cos(theta)) / thetaSq;
- C = (1 - A / (2 * B)) / thetaSq;
- }
-
- final var V_inv =
- Matrix.eye(Nat.N3()).minus(omega.times(0.5)).plus(omega.times(omega).times(C));
-
- final var twist_translation =
- V_inv.times(VecBuilder.fill(transform.getX(), transform.getY(), transform.getZ()));
-
+ var thisQuaternion = this.getRotation().getQuaternion();
+ var endQuaternion = end.getRotation().getQuaternion();
+ double[] resultArray =
+ WPIMathJNI.logPose3d(
+ this.getX(),
+ this.getY(),
+ this.getZ(),
+ thisQuaternion.getW(),
+ thisQuaternion.getX(),
+ thisQuaternion.getY(),
+ thisQuaternion.getZ(),
+ end.getX(),
+ end.getY(),
+ end.getZ(),
+ endQuaternion.getW(),
+ endQuaternion.getX(),
+ endQuaternion.getY(),
+ endQuaternion.getZ());
return new Twist3d(
- twist_translation.get(0, 0),
- twist_translation.get(1, 0),
- twist_translation.get(2, 0),
- rvec.get(0, 0),
- rvec.get(1, 0),
- rvec.get(2, 0));
+ resultArray[0],
+ resultArray[1],
+ resultArray[2],
+ resultArray[3],
+ resultArray[4],
+ resultArray[5]);
}
/**
@@ -353,31 +302,4 @@ public Pose3d interpolate(Pose3d endValue, double t) {
return this.exp(scaledTwist);
}
}
-
- /**
- * Applies the hat operator to a rotation vector.
- *
- * It takes a rotation vector and returns the corresponding matrix representation of the Lie
- * algebra element (a 3x3 rotation matrix).
- *
- * @param rotation The rotation vector.
- * @return The rotation vector as a 3x3 rotation matrix.
- */
- private Matrix rotationVectorToMatrix(Vector rotation) {
- // Given a rotation vector ,
- // [ 0 -c b]
- // Omega = [ c 0 -a]
- // [-b a 0]
- return new MatBuilder<>(Nat.N3(), Nat.N3())
- .fill(
- 0.0,
- -rotation.get(2, 0),
- rotation.get(1, 0),
- rotation.get(2, 0),
- 0.0,
- -rotation.get(0, 0),
- -rotation.get(1, 0),
- rotation.get(0, 0),
- 0.0);
- }
}
diff --git a/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp b/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
index 4cdcf706f62..bfc07cc16fc 100644
--- a/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
+++ b/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp
@@ -14,6 +14,7 @@
#include "Eigen/QR"
#include "edu_wpi_first_math_WPIMathJNI.h"
#include "frc/DARE.h"
+#include "frc/geometry/Pose3d.h"
#include "frc/trajectory/TrajectoryUtil.h"
#include "unsupported/Eigen/MatrixFunctions"
@@ -187,6 +188,60 @@ Java_edu_wpi_first_math_WPIMathJNI_pow
env->SetDoubleArrayRegion(dst, 0, rows * rows, Apow.data());
}
+/*
+ * Class: edu_wpi_first_math_WPIMathJNI
+ * Method: expPose3d
+ * Signature: (DDDDDDDDDDDDD)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_expPose3d
+ (JNIEnv* env, jclass, jdouble poseX, jdouble poseY, jdouble poseZ,
+ jdouble poseQw, jdouble poseQx, jdouble poseQy, jdouble poseQz,
+ jdouble twistDx, jdouble twistDy, jdouble twistDz, jdouble twistRx,
+ jdouble twistRy, jdouble twistRz)
+{
+ frc::Pose3d pose{
+ units::meter_t{poseX}, units::meter_t{poseY}, units::meter_t{poseZ},
+ frc::Rotation3d{frc::Quaternion{poseQw, poseQx, poseQy, poseQz}}};
+ frc::Twist3d twist{units::meter_t{twistDx}, units::meter_t{twistDy},
+ units::meter_t{twistDz}, units::radian_t{twistRx},
+ units::radian_t{twistRy}, units::radian_t{twistRz}};
+
+ frc::Pose3d result = pose.Exp(twist);
+
+ const auto& resultQuaternion = result.Rotation().GetQuaternion();
+ return MakeJDoubleArray(
+ env, {{result.X().value(), result.Y().value(), result.Z().value(),
+ resultQuaternion.W(), resultQuaternion.X(), resultQuaternion.Y(),
+ resultQuaternion.Z()}});
+}
+
+/*
+ * Class: edu_wpi_first_math_WPIMathJNI
+ * Method: logPose3d
+ * Signature: (DDDDDDDDDDDDDD)[D
+ */
+JNIEXPORT jdoubleArray JNICALL
+Java_edu_wpi_first_math_WPIMathJNI_logPose3d
+ (JNIEnv* env, jclass, jdouble startX, jdouble startY, jdouble startZ,
+ jdouble startQw, jdouble startQx, jdouble startQy, jdouble startQz,
+ jdouble endX, jdouble endY, jdouble endZ, jdouble endQw, jdouble endQx,
+ jdouble endQy, jdouble endQz)
+{
+ frc::Pose3d startPose{
+ units::meter_t{startX}, units::meter_t{startY}, units::meter_t{startZ},
+ frc::Rotation3d{frc::Quaternion{startQw, startQx, startQy, startQz}}};
+ frc::Pose3d endPose{
+ units::meter_t{endX}, units::meter_t{endY}, units::meter_t{endZ},
+ frc::Rotation3d{frc::Quaternion{endQw, endQx, endQy, endQz}}};
+
+ frc::Twist3d result = startPose.Log(endPose);
+
+ return MakeJDoubleArray(
+ env, {{result.dx.value(), result.dy.value(), result.dz.value(),
+ result.rx.value(), result.ry.value(), result.rz.value()}});
+}
+
/*
* Class: edu_wpi_first_math_WPIMathJNI
* Method: isStabilizable