From ee1ef8c9bf091303c060c8353c536b8ff947592a Mon Sep 17 00:00:00 2001
From: Robert
Date: Wed, 27 Aug 2025 10:30:13 -0500
Subject: [PATCH 01/18] did a bit of a refactor on the EKF and an improved
javadoc
---
.../ExtendedKalmanFilter.java | 16 ++
...ExampleConstantVelocity2DKalmanFilter.java | 74 +++++++++
.../ExamplePendulumExtendedKalmanFilter.java | 136 +++++++++++++++++
...antVelocity2DExtendedKalmanFilterDemo.java | 88 +----------
.../PendulumExtendedKalmanFilterDemo.java | 141 ++++--------------
5 files changed, 266 insertions(+), 189 deletions(-)
create mode 100644 ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExampleConstantVelocity2DKalmanFilter.java
create mode 100644 ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExamplePendulumExtendedKalmanFilter.java
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index 35ce3cc8dee1..83f357673a45 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -13,6 +13,22 @@
*
* This implementation is for state-only systems, no input -- or if you prefer: x_dot = f(x), not x_dot = f(x, u).
*
+ *
+ * To utilize this filter, the user must extend this class and provide implementations of the process and measurement models by overriding the abstract methods:
+ *
+ * - {@link #processModel(DMatrixRMaj)}
+ * - {@link #measurementModel(DMatrixRMaj)}
+ *
+ * Then, to get updates, the user calls {@link #calculateEstimate(DMatrix)} with the observation at the current time step. This will return the estimated
+ * state based on the observations.
+ *
+ *
+ * For example implementations, see:
+ *
+ * - {@link us.ihmc.parameterEstimation.examples.ExamplePendulumExtendedKalmanFilter}
+ * - {@link us.ihmc.parameterEstimation.examples.ExamplePendulumExtendedKalmanFilter}
+ *
+ *
*
* @author James Foster
*/
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExampleConstantVelocity2DKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExampleConstantVelocity2DKalmanFilter.java
new file mode 100644
index 000000000000..264adf477c08
--- /dev/null
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExampleConstantVelocity2DKalmanFilter.java
@@ -0,0 +1,74 @@
+package us.ihmc.parameterEstimation.examples;
+
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
+
+public class ExampleConstantVelocity2DKalmanFilter extends ExtendedKalmanFilter
+{
+ static final int stateSize = 4;
+ static final int measurementSize = 2;
+
+ // Start at (x,y) = (0,0) with no velocity
+ static final DMatrixRMaj x0 = new DMatrixRMaj(new double[] {0.0, 0.0, 0.0, 0.0});
+
+ // Position is barely affected by noise, velocity is affected by noise
+ static final DMatrixRMaj Q = new DMatrixRMaj(new double[][] {{1e-6, 0.0, 0.0, 0.0},
+ {0.0, 1e-6, 0.0, 0.0},
+ {0.0, 0.0, 1e-6, 0.0},
+ {0.0, 0.0, 0.0, 1e-6}});
+
+ // Both x and y position measurements are corrupted by noise
+ static final DMatrixRMaj R = new DMatrixRMaj(new double[][] {{1, 0.0}, {0.0, 1}});
+
+ // Ignorant initial guess on P0, we assume we're more certain about positions than velocities
+ static final DMatrixRMaj P0 = new DMatrixRMaj(new double[][] {{0.1, 0.0, 0.0, 0.0},
+ {0.0, 1.0, 0.0, 0.0},
+ {0.0, 0.0, 0.1, 0.0},
+ {0.0, 0.0, 0.0, 1.0}});
+
+ static final double dt = 0.01;
+
+ // Constant velocity model of a 2D planar system
+ static final DMatrixRMaj A = new DMatrixRMaj(new double[][] {{1.0, dt, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, dt}, {0.0, 0.0, 0.0, 1.0}});
+
+ // We only measure positions, not velocities
+ static final DMatrixRMaj C = new DMatrixRMaj(new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}});
+
+ public ExampleConstantVelocity2DKalmanFilter()
+ {
+ super(x0, P0, Q, R);
+ }
+
+ // In the case of a linear process model, the linearization is just the A matrix of the process model
+ @Override
+ public DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
+ {
+ return A;
+ }
+
+ // In the case of a linear measurement model, the linearization is just the C matrix of the measurement model
+ @Override
+ public DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
+ {
+ return C;
+ }
+
+ // y = Ax
+ @Override
+ public DMatrixRMaj processModel(DMatrixRMaj state)
+ {
+ DMatrixRMaj nextState = new DMatrixRMaj(stateSize, 1);
+ CommonOps_DDRM.mult(A, state, nextState);
+ return nextState;
+ }
+
+ // y = Cx
+ @Override
+ public DMatrixRMaj measurementModel(DMatrixRMaj state)
+ {
+ DMatrixRMaj measurement = new DMatrixRMaj(measurementSize, 1);
+ CommonOps_DDRM.mult(C, state, measurement);
+ return measurement;
+ }
+}
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExamplePendulumExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExamplePendulumExtendedKalmanFilter.java
new file mode 100644
index 000000000000..7bc9cf752727
--- /dev/null
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/examples/ExamplePendulumExtendedKalmanFilter.java
@@ -0,0 +1,136 @@
+package us.ihmc.parameterEstimation.examples;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
+
+public class ExamplePendulumExtendedKalmanFilter extends ExtendedKalmanFilter
+{
+ static final int stateSize = 2;
+ static final int measurementSize = 1;
+
+ // Picking an arbitrary nonzero initial condition since 0 is a singularity for the measurement
+ private static final DMatrixRMaj x0 = new DMatrixRMaj(new double[] {0.1, 0.1});
+
+ // Process noise
+ private static final DMatrixRMaj Q = new DMatrixRMaj(new double[][] {{1e-6, 0.0}, {0.0, 1e-6}});
+
+ // Measurement noise
+ private static final DMatrixRMaj R = new DMatrixRMaj(new double[][] {{1e-3}});
+
+ // Ignorant initial guess on P0, we assume we're more certain about positions than velocities
+ private static final DMatrixRMaj P0 = new DMatrixRMaj(new double[][] {{0.1, 0.0}, {0.0, 1.0}});
+
+ // Linearized process model dynamics ( Jacobian of f(x) where x[k+1] = f(x[k]) )
+ final DMatrixRMaj F = new DMatrixRMaj(stateSize, stateSize);
+
+ // Linearized measurement model dynamics (Jacobian of h(x) where y = h(x))
+ final DMatrixRMaj H = new DMatrixRMaj(measurementSize, stateSize);
+
+ private double I = 0.006; // inertia [kg m^2]
+ private double m = 0.2; // mass [kg]
+ private double b = 0.02; // damping coefficient (not pendulum) [N/s]
+ private double g = 9.8; // acceleration due to gravity [m/s^2]
+ private double l = 0.3; // length to center of mass [m]
+ private double dt = 0.01; // integration dt [s]
+
+ public ExamplePendulumExtendedKalmanFilter()
+ {
+ super(x0, P0, Q, R);
+ }
+
+ public void setModelProperties(double m, double I, double l, double b, double g, double dt)
+ {
+ this.m = m;
+ this.I = I;
+ this.l = l;
+ this.b = b;
+ this.g = g;
+ this.dt = dt;
+ }
+
+ public DMatrixRMaj getQ()
+ {
+ return Q;
+ }
+
+ public DMatrixRMaj getR()
+ {
+ return R;
+ }
+
+ public DMatrixRMaj getF()
+ {
+ return F;
+ }
+
+ public DMatrixRMaj getH()
+ {
+ return H;
+ }
+
+ // Linearize f(x) to obtain it's Jacobian matrix, F(x)
+ @Override
+ public DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
+ {
+ double q = previousState.get(0, 0);
+
+ F.set(0, 0, 1);
+ F.set(0, 1, dt);
+ F.set(1, 0, -(dt * m * g * l * Math.cos(q)) / I);
+ F.set(1, 1, 1 - dt * b / I);
+
+ return F;
+ }
+
+ // Linearize h(x) to obtain it's Jacobian matrix, H(x)
+ @Override
+ public DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
+ {
+ double q = predictedState.get(0, 0);
+ double qDot = predictedState.get(1, 0);
+
+ H.set(0, 0, m * g * l * Math.sin(q));
+ H.set(0, 1, I * qDot);
+
+ return H;
+ }
+
+ // x[k+1] = f(x[k])
+ @Override
+ public DMatrixRMaj processModel(DMatrixRMaj state)
+ {
+ return getNextState(state);
+ }
+
+ // y = h(x)
+ @Override
+ public DMatrixRMaj measurementModel(DMatrixRMaj state)
+ {
+ return getMeasurement(state);
+ }
+
+ DMatrixRMaj getNextState(DMatrixRMaj state)
+ {
+ DMatrixRMaj nextState = new DMatrixRMaj(state.numRows, 1);
+ double q = state.get(0, 0);
+ double qDot = state.get(1, 0);
+
+ // Discretized with Forward Euler
+ nextState.set(0, 0, q + dt * qDot);
+ nextState.set(1, 0, qDot - dt * (b * qDot + m * g * l * Math.sin(q)) / I);
+
+ return nextState;
+ }
+
+ DMatrixRMaj getMeasurement(DMatrixRMaj state)
+ {
+ DMatrixRMaj measurement = new DMatrixRMaj(ExamplePendulumExtendedKalmanFilter.measurementSize, 1);
+ double q = state.get(0, 0);
+ double qDot = state.get(1, 0);
+
+ // Measurement is the total energy in the system, which will be dissipated with nonzero damping.
+ measurement.set(0, 0, 0.5 * I * qDot * qDot + m * g * l * (1 - Math.cos(q)));
+
+ return measurement;
+ }
+}
diff --git a/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/ConstantVelocity2DExtendedKalmanFilterDemo.java b/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/ConstantVelocity2DExtendedKalmanFilterDemo.java
index d7d40f8317bd..cd199e0b5f28 100644
--- a/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/ConstantVelocity2DExtendedKalmanFilterDemo.java
+++ b/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/ConstantVelocity2DExtendedKalmanFilterDemo.java
@@ -5,7 +5,6 @@
import org.knowm.xchart.QuickChart;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XYChart;
-import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
import us.ihmc.parameterEstimation.ExtendedKalmanFilterTestTools.LinearSystem;
import javax.swing.*;
@@ -22,77 +21,6 @@
*/
public class ConstantVelocity2DExtendedKalmanFilterDemo
{
- public static class ConstantVelocity2DKalmanFilter extends ExtendedKalmanFilter
- {
- private static final int stateSize = 4;
- private static final int measurementSize = 2;
-
- // Start at (x,y) = (0,0) with no velocity
- private static final DMatrixRMaj x0 = new DMatrixRMaj(new double[] {0.0, 0.0, 0.0, 0.0});
-
- // Position is barely affected by noise, velocity is affected by noise
- private static final DMatrixRMaj Q = new DMatrixRMaj(new double[][] {{1e-6, 0.0, 0.0, 0.0},
- {0.0, 1e-6, 0.0, 0.0},
- {0.0, 0.0, 1e-6, 0.0},
- {0.0, 0.0, 0.0, 1e-6}});
-
- // Both x and y position measurements are corrupted by noise
- private static final DMatrixRMaj R = new DMatrixRMaj(new double[][] {{1, 0.0}, {0.0, 1}});
-
- // Ignorant initial guess on P0, we assume we're more certain about positions than velocities
- private static final DMatrixRMaj P0 = new DMatrixRMaj(new double[][] {{0.1, 0.0, 0.0, 0.0},
- {0.0, 1.0, 0.0, 0.0},
- {0.0, 0.0, 0.1, 0.0},
- {0.0, 0.0, 0.0, 1.0}});
-
- private static final double dt = 0.01;
-
- // Constant velocity model of a 2D planar system
- private static final DMatrixRMaj A = new DMatrixRMaj(new double[][] {{1.0, dt, 0.0, 0.0},
- {0.0, 1.0, 0.0, 0.0},
- {0.0, 0.0, 1.0, dt},
- {0.0, 0.0, 0.0, 1.0}});
-
- // We only measure positions, not velocities
- private static final DMatrixRMaj C = new DMatrixRMaj(new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}});
-
- public ConstantVelocity2DKalmanFilter()
- {
- super(x0, P0, Q, R);
- }
-
- // In the case of a linear process model, the linearization is just the A matrix of the process model
- @Override
- public DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
- {
- return A;
- }
-
- // In the case of a linear measurement model, the linearization is just the C matrix of the measurement model
- @Override
- public DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
- {
- return C;
- }
-
- // y = Ax
- @Override
- public DMatrixRMaj processModel(DMatrixRMaj state)
- {
- DMatrixRMaj nextState = new DMatrixRMaj(stateSize, 1);
- CommonOps_DDRM.mult(A, state, nextState);
- return nextState;
- }
-
- // y = Cx
- @Override
- public DMatrixRMaj measurementModel(DMatrixRMaj state)
- {
- DMatrixRMaj measurement = new DMatrixRMaj(measurementSize, 1);
- CommonOps_DDRM.mult(C, state, measurement);
- return measurement;
- }
- }
private static final int ITERATIONS = 1000;
@@ -102,11 +30,11 @@ public static void main(String[] args)
// We'll set things up such that the real system is always less noisy than the filter. Said the other way, our filter design
// will assume that the system is more noisy than it actually is -- a good thing.
- DMatrixRMaj Q = new DMatrixRMaj(ConstantVelocity2DKalmanFilter.Q);
+ DMatrixRMaj Q = new DMatrixRMaj(ExampleConstantVelocity2DKalmanFilter.Q);
CommonOps_DDRM.scale(0.1, Q);
// We'll set things up such that the real system is always less noisy than the filter. Said the other way, our filter design
// will assume that the system is more noisy than it actually is -- a good thing.
- DMatrixRMaj R = new DMatrixRMaj(ConstantVelocity2DKalmanFilter.R);
+ DMatrixRMaj R = new DMatrixRMaj(ExampleConstantVelocity2DKalmanFilter.R);
CommonOps_DDRM.scale(0.01, R);
// In general, initial state of system is different from that of filter.
@@ -116,14 +44,14 @@ public static void main(String[] args)
// LinearSystem system = new LinearSystem(initialSystemState,
// ConstantVelocity2DKalmanFilter.A,
// ConstantVelocity2DKalmanFilter.C);
- LinearSystem system = new LinearSystem(initialSystemState, ConstantVelocity2DKalmanFilter.A, Q, ConstantVelocity2DKalmanFilter.C, R, random);
+ LinearSystem system = new LinearSystem(initialSystemState, ExampleConstantVelocity2DKalmanFilter.A, Q, ExampleConstantVelocity2DKalmanFilter.C, R, random);
- DMatrixRMaj state = new DMatrixRMaj(ConstantVelocity2DKalmanFilter.stateSize, 1);
- DMatrixRMaj measurement = new DMatrixRMaj(ConstantVelocity2DKalmanFilter.measurementSize, 1);
+ DMatrixRMaj state = new DMatrixRMaj(ExampleConstantVelocity2DKalmanFilter.stateSize, 1);
+ DMatrixRMaj measurement = new DMatrixRMaj(ExampleConstantVelocity2DKalmanFilter.measurementSize, 1);
// Filter
- ConstantVelocity2DKalmanFilter kalmanFilter = new ConstantVelocity2DKalmanFilter();
- DMatrixRMaj estimatedState = new DMatrixRMaj(ConstantVelocity2DKalmanFilter.stateSize, 1);
+ ExampleConstantVelocity2DKalmanFilter kalmanFilter = new ExampleConstantVelocity2DKalmanFilter();
+ DMatrixRMaj estimatedState = new DMatrixRMaj(ExampleConstantVelocity2DKalmanFilter.stateSize, 1);
// Arrays for recording data that we'll plot
double[] timestamps = new double[ITERATIONS];
@@ -139,7 +67,7 @@ public static void main(String[] args)
estimatedState.set(kalmanFilter.calculateEstimate(measurement));
- timestamps[i] = i * ConstantVelocity2DKalmanFilter.dt;
+ timestamps[i] = i * ExampleConstantVelocity2DKalmanFilter.dt;
trueStates.add(new DMatrixRMaj(state));
measurements.add(new DMatrixRMaj(measurement));
estimatedStates.add(new DMatrixRMaj(estimatedState));
diff --git a/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/PendulumExtendedKalmanFilterDemo.java b/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/PendulumExtendedKalmanFilterDemo.java
index 56ec3bc0537a..4e66031d5ca9 100644
--- a/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/PendulumExtendedKalmanFilterDemo.java
+++ b/ihmc-parameter-estimation/src/test/java/us/ihmc/parameterEstimation/examples/PendulumExtendedKalmanFilterDemo.java
@@ -5,13 +5,13 @@
import org.knowm.xchart.QuickChart;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XYChart;
-import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
import us.ihmc.parameterEstimation.ExtendedKalmanFilterTestTools.NonlinearSystem;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
+import java.util.function.Function;
/**
* Demo of an EKF being used with a nonlinear process model and measurement model.
@@ -30,139 +30,65 @@ public class PendulumExtendedKalmanFilterDemo
private static final double dt = 0.01;
- public static class PendulumExtendedKalmanFilter extends ExtendedKalmanFilter
- {
- private static final int stateSize = 2;
- private static final int measurementSize = 1;
-
- // Picking an arbitrary nonzero initial condition since 0 is a singularity for the measurement
- private static final DMatrixRMaj x0 = new DMatrixRMaj(new double[] {0.1, 0.1});
-
- // Process noise
- private static final DMatrixRMaj Q = new DMatrixRMaj(new double[][] {{1e-6, 0.0}, {0.0, 1e-6}});
-
- // Measurement noise
- private static final DMatrixRMaj R = new DMatrixRMaj(new double[][] {{1e-3}});
-
- // Ignorant initial guess on P0, we assume we're more certain about positions than velocities
- private static final DMatrixRMaj P0 = new DMatrixRMaj(new double[][] {{0.1, 0.0}, {0.0, 1.0}});
-
- // Linearized process model dynamics ( Jacobian of f(x) where x[k+1] = f(x[k]) )
- private static final DMatrixRMaj F = new DMatrixRMaj(stateSize, stateSize);
-
- // Linearized measurement model dynamics (Jacobian of h(x) where y = h(x))
- private static final DMatrixRMaj H = new DMatrixRMaj(measurementSize, stateSize);
-
- public PendulumExtendedKalmanFilter()
- {
- super(x0, P0, Q, R);
- }
-
- // Linearize f(x) to obtain it's Jacobian matrix, F(x)
- @Override
- public DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
- {
- double q = previousState.get(0, 0);
-
- F.set(0, 0, 1);
- F.set(0, 1, dt);
- F.set(1, 0, -(dt * m * g * l * Math.cos(q)) / I);
- F.set(1, 1, 1 - dt * b / I);
-
- return F;
- }
-
- // Linearize h(x) to obtain it's Jacobian matrix, H(x)
- @Override
- public DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
- {
- double q = predictedState.get(0, 0);
- double qDot = predictedState.get(1, 0);
-
- H.set(0, 0, m * g * l * Math.sin(q));
- H.set(0, 1, I * qDot);
-
- return H;
- }
-
- // x[k+1] = f(x[k])
- @Override
- public DMatrixRMaj processModel(DMatrixRMaj state)
- {
- return getNextState(state);
- }
-
- // y = h(x)
- @Override
- public DMatrixRMaj measurementModel(DMatrixRMaj state)
- {
- return getMeasurement(state);
- }
- }
-
private static class PendulumSystem extends NonlinearSystem
{
- public PendulumSystem(DMatrixRMaj x0, DMatrixRMaj F, DMatrixRMaj H)
+ private final Function systemDynamics;
+ private final Function measurementDynamics;
+
+ public PendulumSystem(DMatrixRMaj x0,
+ DMatrixRMaj F,
+ DMatrixRMaj H,
+ Function systemDynamics,
+ Function measurementDynamics)
{
super(x0, F, H);
+
+ this.systemDynamics = systemDynamics;
+ this.measurementDynamics = measurementDynamics;
}
- public PendulumSystem(DMatrixRMaj x0, DMatrixRMaj F, DMatrixRMaj Q, DMatrixRMaj H, DMatrixRMaj R, Random random)
+ public PendulumSystem(DMatrixRMaj x0, DMatrixRMaj F, DMatrixRMaj Q, DMatrixRMaj H, DMatrixRMaj R, Random random,
+ Function systemDynamics,
+ Function measurementDynamics)
{
super(x0, F, Q, H, R, random);
+
+ this.systemDynamics = systemDynamics;
+ this.measurementDynamics = measurementDynamics;
+
}
@Override
public DMatrixRMaj calculateSystemDynamics(DMatrixRMaj state)
{
- return getNextState(state);
+ return systemDynamics.apply(state);
}
@Override
public DMatrixRMaj calculateMeasurementDynamics(DMatrixRMaj state)
{
- return getMeasurement(state);
+ return measurementDynamics.apply(state);
}
}
- private static DMatrixRMaj getNextState(DMatrixRMaj state)
- {
- DMatrixRMaj nextState = new DMatrixRMaj(state.numRows, 1);
- double q = state.get(0, 0);
- double qDot = state.get(1, 0);
-
- // Discretized with Forward Euler
- nextState.set(0, 0, q + dt * qDot);
- nextState.set(1, 0, qDot - dt * (b * qDot + m * g * l * Math.sin(q)) / I);
-
- return nextState;
- }
-
- private static DMatrixRMaj getMeasurement(DMatrixRMaj state)
- {
- DMatrixRMaj measurement = new DMatrixRMaj(PendulumExtendedKalmanFilter.measurementSize, 1);
- double q = state.get(0, 0);
- double qDot = state.get(1, 0);
-
- // Measurement is the total energy in the system, which will be dissipated with nonzero damping.
- measurement.set(0, 0, 0.5 * I * qDot * qDot + m * g * l * (1 - Math.cos(q)));
-
- return measurement;
- }
-
private static final int ITERATIONS = 500;
public static void main(String[] args)
{
Random random = new Random(45);
+ // Filter
+ ExamplePendulumExtendedKalmanFilter kalmanFilter = new ExamplePendulumExtendedKalmanFilter();
+ kalmanFilter.setModelProperties(m, I, l, b, g, dt);
+ DMatrixRMaj estimatedState = new DMatrixRMaj(ExamplePendulumExtendedKalmanFilter.stateSize, 1);
+
// We'll set things up such that the real system is always less noisy than the filter. Said the other way, our filter design
// will assume that the system is more noisy than it actually is -- a good thing.
- DMatrixRMaj Q = new DMatrixRMaj(PendulumExtendedKalmanFilter.Q);
+ DMatrixRMaj Q = new DMatrixRMaj(kalmanFilter.getQ());
CommonOps_DDRM.scale(0.1, Q);
// We'll set things up such that the real system is always less noisy than the filter. Said the other way, our filter design
// will assume that the system is more noisy than it actually is -- a good thing.
- DMatrixRMaj R = new DMatrixRMaj(PendulumExtendedKalmanFilter.R);
+ DMatrixRMaj R = new DMatrixRMaj(kalmanFilter.getR());
CommonOps_DDRM.scale(0.01, R);
// In general, initial state of system is different from that of filter.
@@ -172,14 +98,11 @@ public static void main(String[] args)
// NonlinearSystem system = new ConstantVelocity2DNonlinearMeasurementSystem(initialSystemState,
// PendulumExtendedKalmanFilter.F,
// PendulumExtendedKalmanFilter.H);
- NonlinearSystem system = new PendulumSystem(initialSystemState, PendulumExtendedKalmanFilter.F, Q, PendulumExtendedKalmanFilter.H, R, random);
-
- DMatrixRMaj state = new DMatrixRMaj(PendulumExtendedKalmanFilter.stateSize, 1);
- DMatrixRMaj measurement = new DMatrixRMaj(PendulumExtendedKalmanFilter.measurementSize, 1);
+ NonlinearSystem system = new PendulumSystem(initialSystemState, kalmanFilter.getF(), Q, kalmanFilter.getH(), R, random,
+ kalmanFilter::getNextState, kalmanFilter::getMeasurement);
- // Filter
- PendulumExtendedKalmanFilter kalmanFilter = new PendulumExtendedKalmanFilter();
- DMatrixRMaj estimatedState = new DMatrixRMaj(PendulumExtendedKalmanFilter.stateSize, 1);
+ DMatrixRMaj state = new DMatrixRMaj(ExamplePendulumExtendedKalmanFilter.stateSize, 1);
+ DMatrixRMaj measurement = new DMatrixRMaj(ExamplePendulumExtendedKalmanFilter.measurementSize, 1);
// Arrays for recording data that we'll plot
double[] timestamps = new double[ITERATIONS];
From b37517df6dbc4d8e2a6f53d2184a6afeabdbc04d Mon Sep 17 00:00:00 2001
From: Robert
Date: Wed, 27 Aug 2025 17:05:26 -0500
Subject: [PATCH 02/18] started adding an odometry extended kalman filter
---
.../ExtendedKalmanFilter.java | 23 +-
.../DRCKinematicsBasedStateEstimator.java | 11 +
.../odomEKF/OdometryKalmanFilter.java | 573 ++++++++++++++++++
.../odomEKF/OdometryKalmanFilterTest.java | 57 ++
4 files changed, 663 insertions(+), 1 deletion(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
create mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index 83f357673a45..f6a7d070520c 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -26,7 +26,7 @@
* For example implementations, see:
*
* - {@link us.ihmc.parameterEstimation.examples.ExamplePendulumExtendedKalmanFilter}
- * - {@link us.ihmc.parameterEstimation.examples.ExamplePendulumExtendedKalmanFilter}
+ * - {@link us.ihmc.parameterEstimation.examples.ExampleConstantVelocity2DKalmanFilter}
*
*
*
@@ -144,8 +144,14 @@ public abstract class ExtendedKalmanFilter
private final DMatrixRMaj josephGainTerm;
private final DMatrixRMaj josephGainTermContainer;
+ private final int stateSize;
+ private final int measurementSize;
+
public ExtendedKalmanFilter(int stateSize, int measurementSize)
{
+ this.stateSize = stateSize;
+ this.measurementSize = measurementSize;
+
processCovariance = new DMatrixRMaj(stateSize, stateSize);
measurementCovariance = new DMatrixRMaj(measurementSize, measurementSize);
@@ -204,6 +210,16 @@ public ExtendedKalmanFilter(DMatrixRMaj initialState, DMatrixRMaj initialCovaria
covariance.set(initialCovariance);
}
+ public int getStateSize()
+ {
+ return stateSize;
+ }
+
+ public int getMeasurementSize()
+ {
+ return measurementSize;
+ }
+
/**
* Runs the filter for a single time step.
*
@@ -440,6 +456,11 @@ public double getNormalizedInnovation()
return normalizedInnovation.getData()[0];
}
+ public DMatrixRMaj getCovariance()
+ {
+ return covariance;
+ }
+
public void setNormalizedInnovationThreshold(double normalizedInnovationThreshold)
{
this.normalizedInnovationThreshold = normalizedInnovationThreshold;
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
index cc9fb8862086..68792aaba966 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
@@ -39,6 +39,7 @@
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.centerOfMassEstimator.MomentumStateUpdater;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.centerOfMassEstimator.SimpleMomentumStateUpdater;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.centerOfMassEstimator.WrenchBasedMomentumStateUpdater;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePose3D;
import us.ihmc.yoVariables.parameters.BooleanParameter;
import us.ihmc.yoVariables.providers.BooleanProvider;
@@ -65,6 +66,7 @@ public class DRCKinematicsBasedStateEstimator implements StateEstimatorControlle
private final MomentumStateUpdater momentumStateUpdater;
private final IMUBiasStateEstimator imuBiasStateEstimator;
private final IMUYawDriftEstimator imuYawDriftEstimator;
+ private final OdometryKalmanFilter odometry;
private final PelvisPoseHistoryCorrectionInterface pelvisPoseHistoryCorrection;
@@ -107,6 +109,7 @@ public DRCKinematicsBasedStateEstimator(FullInverseDynamicsStructure inverseDyna
rootJoint = inverseDynamicsStructure.getRootJoint();
+
usePelvisCorrector = new YoBoolean("useExternalPelvisCorrector", registry);
usePelvisCorrector.set(true);
@@ -144,6 +147,7 @@ public DRCKinematicsBasedStateEstimator(FullInverseDynamicsStructure inverseDyna
List imusToUse = new ArrayList<>(imuProcessedOutputs);
+
BooleanProvider cancelGravityFromAccelerationMeasurement = new BooleanParameter("cancelGravityFromAccelerationMeasurement",
registry,
stateEstimatorParameters.cancelGravityFromAccelerationMeasurement());
@@ -162,6 +166,10 @@ public DRCKinematicsBasedStateEstimator(FullInverseDynamicsStructure inverseDyna
stateEstimatorParameters,
registry);
+
+ List feetIMUs = imusToUse.stream().filter(sensor -> feet.containsKey(sensor.getMeasurementLink())).toList();
+ odometry = new OdometryKalmanFilter(imusToUse.get(0), feetIMUs, imuBiasStateEstimator, cancelGravityFromAccelerationMeasurement, estimatorDT, gravitationalAcceleration, registry);
+
jointStateUpdater = new JointStateUpdater(inverseDynamicsStructure, sensorOutputMap, stateEstimatorParameters, registry);
if (forceSensorDataHolderToUpdate != null)
@@ -336,6 +344,9 @@ public void doControl()
for (int i = 0; i < footSwitchList.size(); i++)
footSwitchList.get(i).update();
+ // TODO should this also consume some of the foot switch data?
+ odometry.compute();
+
switch (operatingMode.getEnumValue())
{
case FROZEN:
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
new file mode 100644
index 000000000000..d6449feae4f0
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -0,0 +1,573 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import us.ihmc.euclid.referenceFrame.FramePoint3D;
+import us.ihmc.euclid.referenceFrame.FramePose3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
+import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
+import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
+import us.ihmc.mecano.spatial.Twist;
+import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePose3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.providers.BooleanProvider;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The process model in this class is defined as
+ */
+public class OdometryKalmanFilter extends ExtendedKalmanFilter
+{
+ // Constants and providers
+ private final BooleanProvider cancelGravityFromAccelerationMeasurement;
+
+ private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+
+ private final YoRegistry registry = new YoRegistry(getClass().getSimpleName());
+
+ private final IMUSensorReadOnly baseIMU;
+ private final List extends IMUSensorReadOnly> feetIMUs;
+ private final IMUBiasProvider imuBiasProvider;
+
+ private final double estimatorDT;
+ private final FrameVector3DReadOnly gravityVector;
+
+ // Internal variables for state holding
+ private final YoFrameVector3D yoBaseLinearAccelerationMeasurementInWorld;
+ private final YoFrameVector3D yoBaseLinearAccelerationMeasurement;
+ private final YoFrameVector3D yoBaseAngularVelocityMeasurement;
+
+ private final YoFrameVector3D yoRootJointAngularVelocityMeasurementInWorld;
+ private final YoFrameVector3D yoRootJointAngularVelocityMeasurement;
+
+ private final List yoFootLinearAccelerationMeasurementInWorld = new ArrayList<>();
+ private final List yoFootLinearAccelerationMeasurement = new ArrayList<>();
+
+ // Outputs
+ private final DMatrixRMaj predictedState;
+ private final DMatrixRMaj predictedMeasurement;
+
+ private final DMatrixRMaj AMatrix;
+ private final DMatrixRMaj CMatrix;
+
+ private final YoFrameQuaternion predictedBaseOrientation;
+ private final YoFramePoint3D predictedBaseTranslation;
+ private final YoFrameVector3D predictedBaseLinearVelocity;
+
+ private final YoFrameQuaternion filteredBaseOrientation;
+ private final YoFramePoint3D filteredBaseTranslation;
+ private final YoFrameVector3D filteredBaseLinearVelocity;
+
+ private final YoFramePose3D estimatedRootJointPose;
+ private final YoFrameVector3D estimatedRootJointLinearVelocity;
+ private final YoFrameVector3D estimatedRootJointAngularVelocity;
+
+ private final List predictedFootTranslations = new ArrayList<>();
+ private final List predictedFootLinearVelocities = new ArrayList<>();
+
+ private final RigidBodyTransformReadOnly transformToRootJoint;
+
+ private final List predictedRelativeFootPositionMeasurements = new ArrayList<>();
+ private final List predictedRelativeFootVelocityMeasurements = new ArrayList<>();
+ private final List predictedWorldFootVelocityMeasurements = new ArrayList<>();
+
+ // Temporary variables
+ private final FrameVector3D linearAcceleration = new FrameVector3D();
+ private final FrameVector3D linearVelocity = new FrameVector3D();
+ private final Vector3D deltaVector = new Vector3D();
+ private final Quaternion estimatedRootJointRotation = new Quaternion();
+ private final DMatrixRMaj rotation3x3 = new DMatrixRMaj(3, 3);
+
+ private final DMatrixRMaj predictedBaseIMUPositionState = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj predictedBaseIMUVelocityState = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj predictedFootPositionState = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj predictedFootVelocityState = new DMatrixRMaj(3, 1);
+
+ private final DMatrixRMaj offsetVector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj footCovariance = new DMatrixRMaj(3, 3);
+
+ private final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
+ private final Twist twist = new Twist();
+
+ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
+ List extends IMUSensorReadOnly> feetIMUs,
+ IMUBiasProvider baseImuBiasProvider,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
+ double estimatorDT,
+ double gravitationalAcceleration,
+ YoRegistry parentRegistry)
+ {
+ super(10 + feetIMUs.size() * 6, 9 * feetIMUs.size());
+
+ this.baseIMU = baseIMU;
+ this.feetIMUs = feetIMUs;
+ this.imuBiasProvider = baseImuBiasProvider;
+ this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
+ this.estimatorDT = estimatorDT;
+
+ predictedState = new DMatrixRMaj(getStateSize(), 1);
+ predictedMeasurement = new DMatrixRMaj(getMeasurementSize(), 1);
+ AMatrix = new DMatrixRMaj(getStateSize(), getStateSize());
+ CMatrix = new DMatrixRMaj(getMeasurementSize(), getStateSize());
+
+ gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
+
+ predictedBaseOrientation = new YoFrameQuaternion("predictedBaseOrientation", worldFrame, registry);
+ predictedBaseTranslation = new YoFramePoint3D("predictedBaseTranslation", worldFrame, registry);
+ predictedBaseLinearVelocity = new YoFrameVector3D("predictedBaseLinearVelocity", worldFrame, registry);
+
+ filteredBaseOrientation = new YoFrameQuaternion("filteredBaseOrientation", worldFrame, registry);
+ filteredBaseTranslation = new YoFramePoint3D("filteredBaseTranslation", worldFrame, registry);
+ filteredBaseLinearVelocity = new YoFrameVector3D("filteredBaseLinearVelocity", worldFrame, registry);
+
+ estimatedRootJointPose = new YoFramePose3D("estimatedRootJointPose", worldFrame, registry);
+ estimatedRootJointLinearVelocity = new YoFrameVector3D("estimatedRootJointLinearVelocity", worldFrame, registry);
+ estimatedRootJointAngularVelocity = new YoFrameVector3D("estimatedRootJointAngularVelocity", worldFrame, registry);
+
+ transformToRootJoint = baseIMU.getMeasurementFrame().getTransformToDesiredFrame(baseIMU.getMeasurementLink().getParentJoint().getFrameAfterJoint());
+
+ yoBaseLinearAccelerationMeasurement = new YoFrameVector3D("baseImuLinearAcceleration", baseIMU.getMeasurementFrame(), registry);
+ yoBaseLinearAccelerationMeasurementInWorld = new YoFrameVector3D("baseImuLinearAccelerationInWorld", worldFrame, registry);
+ yoBaseAngularVelocityMeasurement = new YoFrameVector3D("baseImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
+ yoRootJointAngularVelocityMeasurement = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
+ yoRootJointAngularVelocityMeasurementInWorld = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", worldFrame, registry);
+
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ IMUSensorReadOnly footIMU = feetIMUs.get(i);
+ YoFrameVector3D footLinearAccelerationMeasurement = new YoFrameVector3D("foot" + i + "ImuLinearAcceleration", footIMU.getMeasurementFrame(), registry);
+ YoFrameVector3D footLinearAccelerationMeasurementInWorld = new YoFrameVector3D("foot" + i + "ImuLinearAccelerationInWorld", worldFrame, registry);
+ yoFootLinearAccelerationMeasurement.add(footLinearAccelerationMeasurement);
+ yoFootLinearAccelerationMeasurementInWorld.add(footLinearAccelerationMeasurementInWorld);
+
+ predictedFootTranslations.add(new YoFramePoint3D("predictedFoot" + i + "Translation", worldFrame, registry));
+ predictedFootLinearVelocities.add(new YoFrameVector3D("predictedFoot" + i + "LinearVelocity", worldFrame, registry));
+
+ predictedRelativeFootPositionMeasurements.add(new YoFramePoint3D("predictedRelativeFoot" + i + "PositionMeasurement", baseIMU.getMeasurementFrame(), registry));
+ predictedRelativeFootVelocityMeasurements.add(new YoFrameVector3D("predictedRelativeFoot" + i + "VelocityMeasurement", baseIMU.getMeasurementFrame(), registry));
+ predictedWorldFootVelocityMeasurements.add(new YoFrameVector3D("predictedWorldFoot" + i + "VelocityMeasurement", worldFrame, registry));
+ }
+
+ parentRegistry.addChild(registry);
+ }
+
+ public void compute()
+ {
+ updateMeasures();
+ updateRootJointTwistAngularPart();
+
+ // FIXME populate the observations, which are
+ // FIXME 1. the relative distance between the IMU and the foot in the IMU frame
+ // FIXME 2. the relative linear velocity between the IMU and the foot in the IMU frame
+ // FIXME 3. the absolute linear velocity of the foot
+
+ DMatrixRMaj stateEstimate = calculateEstimate();
+
+ filteredBaseTranslation.set(stateEstimate);
+ filteredBaseLinearVelocity.set(3, stateEstimate);
+ filteredBaseOrientation.set(6, stateEstimate);
+
+ // Transform the pose of the IMU in the world frame to the pose of the root joint in the world frame
+ tempPose.set(filteredBaseOrientation, filteredBaseTranslation);
+ tempPose.appendTransform(transformToRootJoint);
+ estimatedRootJointPose.set(tempPose);
+ estimatedRootJointAngularVelocity.set(yoRootJointAngularVelocityMeasurementInWorld);
+
+ updateRootJointTwistLinearPart(filteredBaseLinearVelocity,
+ estimatedRootJointAngularVelocity,
+ estimatedRootJointLinearVelocity);
+ }
+
+ private final FramePose3D tempPose = new FramePose3D();
+
+
+ public void updateMeasures()
+ {
+ // TODO re-enable this
+// if (!isEstimationEnabled())
+// return;
+
+ FrameVector3DReadOnly biasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(baseIMU);
+ Vector3DReadOnly rawInput = baseIMU.getLinearAccelerationMeasurement();
+
+ linearAcceleration.setReferenceFrame(baseIMU.getMeasurementFrame());
+ linearAcceleration.sub(rawInput, biasInput);
+
+ // Update acceleration in world (minus gravity)
+ if (cancelGravityFromAccelerationMeasurement.getValue())
+ {
+ // FIXME use the estimated orientation of the base instead of the measured one.
+ linearAcceleration.changeFrame(worldFrame);
+ linearAcceleration.add(gravityVector);
+ }
+
+ yoBaseLinearAccelerationMeasurementInWorld.setMatchingFrame(linearAcceleration);
+ yoBaseLinearAccelerationMeasurement.setMatchingFrame(linearAcceleration);
+
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ IMUSensorReadOnly footIMU = feetIMUs.get(i);
+ FrameVector3DReadOnly footBiasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(footIMU);
+ Vector3DReadOnly footRawInput = footIMU.getLinearAccelerationMeasurement();
+
+ linearAcceleration.setReferenceFrame(footIMU.getMeasurementFrame());
+ linearAcceleration.sub(footRawInput, footBiasInput);
+
+ // Update acceleration in world (minus gravity)
+ if (cancelGravityFromAccelerationMeasurement.getValue())
+ {
+ // FIXME use the estimated orientation of the base instead of the measured one.
+ linearAcceleration.changeFrame(worldFrame);
+ linearAcceleration.add(gravityVector);
+ }
+
+ yoFootLinearAccelerationMeasurementInWorld.get(i).setMatchingFrame(linearAcceleration);
+ yoFootLinearAccelerationMeasurement.get(i).setMatchingFrame(linearAcceleration);
+ }
+ }
+
+ private final Vector3D angularVelocityMeasurement = new Vector3D();
+
+ /** Angular velocity of the measurement link, with respect to world. */
+ private final FrameVector3D angularVelocityMeasurementLinkRelativeToWorld = new FrameVector3D();
+
+ /** Angular velocity of the estimation link, with respect to the measurement link. */
+ private final FrameVector3D angularVelocityRootJointFrameRelativeToMeasurementLink = new FrameVector3D();
+
+ /** Twist of the estimation link, with respect to the measurement link. */
+ private final Twist twistRootJointFrameRelativeToMeasurementLink = new Twist();
+
+ private void updateRootJointTwistAngularPart()
+ {
+ RigidBodyBasics measurementLink = baseIMU.getMeasurementLink();
+ FloatingJointBasics rootJoint = (FloatingJointBasics) baseIMU.getMeasurementLink().getParentJoint();
+ ReferenceFrame rootJointFrame = rootJoint.getFrameAfterJoint();
+ // T_{rootBody}^{rootBody, measurementLink}
+ rootJoint.getSuccessor().getBodyFixedFrame().getTwistRelativeToOther(measurementLink.getBodyFixedFrame(), twistRootJointFrameRelativeToMeasurementLink);
+ // T_{rootBody}^{rootJointFrame, measurementLink}
+ twistRootJointFrameRelativeToMeasurementLink.changeFrame(rootJointFrame);
+ // T_{rootJointFrame}^{rootJointFrame, measurementLink}
+ twistRootJointFrameRelativeToMeasurementLink.setBodyFrame(rootJointFrame);
+
+ // omega_{rootJointFrame}^{rootJointFrame, measurementLink}
+ angularVelocityRootJointFrameRelativeToMeasurementLink.setIncludingFrame(twistRootJointFrameRelativeToMeasurementLink.getAngularPart());
+
+ // omega_{measurementLink}^{measurementFrame, world}
+ angularVelocityMeasurement.set(baseIMU.getAngularVelocityMeasurement());
+ if (imuBiasProvider != null)
+ {
+ FrameVector3DReadOnly angularVelocityBiasInIMUFrame = imuBiasProvider.getAngularVelocityBiasInIMUFrame(baseIMU);
+ if (angularVelocityBiasInIMUFrame != null)
+ angularVelocityMeasurement.sub(angularVelocityBiasInIMUFrame);
+ }
+ angularVelocityMeasurementLinkRelativeToWorld.setIncludingFrame(baseIMU.getMeasurementFrame(), angularVelocityMeasurement);
+
+ // omega_{measurementLink}^{rootJointFrame, world}
+ angularVelocityMeasurementLinkRelativeToWorld.changeFrame(rootJointFrame);
+
+ // omega_{rootJointFrame}^{rootJointFrame, world} = omega_{rootJointFrame}^{rootJointFrame, measurementLink} + omega_{measurementLink}^{rootJointFrame, world}
+ rootJoint.getJointTwist().getAngularPart().add(angularVelocityRootJointFrameRelativeToMeasurementLink, angularVelocityMeasurementLinkRelativeToWorld);
+ rootJoint.updateFrame();
+
+ yoRootJointAngularVelocityMeasurement.setMatchingFrame(rootJoint.getJointTwist().getAngularPart());
+ yoRootJointAngularVelocityMeasurementInWorld.setMatchingFrame(rootJoint.getJointTwist().getAngularPart());
+ yoBaseAngularVelocityMeasurement.setMatchingFrame(angularVelocityMeasurementLinkRelativeToWorld);
+ }
+
+ private final FramePoint3D imuToRoot = new FramePoint3D();
+ private final FrameVector3D addedAngular = new FrameVector3D();
+ private final FrameVector3D momentArm = new FrameVector3D();
+
+ private void updateRootJointTwistLinearPart(FrameVector3DReadOnly imuLinearVelocity,
+ FrameVector3DReadOnly imuAngularVelocity,
+ FixedFrameVector3DBasics rootLinearVelocityToPack)
+ {
+ FloatingJointBasics rootJoint = (FloatingJointBasics) baseIMU.getMeasurementLink().getParentJoint();
+ ReferenceFrame rootJointFrame = rootJoint.getFrameAfterJoint();
+
+ imuToRoot.setToZero(baseIMU.getMeasurementFrame());
+ imuToRoot.changeFrame(rootJointFrame);
+
+ momentArm.setIncludingFrame(imuToRoot);
+ momentArm.changeFrame(worldFrame);
+
+ addedAngular.cross(momentArm, imuAngularVelocity);
+
+ rootLinearVelocityToPack.set(imuLinearVelocity);
+ rootLinearVelocityToPack.add(addedAngular);
+ }
+
+ @Override
+ public DMatrixRMaj processModel(DMatrixRMaj state)
+ {
+ predictedState.set(state);
+ estimatedRootJointRotation.set(6, state);
+
+ // Compute the predicted position state
+ // p_k+1 = p_k + v_k * dt
+ MatrixTools.setMatrixBlock(predictedBaseIMUPositionState, 0, 0, state, 0, 0, 3, 1, 1.0);
+ MatrixTools.addMatrixBlock(predictedBaseIMUPositionState, 0, 0, state, 3, 0, 3, 1, estimatorDT );
+ predictedBaseTranslation.set(predictedBaseIMUPositionState);
+
+ // Compute the predicted velocity state
+ // v_k+1 = v_k + a_k * dt
+ // get the acceleration of the base IMU in the base frame, but using the estimated orientation that is internal to the filter.
+ linearAcceleration.setIncludingFrame(yoBaseLinearAccelerationMeasurement);
+ estimatedRootJointRotation.transform(linearAcceleration); // TODO make sure this isn't an inverse transform
+ // figure out the change in velocity based on integrating this acceleration
+ linearAcceleration.get(predictedBaseIMUVelocityState);
+ CommonOps_DDRM.scale(estimatorDT, predictedBaseIMUVelocityState);
+ // add to the velocity the current velocity estimate
+ MatrixTools.addMatrixBlock(predictedBaseIMUVelocityState, 0, 0, state, 3, 0, 3, 1, 1.0);
+ predictedBaseLinearVelocity.set(predictedBaseIMUVelocityState);
+
+ // Compute the predicted orientation state by integrating the angular velocity measurement
+ deltaVector.setAndScale(estimatorDT, baseIMU.getAngularVelocityMeasurement());
+ estimatedRootJointRotation.transform(deltaVector);
+ predictedBaseOrientation.set(estimatedRootJointRotation);
+ predictedBaseOrientation.append(estimatedRootJointRotation);
+
+ // Pack everything into the predicted state vector
+ predictedBaseTranslation.get(predictedState);
+ predictedBaseLinearVelocity.get(3, predictedState);
+ predictedBaseOrientation.get(6, predictedState);
+
+ // Compute the predicted foot position and velocity states
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ int offset = 10 + i * 6;
+ // Compute the predicted foot position state
+ // p_k+1 = p_k + v_k * dt
+ MatrixTools.setMatrixBlock(predictedFootPositionState, 0, 0, state, offset, 0, 3 , 1, 1.0);
+ MatrixTools.addMatrixBlock(predictedFootPositionState, 0, 0, state, offset + 3, 0, 3, 1, estimatorDT);
+ predictedFootTranslations.get(i).set(predictedFootPositionState);
+
+ // Compute the predicted foot velocity state
+ // v_k+1 = v_k + a_k * dt
+ // First, get the acceleration of the foot IMU in the base IMU frame, which we're considering here as the root.
+ linearAcceleration.setIncludingFrame(yoFootLinearAccelerationMeasurement.get(i));
+ linearAcceleration.changeFrame(baseIMU.getMeasurementFrame());
+ // Now, get the acceleration in the estimated world frame
+ estimatedRootJointRotation.transform(linearAcceleration); // TODO make sure this isn't an inverse transform
+ linearAcceleration.get(predictedFootVelocityState);
+ CommonOps_DDRM.scale(estimatorDT, predictedFootVelocityState);
+ // add to the velocity the current velocity estimate
+ MatrixTools.addMatrixBlock(predictedFootVelocityState, 0, 0, state, offset + 3, 0, 3, 1, 1.0);
+ predictedFootLinearVelocities.get(i).set(predictedFootVelocityState);
+
+ // Pack into the prediction vector.
+ predictedFootTranslations.get(i).get(offset, predictedState);
+ predictedFootLinearVelocities.get(i).get(offset + 3, predictedState);
+ }
+
+ // return the predicted state
+ return predictedState;
+ }
+
+ @Override
+ public DMatrixRMaj measurementModel(DMatrixRMaj state)
+ {
+ estimatedRootJointRotation.set(6, state);
+
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ int measurementOffset = 9 * i;
+ int footStateOffset = 10 + i * 6;
+
+ // Compute the predicted foot relative position measurement
+ MatrixTools.setMatrixBlock(offsetVector, 0, 0, state, 0, 0, 3 , 1, 1.0);
+ MatrixTools.addMatrixBlock(offsetVector, 0, 0, state, footStateOffset, 0, 3 , 1, 1.0);
+
+ predictedRelativeFootPositionMeasurements.get(i).set(offsetVector);
+ estimatedRootJointRotation.inverseTransform(predictedRelativeFootPositionMeasurements.get(i)); // TODO make sure this isn't a transform
+
+ // Compute the predicted foot relative velocity measurement
+ MatrixTools.setMatrixBlock(offsetVector, 0, 0, state, 3, 0, 3 , 1, 1.0);
+ MatrixTools.addMatrixBlock(offsetVector, 0, 0, state, footStateOffset + 3, 0, 3 , 1, 1.0);
+
+ predictedRelativeFootVelocityMeasurements.get(i).set(offsetVector);
+ estimatedRootJointRotation.inverseTransform(predictedRelativeFootVelocityMeasurements.get(i)); // TODO make sure this isn't a transform
+
+ // If the foot is in contact, set the velocity to zero, otherwise use the previous prediction state
+ if (isFootInContact(i, state))
+ {
+ predictedWorldFootVelocityMeasurements.get(i).setToZero();
+ }
+ else
+ {
+ predictedWorldFootVelocityMeasurements.get(i).set(footStateOffset + 3, state);
+ }
+
+ predictedRelativeFootPositionMeasurements.get(i).get(measurementOffset, predictedMeasurement);
+ predictedRelativeFootVelocityMeasurements.get(i).get(measurementOffset + 3, predictedMeasurement);
+ predictedWorldFootVelocityMeasurements.get(i).get(measurementOffset + 6, predictedMeasurement);
+ }
+
+ // return the predicted measurement
+ return predictedMeasurement;
+ }
+
+ private final DMatrixRMaj temp = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempScalar = new DMatrixRMaj(1, 1);
+ private boolean isFootInContact(int footIndex, DMatrixRMaj state)
+ {
+ int startRow = 10 + footIndex * 6;
+ CommonOps_DDRM.extract(state, startRow, startRow + 3, 0, 1, offsetVector, 0, 0);
+ CommonOps_DDRM.extract(getCovariance(), startRow, startRow + 3, startRow, startRow + 3, footCovariance, 0, 0);
+
+ CommonOps_DDRM.mult(footCovariance, offsetVector, temp);
+ CommonOps_DDRM.multTransA(offsetVector, temp, tempScalar);
+
+ // tests the mahalonobis distance of the foot velocity being below a certain threshold.
+ return tempScalar.get(0, 0) < 0.2;
+ }
+
+ @Override
+ protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
+ {
+ // Populate the gradient of the base position w.r.t. the state
+ CommonOps_DDRM.insert(eye3x3, AMatrix, 0, 0);
+ MatrixTools.setMatrixBlock(AMatrix, 0, 3, eye3x3, 0, 0, 3, 3, estimatorDT);
+
+ // Populate the gradient of the base velocity w.r.t. the state
+ CommonOps_DDRM.insert(eye3x3, AMatrix, 3, 3);
+ // FIXME add the effect of the gradient of the base rotation
+
+ // FIXME do the gradient crap for the base orientation, which is going to be frustrating
+
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ int stateOffset = 10 + i * 6;
+ // Populate the gradient of the foot position w.r.t. the state
+ CommonOps_DDRM.insert(eye3x3, AMatrix, stateOffset, stateOffset);
+ MatrixTools.setMatrixBlock(AMatrix, stateOffset, stateOffset + 3, eye3x3, 0, 0, 3, 3, estimatorDT);
+
+ // Populate the gradient of the foot position w.r.t. the state
+ // FIXME add the effect of the gradient of the base rotation
+ CommonOps_DDRM.insert(eye3x3, AMatrix, stateOffset + 3, stateOffset + 3);
+ }
+
+ return AMatrix;
+ }
+
+ @Override
+ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
+ {
+ estimatedRootJointRotation.set(6, predictedState);
+ toRotationMatrix(estimatedRootJointRotation, rotation3x3);
+
+ CommonOps_DDRM.transpose(rotation3x3);
+
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ int footStateOffset = 10 + i * 6;
+ int measureOffset = 9 * i;
+ // R^T * (s_k - p_k)
+ CommonOps_DDRM.insert(rotation3x3, CMatrix, measureOffset, footStateOffset);
+ MatrixTools.setMatrixBlock(CMatrix, measureOffset, 0, rotation3x3, 0, 0, 3, 3, -1.0);
+
+ MatrixTools.setMatrixBlock(offsetVector, 0, 0, predictedState, footStateOffset, 0, 3, 1, 1.0);
+ MatrixTools.addMatrixBlock(offsetVector, 0, 0, predictedState, 0, 0, 3, 1, -1.0);
+
+ // TODO add the effect of the gradient of the base rotation
+
+ // R^T * (v_k - sDot_k)
+ CommonOps_DDRM.insert(rotation3x3, CMatrix, measureOffset + 3, 3);
+ MatrixTools.setMatrixBlock(CMatrix, measureOffset + 3, footStateOffset + 3, rotation3x3, 0, 0, 3, 3, -1.0);
+ // TODO add the effect of the gradient of the base rotation
+
+ MatrixTools.setMatrixBlock(offsetVector, 0, 0, predictedState, 3, 0, 3, 1, 1.0);
+ MatrixTools.addMatrixBlock(offsetVector, 0, 0, predictedState, footStateOffset + 3, 0, 3, 1, -1.0);
+
+ // sDot_k
+ CommonOps_DDRM.insert(eye3x3, CMatrix, measureOffset + 6, footStateOffset + 3);
+ }
+
+ return CMatrix;
+ }
+
+ static void toRotationMatrix(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
+ {
+ toRotationMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
+ }
+
+ static void toRotationMatrix(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
+ {
+ double qx2 = qx * qx;
+ double qy2 = qy * qy;
+ double qz2 = qz * qz;
+ double qs2 = qs * qs;
+
+ rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
+ rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
+ rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
+
+ double xy = qx * qy;
+ double xz = qx * qz;
+ double yz = qy * qz;
+ double xw = qx * qs;
+ double yw = qy * qs;
+ double zw = qz * qs;
+
+ rotationMatrix.set(0, 1, 2.0 * (xy - zw));
+ rotationMatrix.set(1, 0, 2.0 * (xy + zw));
+
+ rotationMatrix.set(0, 2, 2.0 * (xz + yw));
+ rotationMatrix.set(2, 0, 2.0 * (xz - yw));
+
+ rotationMatrix.set(1, 2, 2.0 * (yz - xw));
+ rotationMatrix.set(2, 1, 2.0 * (yz + xw));
+ }
+
+ static void toRotationMatrixInverse(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
+ {
+ toRotationMatrixInverse(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
+ }
+
+ static void toRotationMatrixInverse(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
+ {
+ double qx2 = qx * qx;
+ double qy2 = qy * qy;
+ double qz2 = qz * qz;
+ double qs2 = qs * qs;
+
+ rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
+ rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
+ rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
+
+ double xy = qx * qy;
+ double xz = qx * qz;
+ double yz = qy * qz;
+ double xw = qx * qs;
+ double yw = qy * qs;
+ double zw = qz * qs;
+
+ rotationMatrix.set(1, 0, 2.0 * (xy - zw));
+ rotationMatrix.set(0, 1, 2.0 * (xy + zw));
+
+ rotationMatrix.set(2, 0, 2.0 * (xz + yw));
+ rotationMatrix.set(0, 2, 2.0 * (xz - yw));
+
+ rotationMatrix.set(2, 1, 2.0 * (yz - xw));
+ rotationMatrix.set(1, 2, 2.0 * (yz + xw));
+ }
+}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
new file mode 100644
index 000000000000..a53fb87195d4
--- /dev/null
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
@@ -0,0 +1,57 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+
+import org.ejml.EjmlUnitTests;
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import org.junit.jupiter.api.Test;
+import us.ihmc.euclid.matrix.RotationMatrix;
+import us.ihmc.euclid.tools.EuclidCoreRandomTools;
+import us.ihmc.euclid.tuple4D.Quaternion;
+
+import java.util.Random;
+
+public class OdometryKalmanFilterTest
+{
+ @Test
+ public void testToRotationMatrix()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+
+ OdometryKalmanFilter.toRotationMatrix(quaternion, rotationMatrix);
+
+ RotationMatrix mat = new RotationMatrix(quaternion);
+ DMatrixRMaj expected = new DMatrixRMaj(3, 3);
+ mat.get(expected);
+
+ EjmlUnitTests.assertEquals(expected, rotationMatrix, 1e-8);
+ }
+ }
+
+ @Test
+ public void testToRotationMatrixInverse()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
+ DMatrixRMaj rotationMatrixInverse = new DMatrixRMaj(3, 3);
+
+ OdometryKalmanFilter.toRotationMatrixInverse(quaternion, rotationMatrixInverse);
+
+ RotationMatrix mat = new RotationMatrix(quaternion);
+ DMatrixRMaj rotationExpected = new DMatrixRMaj(3, 3);
+ mat.get(rotationExpected);
+
+ CommonOps_DDRM.invert(rotationExpected);
+
+ EjmlUnitTests.assertEquals(rotationExpected, rotationMatrixInverse, 1e-8);
+ }
+ }
+}
From 55ffcf0fa2dd69570324b15631cfe3f7291278f4 Mon Sep 17 00:00:00 2001
From: Robert
Date: Thu, 28 Aug 2025 13:56:57 -0500
Subject: [PATCH 03/18] updated the plant model
---
.../odomEKF/OdometryIndexHelper.java | 126 +++++
.../odomEKF/OdometryKalmanFilter.java | 429 +++++++++++-------
2 files changed, 391 insertions(+), 164 deletions(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
new file mode 100644
index 000000000000..254661f746a3
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
@@ -0,0 +1,126 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import us.ihmc.euclid.tools.EuclidCoreTools;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionBasics;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+
+public class OdometryIndexHelper
+{
+ public static int getStateSizePerLink()
+ {
+ return 16;
+ }
+
+ public static int getStatePositionIndex()
+ {
+ return 0;
+ }
+
+ public static int getStateVelocityIndex()
+ {
+ return 3;
+ }
+
+ public static int getStateOrientationIndex()
+ {
+ return 6;
+ }
+
+ public static int getStateAccelerationBiasIndex()
+ {
+ return 10;
+ }
+
+ public static int getStateGyroBiasIndex()
+ {
+ return 13;
+ }
+
+ public static int getBasePositionIndex()
+ {
+ return getStatePositionIndex();
+ }
+
+ public static int getBaseVelocityIndex()
+ {
+ return getStateVelocityIndex();
+ }
+
+ public static int getBaseOrientationIndex()
+ {
+ return getStateOrientationIndex();
+ }
+
+ public static int getBaseAccelerationBiasIndex()
+ {
+ return getStateAccelerationBiasIndex();
+ }
+
+ public static int getBaseGyroBiasIndex()
+ {
+ return getStateGyroBiasIndex();
+ }
+
+ public static int getFootPositionIndex(int footNumber)
+ {
+ return getStateSizePerLink() * (footNumber + 1) + getStatePositionIndex();
+ }
+
+ public static int getFootVelocityIndex(int footNumber)
+ {
+ return getFootPositionIndex(footNumber) + getStateVelocityIndex();
+ }
+
+ public static int getFootOrientationIndex(int footNumber)
+ {
+ return getFootPositionIndex(footNumber) + getStateOrientationIndex();
+ }
+
+ public static int getFootAccelerationBiasIndex(int footNumber)
+ {
+ return getFootPositionIndex(footNumber) + getStateAccelerationBiasIndex();
+ }
+
+ public static int getFootGyroBiasIndex(int footNumber)
+ {
+ return getFootPositionIndex(footNumber) + getStateGyroBiasIndex();
+ }
+
+ public static void toQuaternionFromRotationVector(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
+ {
+ double magnitude = rotation.norm();
+ double s = Math.sin(magnitude * 0.5);
+ double qs = Math.cos(magnitude * 0.5);
+ double qx = s * rotation.getX() / magnitude;
+ double qy = s * rotation.getY() / magnitude;
+ double qz = s * rotation.getZ() / magnitude;
+ quaternionToPack.set(qx, qy, qz, qs);
+ }
+
+ public static void toQuaternionFromRotationVectorSmallAngle(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
+ {
+ double magnitude = rotation.norm();
+ double s = magnitude * 0.5;
+ double qs = 1.0;
+ double qx = s * rotation.getX() / magnitude;
+ double qy = s * rotation.getY() / magnitude;
+ double qz = s * rotation.getZ() / magnitude;
+ quaternionToPack.set(qx, qy, qz, qs);
+ }
+
+ public static void logMap(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
+ {
+ double norm = EuclidCoreTools.norm(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ double scale = 2.0 * Math.atan2(norm, quaternion.getS()) / norm;
+ rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ rotationToPack.scale(scale);
+ }
+
+ public static void logMapSmallAngle(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
+ {
+ rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ rotationToPack.scale(2.0);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index d6449feae4f0..050c4b9c0ab2 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -7,7 +7,6 @@
import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
-import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
@@ -22,6 +21,8 @@
import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.yoVariables.euclid.YoQuaternion;
+import us.ihmc.yoVariables.euclid.YoVector3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePose3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
@@ -29,6 +30,7 @@
import us.ihmc.yoVariables.providers.BooleanProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
+import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@@ -38,8 +40,6 @@
public class OdometryKalmanFilter extends ExtendedKalmanFilter
{
// Constants and providers
- private final BooleanProvider cancelGravityFromAccelerationMeasurement;
-
private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
private final YoRegistry registry = new YoRegistry(getClass().getSimpleName());
@@ -52,16 +52,11 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final FrameVector3DReadOnly gravityVector;
// Internal variables for state holding
- private final YoFrameVector3D yoBaseLinearAccelerationMeasurementInWorld;
- private final YoFrameVector3D yoBaseLinearAccelerationMeasurement;
private final YoFrameVector3D yoBaseAngularVelocityMeasurement;
private final YoFrameVector3D yoRootJointAngularVelocityMeasurementInWorld;
private final YoFrameVector3D yoRootJointAngularVelocityMeasurement;
- private final List yoFootLinearAccelerationMeasurementInWorld = new ArrayList<>();
- private final List yoFootLinearAccelerationMeasurement = new ArrayList<>();
-
// Outputs
private final DMatrixRMaj predictedState;
private final DMatrixRMaj predictedMeasurement;
@@ -69,9 +64,19 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final DMatrixRMaj AMatrix;
private final DMatrixRMaj CMatrix;
- private final YoFrameQuaternion predictedBaseOrientation;
- private final YoFramePoint3D predictedBaseTranslation;
- private final YoFrameVector3D predictedBaseLinearVelocity;
+ private final MeasuredVariables baseMeasurement;
+ private final List feetMeasurements = new ArrayList<>();
+
+ private final EstimatedVariables baseCurrentState;
+ private final EstimatedVariables basePredictedState;
+ private final List feetCurrentState = new ArrayList<>();
+ private final List feetPredictedState = new ArrayList<>();
+
+ private final List sensorProcess = new ArrayList<>();
+ private final List sensorMeasurements = new ArrayList<>();
+
+ private final EstimatedVariables baseObserveState;
+ private final List feetObservationState = new ArrayList<>();
private final YoFrameQuaternion filteredBaseOrientation;
private final YoFramePoint3D filteredBaseTranslation;
@@ -81,14 +86,8 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final YoFrameVector3D estimatedRootJointLinearVelocity;
private final YoFrameVector3D estimatedRootJointAngularVelocity;
- private final List predictedFootTranslations = new ArrayList<>();
- private final List predictedFootLinearVelocities = new ArrayList<>();
-
private final RigidBodyTransformReadOnly transformToRootJoint;
- private final List predictedRelativeFootPositionMeasurements = new ArrayList<>();
- private final List predictedRelativeFootVelocityMeasurements = new ArrayList<>();
- private final List predictedWorldFootVelocityMeasurements = new ArrayList<>();
// Temporary variables
private final FrameVector3D linearAcceleration = new FrameVector3D();
@@ -97,11 +96,6 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final Quaternion estimatedRootJointRotation = new Quaternion();
private final DMatrixRMaj rotation3x3 = new DMatrixRMaj(3, 3);
- private final DMatrixRMaj predictedBaseIMUPositionState = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj predictedBaseIMUVelocityState = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj predictedFootPositionState = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj predictedFootVelocityState = new DMatrixRMaj(3, 1);
-
private final DMatrixRMaj offsetVector = new DMatrixRMaj(3, 1);
private final DMatrixRMaj footCovariance = new DMatrixRMaj(3, 3);
@@ -131,9 +125,24 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
- predictedBaseOrientation = new YoFrameQuaternion("predictedBaseOrientation", worldFrame, registry);
- predictedBaseTranslation = new YoFramePoint3D("predictedBaseTranslation", worldFrame, registry);
- predictedBaseLinearVelocity = new YoFrameVector3D("predictedBaseLinearVelocity", worldFrame, registry);
+ baseMeasurement = new MeasuredVariables("base", baseIMU, imuBiasProvider, registry);
+ baseCurrentState = new EstimatedVariables("baseCurrent");
+ baseObserveState = new EstimatedVariables("baseObserve");
+ basePredictedState = new EstimatedVariables("basePredicted");
+ sensorProcess.add(new SensorProcess(baseMeasurement, baseCurrentState, basePredictedState, cancelGravityFromAccelerationMeasurement, gravityVector, estimatorDT));
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ MeasuredVariables footMeasurements = new MeasuredVariables("foot" + i, feetIMUs.get(i), imuBiasProvider, registry);
+ EstimatedVariables footCurrentState = new EstimatedVariables("foot" + i + "Current");
+ EstimatedVariables footObservationState = new EstimatedVariables("foot" + i + "Observe");
+ EstimatedVariables footPredictedState = new EstimatedVariables("foot" + i + "Predicted");
+ feetMeasurements.add(footMeasurements);
+ feetObservationState.add(footObservationState);
+ feetCurrentState.add(footCurrentState);
+ feetPredictedState.add(footPredictedState);
+ sensorProcess.add(new SensorProcess(footMeasurements, footCurrentState, footPredictedState, cancelGravityFromAccelerationMeasurement, gravityVector, estimatorDT));
+ sensorMeasurements.add(new SensorMeasurement("foot" + i, baseObserveState, footObservationState, baseIMU, feetIMUs.get(i), registry));
+ }
filteredBaseOrientation = new YoFrameQuaternion("filteredBaseOrientation", worldFrame, registry);
filteredBaseTranslation = new YoFramePoint3D("filteredBaseTranslation", worldFrame, registry);
@@ -145,29 +154,218 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
transformToRootJoint = baseIMU.getMeasurementFrame().getTransformToDesiredFrame(baseIMU.getMeasurementLink().getParentJoint().getFrameAfterJoint());
- yoBaseLinearAccelerationMeasurement = new YoFrameVector3D("baseImuLinearAcceleration", baseIMU.getMeasurementFrame(), registry);
- yoBaseLinearAccelerationMeasurementInWorld = new YoFrameVector3D("baseImuLinearAccelerationInWorld", worldFrame, registry);
yoBaseAngularVelocityMeasurement = new YoFrameVector3D("baseImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
yoRootJointAngularVelocityMeasurement = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
yoRootJointAngularVelocityMeasurementInWorld = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", worldFrame, registry);
- for (int i = 0; i < feetIMUs.size(); i++)
+ parentRegistry.addChild(registry);
+ }
+
+ private static class SensorProcess
+ {
+ // State providers
+ private final MeasuredVariables measuredVariables;
+ private final EstimatedVariables currentState;
+ private final EstimatedVariables predictedState;
+ private final BooleanProvider cancelGravityFromAccelerationMeasurement;
+ private final FrameVector3DReadOnly gravityVector;
+ private final double estimatorDt;
+
+ // Temp variables
+ private final Vector3D unbiasedAcceleration = new Vector3D();
+ private final Vector3D unbiasedAngularVelocity = new Vector3D();
+ private final Vector3D integratedVelocity = new Vector3D();
+ private final Quaternion integratedRotation = new Quaternion();
+
+ public SensorProcess(MeasuredVariables measuredVariables,
+ EstimatedVariables currentState,
+ EstimatedVariables predictedState,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
+ FrameVector3DReadOnly gravityVector,
+ double estimatorDt)
{
- IMUSensorReadOnly footIMU = feetIMUs.get(i);
- YoFrameVector3D footLinearAccelerationMeasurement = new YoFrameVector3D("foot" + i + "ImuLinearAcceleration", footIMU.getMeasurementFrame(), registry);
- YoFrameVector3D footLinearAccelerationMeasurementInWorld = new YoFrameVector3D("foot" + i + "ImuLinearAccelerationInWorld", worldFrame, registry);
- yoFootLinearAccelerationMeasurement.add(footLinearAccelerationMeasurement);
- yoFootLinearAccelerationMeasurementInWorld.add(footLinearAccelerationMeasurementInWorld);
-
- predictedFootTranslations.add(new YoFramePoint3D("predictedFoot" + i + "Translation", worldFrame, registry));
- predictedFootLinearVelocities.add(new YoFrameVector3D("predictedFoot" + i + "LinearVelocity", worldFrame, registry));
-
- predictedRelativeFootPositionMeasurements.add(new YoFramePoint3D("predictedRelativeFoot" + i + "PositionMeasurement", baseIMU.getMeasurementFrame(), registry));
- predictedRelativeFootVelocityMeasurements.add(new YoFrameVector3D("predictedRelativeFoot" + i + "VelocityMeasurement", baseIMU.getMeasurementFrame(), registry));
- predictedWorldFootVelocityMeasurements.add(new YoFrameVector3D("predictedWorldFoot" + i + "VelocityMeasurement", worldFrame, registry));
+ this.measuredVariables = measuredVariables;
+ this.currentState = currentState;
+ this.predictedState = predictedState;
+ this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
+ this.gravityVector = gravityVector;
+ this.estimatorDt = estimatorDt;
}
- parentRegistry.addChild(registry);
+ public void update()
+ {
+ unbiasedAcceleration.sub(measuredVariables.linearAcceleration, currentState.accelBias);
+ unbiasedAngularVelocity.sub(measuredVariables.angularVelocity, currentState.gyroBias);
+ // transform the acceleration to world
+ currentState.orientation.transform(unbiasedAcceleration);
+ if (cancelGravityFromAccelerationMeasurement.getValue())
+ {
+ // remove gravity from acceleration
+ unbiasedAcceleration.sub(gravityVector);
+ }
+
+ // First order integration of the position state
+ predictedState.translation.scaleAdd(estimatorDt, currentState.linearVelocity, currentState.translation);
+
+ // Integration of the velocity state
+ predictedState.linearVelocity.scaleAdd(estimatorDt, unbiasedAcceleration, currentState.linearVelocity);
+
+ // First order integration of the angular state along the SO(3) manifold
+ integratedVelocity.setAndScale(estimatorDt, unbiasedAngularVelocity);
+ OdometryIndexHelper.toQuaternionFromRotationVector(integratedVelocity, integratedRotation);
+ predictedState.orientation.multiply(currentState.orientation, integratedRotation);
+
+ // Propagate the bias
+ predictedState.accelBias.set(currentState.accelBias);
+ predictedState.gyroBias.set(currentState.gyroBias);
+ }
+ }
+
+ private static class SensorMeasurement
+ {
+ private final EstimatedVariables baseObserve;
+ private final EstimatedVariables footObserve;
+ private final IMUSensorReadOnly baseIMU;
+ private final IMUSensorReadOnly footIMU;
+
+ // State variables
+ public final YoVector3D footPredictedRelativePosition;
+ public final YoVector3D footPredictedRelativeOrientation;
+
+ // Temp variables
+ private final FramePose3D footPose = new FramePose3D();
+ private final Quaternion footOrientationError = new Quaternion();
+ private final Vector3D velocityError = new Vector3D();
+
+ public SensorMeasurement(String prefix, EstimatedVariables baseObserve, EstimatedVariables footObserve, IMUSensorReadOnly baseIMU,
+ IMUSensorReadOnly footIMU, YoRegistry registry)
+ {
+ this.baseObserve = baseObserve;
+ this.footObserve = footObserve;
+ this.baseIMU = baseIMU;
+ this.footIMU = footIMU;
+
+ footPredictedRelativePosition = new YoVector3D(prefix + "PredictedRelativePosition", registry);
+ footPredictedRelativeOrientation = new YoVector3D(prefix + "PredictedRelativeOrientation", registry);
+ }
+
+ public void update()
+ {
+ // Update the predicted foot position in world
+ footPredictedRelativePosition.sub(footObserve.translation, baseObserve.translation);
+ baseObserve.orientation.transform(footPredictedRelativePosition);
+
+ // get the foot pose relative to the base link, which should be entirely based on kinematics
+ footPose.setToZero(footIMU.getMeasurementFrame());
+ footPose.changeFrame(baseIMU.getMeasurementFrame());
+
+ // FIXME compute the predicted relative orientation
+ footOrientationError.multiply(footPose.getOrientation().inverse(), baseObserve.orientation.inverse());
+ footOrientationError.multiply(footObserve.orientation);
+ OdometryIndexHelper.logMap(footOrientationError, footPredictedRelativeOrientation);
+
+
+ // Compute
+ // FIXME finish the sensor measurement process
+
+ }
+
+ public void get(int start, DMatrixRMaj measurementToPack)
+ {
+ footPredictedRelativePosition.get(start, measurementToPack);
+ footPredictedRelativeOrientation.get(start + 3, measurementToPack);
+ }
+ }
+
+ private static class MeasuredVariables
+ {
+ // State providers
+ private final IMUSensorReadOnly imu;
+ private final IMUBiasProvider imuBiasProvider;
+
+ // State recorders
+ public final YoFrameVector3D gyroMeasurementInWorld;
+ public final YoFrameVector3D gyroMeasurement;
+ public final YoFrameVector3D linearAccelerationMeasurementInWorld;
+ public final YoFrameVector3D linearAccelerationMeasurement;
+
+ // Temp variables
+ private final FrameVector3D linearAcceleration = new FrameVector3D();
+ private final FrameVector3D angularVelocity = new FrameVector3D();
+
+ public MeasuredVariables(String prefix,
+ IMUSensorReadOnly imu,
+ IMUBiasProvider imuBiasProvider,
+ YoRegistry registry)
+ {
+ this.imu = imu;
+ this.imuBiasProvider = imuBiasProvider;
+
+ gyroMeasurementInWorld = new YoFrameVector3D(prefix + "GyroMeasurementInWorld", worldFrame, registry);
+ gyroMeasurement = new YoFrameVector3D(prefix + "GyroMeasurement", imu.getMeasurementFrame(), registry);
+ linearAccelerationMeasurementInWorld = new YoFrameVector3D(prefix + "LinearAccelerationMeasurementInWorld", worldFrame, registry);
+ linearAccelerationMeasurement = new YoFrameVector3D(prefix + "LinearAccelerationMeasurement", imu.getMeasurementFrame(), registry);
+
+ }
+
+ public void update()
+ {
+ // Update gyro measure
+ FrameVector3DReadOnly gyroBiasInput = imuBiasProvider.getAngularVelocityBiasInIMUFrame(imu);
+ Vector3DReadOnly gyroRawInput = imu.getAngularVelocityMeasurement();
+
+ angularVelocity.setReferenceFrame(imu.getMeasurementFrame());
+ angularVelocity.sub(gyroRawInput, gyroBiasInput);
+
+ gyroMeasurementInWorld.setMatchingFrame(angularVelocity);
+ gyroMeasurement.setMatchingFrame(angularVelocity);
+
+ // Update the accelerometer measure
+ FrameVector3DReadOnly accelBiasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(imu);
+ Vector3DReadOnly accelRawInput = imu.getLinearAccelerationMeasurement();
+
+ linearAcceleration.setReferenceFrame(imu.getMeasurementFrame());
+ linearAcceleration.sub(accelRawInput, accelBiasInput);
+
+ linearAccelerationMeasurementInWorld.setMatchingFrame(linearAcceleration);
+ linearAccelerationMeasurement.setMatchingFrame(linearAcceleration);
+ }
+
+ }
+ private class EstimatedVariables
+ {
+ public final YoFramePoint3D translation;
+ public final YoFrameVector3D linearVelocity;
+ public final YoFrameQuaternion orientation;
+ public final YoFrameVector3D accelBias;
+ public final YoFrameVector3D gyroBias;
+
+ public EstimatedVariables(String prefix)
+ {
+ translation = new YoFramePoint3D(prefix + "Translation", worldFrame, registry);
+ linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
+ orientation = new YoFrameQuaternion(prefix + "Orientation", worldFrame, registry);
+ accelBias = new YoFrameVector3D(prefix + "AccelBias", worldFrame, registry);
+ gyroBias = new YoFrameVector3D(prefix + "GyroBias", worldFrame, registry);
+ }
+
+ public void set(int start, DMatrixRMaj state)
+ {
+ translation.set(start + OdometryIndexHelper.getStatePositionIndex(), state);
+ linearVelocity.set(start + OdometryIndexHelper.getStateVelocityIndex(), state);
+ orientation.set(start + OdometryIndexHelper.getStateOrientationIndex(), state);
+ accelBias.set(start + OdometryIndexHelper.getStateAccelerationBiasIndex(), state);
+ gyroBias.set(start + OdometryIndexHelper.getStateGyroBiasIndex(), state);
+ }
+
+ public void get(int start, DMatrixRMaj stateToPack)
+ {
+ translation.get(start + OdometryIndexHelper.getStatePositionIndex(), stateToPack);
+ linearVelocity.get(start + OdometryIndexHelper.getStateVelocityIndex(), stateToPack);
+ orientation.get(start + OdometryIndexHelper.getStateOrientationIndex(), stateToPack);
+ accelBias.get(start + OdometryIndexHelper.getStateAccelerationBiasIndex(), stateToPack);
+ gyroBias.get(start + OdometryIndexHelper.getStateGyroBiasIndex(), stateToPack);
+ }
}
public void compute()
@@ -206,43 +404,9 @@ public void updateMeasures()
// if (!isEstimationEnabled())
// return;
- FrameVector3DReadOnly biasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(baseIMU);
- Vector3DReadOnly rawInput = baseIMU.getLinearAccelerationMeasurement();
-
- linearAcceleration.setReferenceFrame(baseIMU.getMeasurementFrame());
- linearAcceleration.sub(rawInput, biasInput);
-
- // Update acceleration in world (minus gravity)
- if (cancelGravityFromAccelerationMeasurement.getValue())
- {
- // FIXME use the estimated orientation of the base instead of the measured one.
- linearAcceleration.changeFrame(worldFrame);
- linearAcceleration.add(gravityVector);
- }
-
- yoBaseLinearAccelerationMeasurementInWorld.setMatchingFrame(linearAcceleration);
- yoBaseLinearAccelerationMeasurement.setMatchingFrame(linearAcceleration);
-
- for (int i = 0; i < feetIMUs.size(); i++)
- {
- IMUSensorReadOnly footIMU = feetIMUs.get(i);
- FrameVector3DReadOnly footBiasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(footIMU);
- Vector3DReadOnly footRawInput = footIMU.getLinearAccelerationMeasurement();
-
- linearAcceleration.setReferenceFrame(footIMU.getMeasurementFrame());
- linearAcceleration.sub(footRawInput, footBiasInput);
-
- // Update acceleration in world (minus gravity)
- if (cancelGravityFromAccelerationMeasurement.getValue())
- {
- // FIXME use the estimated orientation of the base instead of the measured one.
- linearAcceleration.changeFrame(worldFrame);
- linearAcceleration.add(gravityVector);
- }
-
- yoFootLinearAccelerationMeasurementInWorld.get(i).setMatchingFrame(linearAcceleration);
- yoFootLinearAccelerationMeasurement.get(i).setMatchingFrame(linearAcceleration);
- }
+ baseMeasurement.update();
+ for (int i = 0; i < feetMeasurements.size(); i++)
+ feetMeasurements.get(i).update();
}
private final Vector3D angularVelocityMeasurement = new Vector3D();
@@ -319,64 +483,22 @@ private void updateRootJointTwistLinearPart(FrameVector3DReadOnly imuLinearVeloc
@Override
public DMatrixRMaj processModel(DMatrixRMaj state)
{
- predictedState.set(state);
- estimatedRootJointRotation.set(6, state);
+ // update the yo variables representing the state
+ baseCurrentState.set(OdometryIndexHelper.getBasePositionIndex(), state);
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ feetCurrentState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
+ }
+
+ // do the prediction
+ for (int i = 0; i < sensorProcess.size(); i++)
+ sensorProcess.get(i).update();
- // Compute the predicted position state
- // p_k+1 = p_k + v_k * dt
- MatrixTools.setMatrixBlock(predictedBaseIMUPositionState, 0, 0, state, 0, 0, 3, 1, 1.0);
- MatrixTools.addMatrixBlock(predictedBaseIMUPositionState, 0, 0, state, 3, 0, 3, 1, estimatorDT );
- predictedBaseTranslation.set(predictedBaseIMUPositionState);
-
- // Compute the predicted velocity state
- // v_k+1 = v_k + a_k * dt
- // get the acceleration of the base IMU in the base frame, but using the estimated orientation that is internal to the filter.
- linearAcceleration.setIncludingFrame(yoBaseLinearAccelerationMeasurement);
- estimatedRootJointRotation.transform(linearAcceleration); // TODO make sure this isn't an inverse transform
- // figure out the change in velocity based on integrating this acceleration
- linearAcceleration.get(predictedBaseIMUVelocityState);
- CommonOps_DDRM.scale(estimatorDT, predictedBaseIMUVelocityState);
- // add to the velocity the current velocity estimate
- MatrixTools.addMatrixBlock(predictedBaseIMUVelocityState, 0, 0, state, 3, 0, 3, 1, 1.0);
- predictedBaseLinearVelocity.set(predictedBaseIMUVelocityState);
-
- // Compute the predicted orientation state by integrating the angular velocity measurement
- deltaVector.setAndScale(estimatorDT, baseIMU.getAngularVelocityMeasurement());
- estimatedRootJointRotation.transform(deltaVector);
- predictedBaseOrientation.set(estimatedRootJointRotation);
- predictedBaseOrientation.append(estimatedRootJointRotation);
-
- // Pack everything into the predicted state vector
- predictedBaseTranslation.get(predictedState);
- predictedBaseLinearVelocity.get(3, predictedState);
- predictedBaseOrientation.get(6, predictedState);
-
- // Compute the predicted foot position and velocity states
+ // update the predicted state from the yo variablized state
+ basePredictedState.get(OdometryIndexHelper.getBasePositionIndex(), state);
for (int i = 0; i < feetIMUs.size(); i++)
{
- int offset = 10 + i * 6;
- // Compute the predicted foot position state
- // p_k+1 = p_k + v_k * dt
- MatrixTools.setMatrixBlock(predictedFootPositionState, 0, 0, state, offset, 0, 3 , 1, 1.0);
- MatrixTools.addMatrixBlock(predictedFootPositionState, 0, 0, state, offset + 3, 0, 3, 1, estimatorDT);
- predictedFootTranslations.get(i).set(predictedFootPositionState);
-
- // Compute the predicted foot velocity state
- // v_k+1 = v_k + a_k * dt
- // First, get the acceleration of the foot IMU in the base IMU frame, which we're considering here as the root.
- linearAcceleration.setIncludingFrame(yoFootLinearAccelerationMeasurement.get(i));
- linearAcceleration.changeFrame(baseIMU.getMeasurementFrame());
- // Now, get the acceleration in the estimated world frame
- estimatedRootJointRotation.transform(linearAcceleration); // TODO make sure this isn't an inverse transform
- linearAcceleration.get(predictedFootVelocityState);
- CommonOps_DDRM.scale(estimatorDT, predictedFootVelocityState);
- // add to the velocity the current velocity estimate
- MatrixTools.addMatrixBlock(predictedFootVelocityState, 0, 0, state, offset + 3, 0, 3, 1, 1.0);
- predictedFootLinearVelocities.get(i).set(predictedFootVelocityState);
-
- // Pack into the prediction vector.
- predictedFootTranslations.get(i).get(offset, predictedState);
- predictedFootLinearVelocities.get(i).get(offset + 3, predictedState);
+ feetPredictedState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), state);
}
// return the predicted state
@@ -386,42 +508,21 @@ public DMatrixRMaj processModel(DMatrixRMaj state)
@Override
public DMatrixRMaj measurementModel(DMatrixRMaj state)
{
- estimatedRootJointRotation.set(6, state);
-
+ // update the yo variables representing the state
+ baseObserveState.set(OdometryIndexHelper.getBasePositionIndex(), state);
for (int i = 0; i < feetIMUs.size(); i++)
{
- int measurementOffset = 9 * i;
- int footStateOffset = 10 + i * 6;
-
- // Compute the predicted foot relative position measurement
- MatrixTools.setMatrixBlock(offsetVector, 0, 0, state, 0, 0, 3 , 1, 1.0);
- MatrixTools.addMatrixBlock(offsetVector, 0, 0, state, footStateOffset, 0, 3 , 1, 1.0);
-
- predictedRelativeFootPositionMeasurements.get(i).set(offsetVector);
- estimatedRootJointRotation.inverseTransform(predictedRelativeFootPositionMeasurements.get(i)); // TODO make sure this isn't a transform
-
- // Compute the predicted foot relative velocity measurement
- MatrixTools.setMatrixBlock(offsetVector, 0, 0, state, 3, 0, 3 , 1, 1.0);
- MatrixTools.addMatrixBlock(offsetVector, 0, 0, state, footStateOffset + 3, 0, 3 , 1, 1.0);
-
- predictedRelativeFootVelocityMeasurements.get(i).set(offsetVector);
- estimatedRootJointRotation.inverseTransform(predictedRelativeFootVelocityMeasurements.get(i)); // TODO make sure this isn't a transform
-
- // If the foot is in contact, set the velocity to zero, otherwise use the previous prediction state
- if (isFootInContact(i, state))
- {
- predictedWorldFootVelocityMeasurements.get(i).setToZero();
- }
- else
- {
- predictedWorldFootVelocityMeasurements.get(i).set(footStateOffset + 3, state);
- }
+ feetObservationState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
+ }
- predictedRelativeFootPositionMeasurements.get(i).get(measurementOffset, predictedMeasurement);
- predictedRelativeFootVelocityMeasurements.get(i).get(measurementOffset + 3, predictedMeasurement);
- predictedWorldFootVelocityMeasurements.get(i).get(measurementOffset + 6, predictedMeasurement);
+ for (int i = 0; i < sensorMeasurements.size(); i++)
+ {
+ sensorMeasurements.get(i).update();
+ sensorMeasurements.get(i).get(i * 10, predictedMeasurement); // FIXME fix this offset
}
+ estimatedRootJointRotation.set(6, state);
+
// return the predicted measurement
return predictedMeasurement;
}
From 1649f3ef7740e1620450d050a249df4494122e67 Mon Sep 17 00:00:00 2001
From: Robert
Date: Thu, 28 Aug 2025 18:00:39 -0500
Subject: [PATCH 04/18] started getting the EKF running
---
.../ExtendedKalmanFilter.java | 3 +-
.../odomEKF/OdometryIndexHelper.java | 12 +
.../odomEKF/OdometryKalmanFilter.java | 592 +++++++++++++-----
3 files changed, 441 insertions(+), 166 deletions(-)
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index f6a7d070520c..6b90c58c4b47 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -338,7 +338,8 @@ private void calculateResidualCovarianceAndInverse()
CommonOps_DDRM.addEquals(residualCovariance, measurementCovariance);
solver.setA(residualCovariance);
- solver.invert(inverseResidualCovariance);
+ CommonOps_DDRM.invert(residualCovariance, inverseResidualCovariance);
+// solver.invert(inverseResidualCovariance);
}
protected void calculateKalmanGain()
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
index 254661f746a3..939b97161e6f 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
@@ -13,6 +13,11 @@ public static int getStateSizePerLink()
return 16;
}
+ public static int getObservationSizePerLink()
+ {
+ return 15;
+ }
+
public static int getStatePositionIndex()
{
return 0;
@@ -91,6 +96,12 @@ public static int getFootGyroBiasIndex(int footNumber)
public static void toQuaternionFromRotationVector(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
{
double magnitude = rotation.norm();
+ if (magnitude < 1e-7)
+ {
+ quaternionToPack.setToZero();
+ return;
+ }
+
double s = Math.sin(magnitude * 0.5);
double qs = Math.cos(magnitude * 0.5);
double qx = s * rotation.getX() / magnitude;
@@ -112,6 +123,7 @@ public static void toQuaternionFromRotationVectorSmallAngle(Vector3DReadOnly rot
public static void logMap(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
{
+ // TODO is this just a transform to the axis-angle representation?
double norm = EuclidCoreTools.norm(quaternion.getX(), quaternion.getY(), quaternion.getZ());
double scale = 2.0 * Math.atan2(norm, quaternion.getS()) / norm;
rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index 050c4b9c0ab2..bd54ad83d356 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -21,7 +21,6 @@
import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
-import us.ihmc.yoVariables.euclid.YoQuaternion;
import us.ihmc.yoVariables.euclid.YoVector3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePose3D;
@@ -29,8 +28,9 @@
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
import us.ihmc.yoVariables.providers.BooleanProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoBoolean;
+import us.ihmc.yoVariables.variable.YoDouble;
-import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@@ -51,6 +51,17 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final double estimatorDT;
private final FrameVector3DReadOnly gravityVector;
+ private final YoDouble translationCovariance = new YoDouble("translationCovariance", registry);
+ private final YoDouble velocityCovariance = new YoDouble("velocityCovariance", registry);
+ private final YoDouble orientationCovariance = new YoDouble("orientationCovariance", registry);
+ private final YoDouble biasCovariance = new YoDouble("biasCovariance", registry);
+
+ private final YoDouble measuredTranslationCovariance = new YoDouble("measuredTranslationCovariance", registry);
+ private final YoDouble measuredOrientationCovariance = new YoDouble("measuredOrientationCovariance", registry);
+ private final YoDouble velocityErrorCovariance = new YoDouble("velocityErrorCovariance", registry);
+ private final YoDouble contactVelocityCovariance = new YoDouble("contactVelocityCovariance", registry);
+ private final YoDouble contactOrientationCovariance = new YoDouble("contactOrientationCovariance", registry);
+
// Internal variables for state holding
private final YoFrameVector3D yoBaseAngularVelocityMeasurement;
@@ -60,6 +71,7 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
// Outputs
private final DMatrixRMaj predictedState;
private final DMatrixRMaj predictedMeasurement;
+ private final DMatrixRMaj observationVector;
private final DMatrixRMaj AMatrix;
private final DMatrixRMaj CMatrix;
@@ -67,16 +79,14 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final MeasuredVariables baseMeasurement;
private final List feetMeasurements = new ArrayList<>();
- private final EstimatedVariables baseCurrentState;
- private final EstimatedVariables basePredictedState;
- private final List feetCurrentState = new ArrayList<>();
- private final List feetPredictedState = new ArrayList<>();
+ private final StateVariables baseProcessState;
+ private final StateVariables basePredictedState;
+ private final List feetProcessState = new ArrayList<>();
+ private final List feetPredictedState = new ArrayList<>();
- private final List sensorProcess = new ArrayList<>();
- private final List sensorMeasurements = new ArrayList<>();
-
- private final EstimatedVariables baseObserveState;
- private final List feetObservationState = new ArrayList<>();
+ private final List processModels = new ArrayList<>();
+ private final List measurementModels = new ArrayList<>();
+ private final List observations = new ArrayList<>();
private final YoFrameQuaternion filteredBaseOrientation;
private final YoFramePoint3D filteredBaseTranslation;
@@ -88,19 +98,13 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final RigidBodyTransformReadOnly transformToRootJoint;
-
// Temporary variables
- private final FrameVector3D linearAcceleration = new FrameVector3D();
- private final FrameVector3D linearVelocity = new FrameVector3D();
- private final Vector3D deltaVector = new Vector3D();
- private final Quaternion estimatedRootJointRotation = new Quaternion();
- private final DMatrixRMaj rotation3x3 = new DMatrixRMaj(3, 3);
-
- private final DMatrixRMaj offsetVector = new DMatrixRMaj(3, 1);
private final DMatrixRMaj footCovariance = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj processCovariance;
+ private final DMatrixRMaj measurementCovariance;
+
- private final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
- private final Twist twist = new Twist();
+ private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
List extends IMUSensorReadOnly> feetIMUs,
@@ -110,38 +114,74 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
double gravitationalAcceleration,
YoRegistry parentRegistry)
{
- super(10 + feetIMUs.size() * 6, 9 * feetIMUs.size());
+ super(OdometryIndexHelper.getStateSizePerLink() * (1 + feetIMUs.size()), OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size());
+
+ // INitialize covariance
+ processCovariance = new DMatrixRMaj(getStateSize(), getStateSize());
+ measurementCovariance = new DMatrixRMaj(getMeasurementSize(), getMeasurementSize());
+ MatrixTools.setDiagonal(processCovariance, 1e-2);
+ MatrixTools.setDiagonal(measurementCovariance, 1e-3);
+ setProcessCovariance(processCovariance);
+ setMeasurementCovariance(measurementCovariance);
+
+ translationCovariance.set(1e-4);
+ velocityCovariance.set(1e-3);
+ orientationCovariance.set(1e-4);
+ biasCovariance.set(1e-6);
+
+ measuredTranslationCovariance.set(1e-5);
+ measuredOrientationCovariance.set(1e-5);
+ velocityErrorCovariance.set(1e-4);
+ contactVelocityCovariance.set(1e-5);
+ contactOrientationCovariance.set(1e-6);
this.baseIMU = baseIMU;
this.feetIMUs = feetIMUs;
this.imuBiasProvider = baseImuBiasProvider;
- this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
this.estimatorDT = estimatorDT;
predictedState = new DMatrixRMaj(getStateSize(), 1);
predictedMeasurement = new DMatrixRMaj(getMeasurementSize(), 1);
AMatrix = new DMatrixRMaj(getStateSize(), getStateSize());
CMatrix = new DMatrixRMaj(getMeasurementSize(), getStateSize());
+ observationVector = new DMatrixRMaj(OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size(), 1);
gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
baseMeasurement = new MeasuredVariables("base", baseIMU, imuBiasProvider, registry);
- baseCurrentState = new EstimatedVariables("baseCurrent");
- baseObserveState = new EstimatedVariables("baseObserve");
- basePredictedState = new EstimatedVariables("basePredicted");
- sensorProcess.add(new SensorProcess(baseMeasurement, baseCurrentState, basePredictedState, cancelGravityFromAccelerationMeasurement, gravityVector, estimatorDT));
+ baseProcessState = new StateVariables("baseProcessState");
+ basePredictedState = new StateVariables("basePredicted");
+ processModels.add(new ProcessModel(baseMeasurement,
+ baseProcessState,
+ basePredictedState,
+ cancelGravityFromAccelerationMeasurement,
+ gravityVector,
+ estimatorDT));
for (int i = 0; i < feetIMUs.size(); i++)
{
MeasuredVariables footMeasurements = new MeasuredVariables("foot" + i, feetIMUs.get(i), imuBiasProvider, registry);
- EstimatedVariables footCurrentState = new EstimatedVariables("foot" + i + "Current");
- EstimatedVariables footObservationState = new EstimatedVariables("foot" + i + "Observe");
- EstimatedVariables footPredictedState = new EstimatedVariables("foot" + i + "Predicted");
+ StateVariables footProcessState = new StateVariables("foot" + i + "ProcessState");
+ StateVariables footPredictedState = new StateVariables("foot" + i + "PredictedState");
feetMeasurements.add(footMeasurements);
- feetObservationState.add(footObservationState);
- feetCurrentState.add(footCurrentState);
+ feetProcessState.add(footProcessState);
feetPredictedState.add(footPredictedState);
- sensorProcess.add(new SensorProcess(footMeasurements, footCurrentState, footPredictedState, cancelGravityFromAccelerationMeasurement, gravityVector, estimatorDT));
- sensorMeasurements.add(new SensorMeasurement("foot" + i, baseObserveState, footObservationState, baseIMU, feetIMUs.get(i), registry));
+
+ observations.add(new Observation("foot" + i + "Observation", baseIMU, feetIMUs.get(i), footMeasurements, registry));
+
+ processModels.add(new ProcessModel(footMeasurements,
+ footProcessState,
+ footPredictedState,
+ cancelGravityFromAccelerationMeasurement,
+ gravityVector,
+ estimatorDT));
+ measurementModels.add(new MeasurementModel("foot" + i,
+ basePredictedState,
+ baseMeasurement,
+ footPredictedState,
+ baseIMU,
+ feetIMUs.get(i),
+ gravityVector,
+ registry));
}
filteredBaseOrientation = new YoFrameQuaternion("filteredBaseOrientation", worldFrame, registry);
@@ -155,18 +195,18 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
transformToRootJoint = baseIMU.getMeasurementFrame().getTransformToDesiredFrame(baseIMU.getMeasurementLink().getParentJoint().getFrameAfterJoint());
yoBaseAngularVelocityMeasurement = new YoFrameVector3D("baseImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
- yoRootJointAngularVelocityMeasurement = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", baseIMU.getMeasurementFrame(), registry);
+ yoRootJointAngularVelocityMeasurement = new YoFrameVector3D("rootJointImuLinearAcceleration", baseIMU.getMeasurementFrame(), registry);
yoRootJointAngularVelocityMeasurementInWorld = new YoFrameVector3D("rootJointImuLinearAccelerationInWorld", worldFrame, registry);
parentRegistry.addChild(registry);
}
- private static class SensorProcess
+ private static class ProcessModel
{
// State providers
private final MeasuredVariables measuredVariables;
- private final EstimatedVariables currentState;
- private final EstimatedVariables predictedState;
+ private final StateVariables currentState;
+ private final StateVariables predictedState;
private final BooleanProvider cancelGravityFromAccelerationMeasurement;
private final FrameVector3DReadOnly gravityVector;
private final double estimatorDt;
@@ -177,15 +217,15 @@ private static class SensorProcess
private final Vector3D integratedVelocity = new Vector3D();
private final Quaternion integratedRotation = new Quaternion();
- public SensorProcess(MeasuredVariables measuredVariables,
- EstimatedVariables currentState,
- EstimatedVariables predictedState,
- BooleanProvider cancelGravityFromAccelerationMeasurement,
- FrameVector3DReadOnly gravityVector,
- double estimatorDt)
+ public ProcessModel(MeasuredVariables measuredVariables,
+ StateVariables processState,
+ StateVariables predictedState,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
+ FrameVector3DReadOnly gravityVector,
+ double estimatorDt)
{
this.measuredVariables = measuredVariables;
- this.currentState = currentState;
+ this.currentState = processState;
this.predictedState = predictedState;
this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
this.gravityVector = gravityVector;
@@ -194,8 +234,8 @@ public SensorProcess(MeasuredVariables measuredVariables,
public void update()
{
- unbiasedAcceleration.sub(measuredVariables.linearAcceleration, currentState.accelBias);
- unbiasedAngularVelocity.sub(measuredVariables.angularVelocity, currentState.gyroBias);
+ unbiasedAcceleration.sub(measuredVariables.linearAccelerationMeasurement, currentState.accelBias);
+ unbiasedAngularVelocity.sub(measuredVariables.gyroMeasurement, currentState.gyroBias);
// transform the acceleration to world
currentState.orientation.transform(unbiasedAcceleration);
if (cancelGravityFromAccelerationMeasurement.getValue())
@@ -221,59 +261,160 @@ public void update()
}
}
- private static class SensorMeasurement
+ private static class Observation
+ {
+ // State providers
+ private final IMUSensorReadOnly baseIMU;
+ private final IMUSensorReadOnly footIMU;
+ private final MeasuredVariables footMeasurements;
+
+ // State variables
+ private final YoVector3D footPositionFromKinematics;
+
+ // Temp variables
+ private final FramePoint3D footPosition = new FramePoint3D();
+
+ public Observation(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOnly footIMU, MeasuredVariables footMeasurements, YoRegistry registry)
+ {
+ this.baseIMU = baseIMU;
+ this.footIMU = footIMU;
+ this.footMeasurements = footMeasurements;
+
+ footPositionFromKinematics = new YoVector3D(prefix + "PositionFromKinematics", registry);
+ }
+
+ public void update()
+ {
+ footPosition.setToZero(footIMU.getMeasurementFrame());
+ footPosition.changeFrame(baseIMU.getMeasurementFrame());
+
+ footPositionFromKinematics.set(footPosition);
+ }
+
+ public void get(int startRow, DMatrixRMaj observation)
+ {
+ footPositionFromKinematics.get(startRow, observation);
+ footMeasurements.linearAccelerationMeasurement.get(startRow + 6, observation);
+ }
+ }
+
+ private static class MeasurementModel
{
- private final EstimatedVariables baseObserve;
- private final EstimatedVariables footObserve;
+ private final StateVariables baseState;
+ private final MeasuredVariables baseMeasurement;
+ private final StateVariables footState;
private final IMUSensorReadOnly baseIMU;
private final IMUSensorReadOnly footIMU;
+ private final FrameVector3DReadOnly gravityVector;
// State variables
public final YoVector3D footPredictedRelativePosition;
public final YoVector3D footPredictedRelativeOrientation;
+ public final YoVector3D footPredictedLinearVelocityError;
+ public final YoVector3D footPredictedLinearVelocity;
+ public final YoVector3D footPredictedGravityVector;
+
+ public final YoBoolean isInContact;
// Temp variables
private final FramePose3D footPose = new FramePose3D();
private final Quaternion footOrientationError = new Quaternion();
private final Vector3D velocityError = new Vector3D();
+ private final Vector3D unbiasedAngularVelocity = new Vector3D();
- public SensorMeasurement(String prefix, EstimatedVariables baseObserve, EstimatedVariables footObserve, IMUSensorReadOnly baseIMU,
- IMUSensorReadOnly footIMU, YoRegistry registry)
+ public MeasurementModel(String prefix,
+ StateVariables baseState,
+ MeasuredVariables baseMeasurement,
+ StateVariables footState,
+ IMUSensorReadOnly baseIMU,
+ IMUSensorReadOnly footIMU,
+ FrameVector3DReadOnly gravityVector,
+ YoRegistry registry)
{
- this.baseObserve = baseObserve;
- this.footObserve = footObserve;
+ this.baseState = baseState;
+ this.baseMeasurement = baseMeasurement;
+ this.footState = footState;
this.baseIMU = baseIMU;
this.footIMU = footIMU;
+ this.gravityVector = gravityVector;
footPredictedRelativePosition = new YoVector3D(prefix + "PredictedRelativePosition", registry);
footPredictedRelativeOrientation = new YoVector3D(prefix + "PredictedRelativeOrientation", registry);
+ footPredictedLinearVelocityError = new YoVector3D(prefix + "PredictedLinearVelocityError", registry);
+ footPredictedLinearVelocity = new YoVector3D(prefix + "PredictedLinearVelocity", registry);
+ footPredictedGravityVector = new YoVector3D(prefix + "PredictedGravityVector", registry);
+ isInContact = new YoBoolean(prefix + "IsInContact", registry);
}
- public void update()
+ /**
+ * This is equation 35
+ */
+ public void update(DMatrixRMaj footCovariance)
{
- // Update the predicted foot position in world
- footPredictedRelativePosition.sub(footObserve.translation, baseObserve.translation);
- baseObserve.orientation.transform(footPredictedRelativePosition);
+ isInContact.set(isFootInContact(footCovariance));
+
+ // First row. Update the predicted foot position of the foot relative to the base in the base frame
+ footPredictedRelativePosition.sub(footState.translation, baseState.translation);
+ baseState.orientation.transform(footPredictedRelativePosition);
- // get the foot pose relative to the base link, which should be entirely based on kinematics
+ // Second row. get the foot pose relative to the base link, which should be entirely based on kinematics
footPose.setToZero(footIMU.getMeasurementFrame());
footPose.changeFrame(baseIMU.getMeasurementFrame());
- // FIXME compute the predicted relative orientation
- footOrientationError.multiply(footPose.getOrientation().inverse(), baseObserve.orientation.inverse());
- footOrientationError.multiply(footObserve.orientation);
+ // Update the predicted foot orientation of the foot relative to the base in the base frame
+ footOrientationError.set(footPose.getOrientation());
+ footOrientationError.multiplyConjugateBoth(baseState.orientation);
+ footOrientationError.multiply(footState.orientation);
OdometryIndexHelper.logMap(footOrientationError, footPredictedRelativeOrientation);
+ // Compute the linear velocity error in the base frame
+ unbiasedAngularVelocity.sub(baseMeasurement.gyroMeasurement, baseState.gyroBias);
+ velocityError.sub(baseState.linearVelocity, footState.linearVelocity);
+ baseState.orientation.inverseTransform(velocityError);
- // Compute
- // FIXME finish the sensor measurement process
+ // FIXME this likely changes based on the contact conditions
+ footPredictedLinearVelocityError.cross(unbiasedAngularVelocity, footPose.getPosition());
+ footPredictedLinearVelocityError.scale(-1.0);
+ footPredictedLinearVelocityError.sub(velocityError);
+ // FIXME get the estimated foot linear velocity from the kinematics, and add it to here. That is, get the one using the MovingReferenceFrame, relative to the base IMU.
+
+ if (isInContact.getBooleanValue())
+ return;
+
+ // Fourth row. Set the predicted linear velocity
+ footPredictedLinearVelocity.set(footState.linearVelocity);
+
+ // Fifth row. Set the predicted gravity vector
+ footState.orientation.transform(gravityVector, footPredictedGravityVector);
+ footPredictedGravityVector.add(footState.accelBias);
+ }
+
+ private final DMatrixRMaj vector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempVector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempScalar = new DMatrixRMaj(3, 1);
+
+ private boolean isFootInContact(DMatrixRMaj footCovariance)
+ {
+ footState.translation.get(vector);
+
+ CommonOps_DDRM.mult(footCovariance, vector, tempVector);
+ CommonOps_DDRM.multTransA(vector, tempVector, tempScalar);
+
+ // tests the mahalonobis distance of the foot velocity being below a certain threshold.
+ return tempScalar.get(0, 0) < 0.2; // FIXME extract magic number
}
public void get(int start, DMatrixRMaj measurementToPack)
{
footPredictedRelativePosition.get(start, measurementToPack);
footPredictedRelativeOrientation.get(start + 3, measurementToPack);
+ footPredictedLinearVelocityError.get(start + 6, measurementToPack);
+ if (isInContact.getBooleanValue())
+ return;
+
+ footPredictedLinearVelocity.get(start + 9, measurementToPack);
+ footPredictedGravityVector.get(start + 12, measurementToPack);
}
}
@@ -293,10 +434,7 @@ private static class MeasuredVariables
private final FrameVector3D linearAcceleration = new FrameVector3D();
private final FrameVector3D angularVelocity = new FrameVector3D();
- public MeasuredVariables(String prefix,
- IMUSensorReadOnly imu,
- IMUBiasProvider imuBiasProvider,
- YoRegistry registry)
+ public MeasuredVariables(String prefix, IMUSensorReadOnly imu, IMUBiasProvider imuBiasProvider, YoRegistry registry)
{
this.imu = imu;
this.imuBiasProvider = imuBiasProvider;
@@ -305,7 +443,6 @@ public MeasuredVariables(String prefix,
gyroMeasurement = new YoFrameVector3D(prefix + "GyroMeasurement", imu.getMeasurementFrame(), registry);
linearAccelerationMeasurementInWorld = new YoFrameVector3D(prefix + "LinearAccelerationMeasurementInWorld", worldFrame, registry);
linearAccelerationMeasurement = new YoFrameVector3D(prefix + "LinearAccelerationMeasurement", imu.getMeasurementFrame(), registry);
-
}
public void update()
@@ -330,9 +467,9 @@ public void update()
linearAccelerationMeasurementInWorld.setMatchingFrame(linearAcceleration);
linearAccelerationMeasurement.setMatchingFrame(linearAcceleration);
}
-
}
- private class EstimatedVariables
+
+ private class StateVariables
{
public final YoFramePoint3D translation;
public final YoFrameVector3D linearVelocity;
@@ -340,7 +477,7 @@ private class EstimatedVariables
public final YoFrameVector3D accelBias;
public final YoFrameVector3D gyroBias;
- public EstimatedVariables(String prefix)
+ public StateVariables(String prefix)
{
translation = new YoFramePoint3D(prefix + "Translation", worldFrame, registry);
linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
@@ -370,15 +507,20 @@ public void get(int start, DMatrixRMaj stateToPack)
public void compute()
{
- updateMeasures();
- updateRootJointTwistAngularPart();
+ updateCovariance();
+
+ baseMeasurement.update();
+ for (int i = 0; i < feetMeasurements.size(); i++)
+ feetMeasurements.get(i).update();
- // FIXME populate the observations, which are
- // FIXME 1. the relative distance between the IMU and the foot in the IMU frame
- // FIXME 2. the relative linear velocity between the IMU and the foot in the IMU frame
- // FIXME 3. the absolute linear velocity of the foot
+ for (int i = 0; i < observations.size(); i++)
+ {
+ observations.get(i).update();
+ observations.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), observationVector);
+ }
+ updateRootJointTwistAngularPart(); // TODO maybe do this after we update everything based on the estimate?
- DMatrixRMaj stateEstimate = calculateEstimate();
+ DMatrixRMaj stateEstimate = calculateEstimate(observationVector);
filteredBaseTranslation.set(stateEstimate);
filteredBaseLinearVelocity.set(3, stateEstimate);
@@ -390,25 +532,58 @@ public void compute()
estimatedRootJointPose.set(tempPose);
estimatedRootJointAngularVelocity.set(yoRootJointAngularVelocityMeasurementInWorld);
- updateRootJointTwistLinearPart(filteredBaseLinearVelocity,
- estimatedRootJointAngularVelocity,
- estimatedRootJointLinearVelocity);
+ updateRootJointTwistLinearPart(filteredBaseLinearVelocity, estimatedRootJointAngularVelocity, estimatedRootJointLinearVelocity);
}
- private final FramePose3D tempPose = new FramePose3D();
-
-
- public void updateMeasures()
+ private void updateCovariance()
{
- // TODO re-enable this
-// if (!isEstimationEnabled())
-// return;
+ for (int i = 0; i < feetIMUs.size() + 1; i++)
+ {
+ int index = i * OdometryIndexHelper.getStateSizePerLink();
+ processCovariance.set(index, index++, translationCovariance.getValue());
+ processCovariance.set(index, index++, translationCovariance.getValue());
+ processCovariance.set(index, index++, translationCovariance.getValue());
+ processCovariance.set(index, index++, velocityCovariance.getValue());
+ processCovariance.set(index, index++, velocityCovariance.getValue());
+ processCovariance.set(index, index++, velocityCovariance.getValue());
+ processCovariance.set(index, index++, orientationCovariance.getValue());
+ processCovariance.set(index, index++, orientationCovariance.getValue());
+ processCovariance.set(index, index++, orientationCovariance.getValue());
+ processCovariance.set(index, index++, orientationCovariance.getValue());
+ processCovariance.set(index, index++, biasCovariance.getValue());
+ processCovariance.set(index, index++, biasCovariance.getValue());
+ processCovariance.set(index, index++, biasCovariance.getValue());
+ processCovariance.set(index, index++, biasCovariance.getValue());
+ processCovariance.set(index, index++, biasCovariance.getValue());
+ processCovariance.set(index, index, biasCovariance.getValue());
+ }
- baseMeasurement.update();
- for (int i = 0; i < feetMeasurements.size(); i++)
- feetMeasurements.get(i).update();
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ int index = i * OdometryIndexHelper.getObservationSizePerLink();
+ measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
+ measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
+ measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
+ measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
+ measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
+ measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
+ measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
+ measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
+ measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
+ measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
+ measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
+ measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
+ measurementCovariance.set(index, index++, contactOrientationCovariance.getValue());
+ measurementCovariance.set(index, index++, contactOrientationCovariance.getValue());
+ measurementCovariance.set(index, index, contactOrientationCovariance.getValue());
+ }
+
+ setProcessCovariance(processCovariance);
+ setMeasurementCovariance(measurementCovariance);
}
+ private final FramePose3D tempPose = new FramePose3D();
+
private final Vector3D angularVelocityMeasurement = new Vector3D();
/** Angular velocity of the measurement link, with respect to world. */
@@ -483,16 +658,18 @@ private void updateRootJointTwistLinearPart(FrameVector3DReadOnly imuLinearVeloc
@Override
public DMatrixRMaj processModel(DMatrixRMaj state)
{
+ predictedState.zero();
+
// update the yo variables representing the state
- baseCurrentState.set(OdometryIndexHelper.getBasePositionIndex(), state);
+ baseProcessState.set(OdometryIndexHelper.getBasePositionIndex(), state);
for (int i = 0; i < feetIMUs.size(); i++)
{
- feetCurrentState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
+ feetProcessState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
}
// do the prediction
- for (int i = 0; i < sensorProcess.size(); i++)
- sensorProcess.get(i).update();
+ for (int i = 0; i < processModels.size(); i++)
+ processModels.get(i).update();
// update the predicted state from the yo variablized state
basePredictedState.get(OdometryIndexHelper.getBasePositionIndex(), state);
@@ -506,65 +683,38 @@ public DMatrixRMaj processModel(DMatrixRMaj state)
}
@Override
- public DMatrixRMaj measurementModel(DMatrixRMaj state)
+ public DMatrixRMaj measurementModel(DMatrixRMaj predictedState)
{
- // update the yo variables representing the state
- baseObserveState.set(OdometryIndexHelper.getBasePositionIndex(), state);
+ predictedMeasurement.zero();
+
+ // Note that this bypasses the use of predicted state vector, as the predicted state variables are computed in the {@link #processModel(DMatrixRMaj)} call
for (int i = 0; i < feetIMUs.size(); i++)
{
- feetObservationState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
- }
+ int row = OdometryIndexHelper.getFootVelocityIndex(i);
+ MatrixTools.setMatrixBlock(footCovariance, 0, 0, getCovariance(), row, row, 3, 3, 1.0);
- for (int i = 0; i < sensorMeasurements.size(); i++)
- {
- sensorMeasurements.get(i).update();
- sensorMeasurements.get(i).get(i * 10, predictedMeasurement); // FIXME fix this offset
+ measurementModels.get(i).update(footCovariance);
+ measurementModels.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), predictedMeasurement);
}
- estimatedRootJointRotation.set(6, state);
-
// return the predicted measurement
return predictedMeasurement;
}
- private final DMatrixRMaj temp = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj tempScalar = new DMatrixRMaj(1, 1);
- private boolean isFootInContact(int footIndex, DMatrixRMaj state)
- {
- int startRow = 10 + footIndex * 6;
- CommonOps_DDRM.extract(state, startRow, startRow + 3, 0, 1, offsetVector, 0, 0);
- CommonOps_DDRM.extract(getCovariance(), startRow, startRow + 3, startRow, startRow + 3, footCovariance, 0, 0);
-
- CommonOps_DDRM.mult(footCovariance, offsetVector, temp);
- CommonOps_DDRM.multTransA(offsetVector, temp, tempScalar);
-
- // tests the mahalonobis distance of the foot velocity being below a certain threshold.
- return tempScalar.get(0, 0) < 0.2;
- }
-
@Override
protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
{
- // Populate the gradient of the base position w.r.t. the state
- CommonOps_DDRM.insert(eye3x3, AMatrix, 0, 0);
- MatrixTools.setMatrixBlock(AMatrix, 0, 3, eye3x3, 0, 0, 3, 3, estimatorDT);
-
- // Populate the gradient of the base velocity w.r.t. the state
- CommonOps_DDRM.insert(eye3x3, AMatrix, 3, 3);
- // FIXME add the effect of the gradient of the base rotation
-
- // FIXME do the gradient crap for the base orientation, which is going to be frustrating
+ // make sure to call zero, as the compute process model stuff has a bunch of add operators.
+ AMatrix.zero();
+ int rowOffset = 0;
+ int colOffset = 0;
+ computeProcessJacobian(baseMeasurement, baseProcessState, estimatorDT, rowOffset, colOffset, AMatrix);
for (int i = 0; i < feetIMUs.size(); i++)
{
- int stateOffset = 10 + i * 6;
- // Populate the gradient of the foot position w.r.t. the state
- CommonOps_DDRM.insert(eye3x3, AMatrix, stateOffset, stateOffset);
- MatrixTools.setMatrixBlock(AMatrix, stateOffset, stateOffset + 3, eye3x3, 0, 0, 3, 3, estimatorDT);
-
- // Populate the gradient of the foot position w.r.t. the state
- // FIXME add the effect of the gradient of the base rotation
- CommonOps_DDRM.insert(eye3x3, AMatrix, stateOffset + 3, stateOffset + 3);
+ rowOffset += OdometryIndexHelper.getStateSizePerLink();
+ colOffset += OdometryIndexHelper.getStateSizePerLink();
+ computeProcessJacobian(feetMeasurements.get(i), feetProcessState.get(i), estimatorDT, rowOffset, colOffset, AMatrix);
}
return AMatrix;
@@ -573,34 +723,17 @@ protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
@Override
protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
{
- estimatedRootJointRotation.set(6, predictedState);
- toRotationMatrix(estimatedRootJointRotation, rotation3x3);
-
- CommonOps_DDRM.transpose(rotation3x3);
-
+ // make sure to call zero, as the compute process model stuff has a bunch of add operators.
+ CMatrix.zero();
+ int rowOffset = 0;
+ int colOffset = 0;
for (int i = 0; i < feetIMUs.size(); i++)
{
- int footStateOffset = 10 + i * 6;
- int measureOffset = 9 * i;
- // R^T * (s_k - p_k)
- CommonOps_DDRM.insert(rotation3x3, CMatrix, measureOffset, footStateOffset);
- MatrixTools.setMatrixBlock(CMatrix, measureOffset, 0, rotation3x3, 0, 0, 3, 3, -1.0);
-
- MatrixTools.setMatrixBlock(offsetVector, 0, 0, predictedState, footStateOffset, 0, 3, 1, 1.0);
- MatrixTools.addMatrixBlock(offsetVector, 0, 0, predictedState, 0, 0, 3, 1, -1.0);
-
- // TODO add the effect of the gradient of the base rotation
-
- // R^T * (v_k - sDot_k)
- CommonOps_DDRM.insert(rotation3x3, CMatrix, measureOffset + 3, 3);
- MatrixTools.setMatrixBlock(CMatrix, measureOffset + 3, footStateOffset + 3, rotation3x3, 0, 0, 3, 3, -1.0);
- // TODO add the effect of the gradient of the base rotation
-
- MatrixTools.setMatrixBlock(offsetVector, 0, 0, predictedState, 3, 0, 3, 1, 1.0);
- MatrixTools.addMatrixBlock(offsetVector, 0, 0, predictedState, footStateOffset + 3, 0, 3, 1, -1.0);
-
- // sDot_k
- CommonOps_DDRM.insert(eye3x3, CMatrix, measureOffset + 6, footStateOffset + 3);
+ boolean footIsInContact = measurementModels.get(i).isInContact.getBooleanValue();
+ computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), rowOffset, CMatrix);
+ computeFootMeasurementJacobian(footIsInContact, basePredictedState, feetPredictedState.get(i), gravityVector, rowOffset, colOffset + 15, CMatrix);
+ rowOffset += OdometryIndexHelper.getObservationSizePerLink();
+ colOffset += OdometryIndexHelper.getStateSizePerLink();
}
return CMatrix;
@@ -671,4 +804,133 @@ static void toRotationMatrixInverse(double qx, double qy, double qz, double qs,
rotationMatrix.set(2, 1, 2.0 * (yz - xw));
rotationMatrix.set(1, 2, 2.0 * (yz + xw));
}
+
+ /**
+ * This is equation 39
+ */
+ static void computeProcessJacobian(MeasuredVariables measuredVariables, StateVariables stateVariables, double estimateDT, int rowOffset, int colOffset, DMatrixRMaj jacobianToPack)
+ {
+ // Do the partial derivative with respect to the base position state
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + OdometryIndexHelper.getStatePositionIndex(), colOffset + OdometryIndexHelper.getStatePositionIndex());
+ MatrixTools.setMatrixBlock(jacobianToPack,
+ rowOffset + OdometryIndexHelper.getStatePositionIndex(),
+ colOffset + OdometryIndexHelper.getStateVelocityIndex(),
+ eye3x3,
+ 0,
+ 0,
+ 3,
+ 3,
+ estimateDT);
+
+ // Do the partial derivative with respect to the base velocity state FIXME lots of garbage
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateVelocityIndex());
+
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ Vector3D unbiasedAcceleration = new Vector3D();
+ unbiasedAcceleration.sub(measuredVariables.linearAccelerationMeasurement, stateVariables.accelBias);
+ toRotationMatrix(stateVariables.orientation, rotationMatrix);
+ toSkewSymmetricMatrix(unbiasedAcceleration, skewMatrix);
+ MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex());
+
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateGyroBiasIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+
+ // Do the partial derivative with respect to the orientation state
+ Vector3D unbiasedVelocity = new Vector3D();
+ unbiasedVelocity.sub(measuredVariables.gyroMeasurement, stateVariables.gyroBias);
+ toSkewSymmetricMatrix(unbiasedVelocity, skewMatrix);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+ MatrixTools.addMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex(), eye3x3, 0, 0, 3, 3, 1.0);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, -estimateDT);
+
+ // Do the partial derivative with respect to the acceleration bias state
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), colOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, 1.0);
+ // Do the partial derivative with respect to the gyro bias state
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateGyroBiasIndex(), colOffset + OdometryIndexHelper.getStateGyroBiasIndex(), eye3x3, 0, 0, 3, 3, 1.0);
+ }
+
+ /**
+ * This is equation 41
+ */
+ private void computeBaseMeasurementJacobian(StateVariables baseState, StateVariables footState, int rowOffset, DMatrixRMaj jacobianToPack)
+ {
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ toRotationMatrix(baseState.orientation, rotationMatrix);
+ CommonOps_DDRM.transpose(rotationMatrix);
+
+ Vector3D footPositionError = new Vector3D();
+ Vector3D footVelocityError = new Vector3D();
+ footPositionError.sub(footState.translation, baseState.translation);
+ footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(footPositionError);
+ baseState.orientation.inverseTransform(footVelocityError);
+
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ toSkewSymmetricMatrix(footPositionError, skewMatrix);
+
+ // First row
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 0, rotationMatrix, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
+
+ // FIXME do the second row
+
+ // Third row
+ toSkewSymmetricMatrix(footVelocityError, skewMatrix);
+
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + 6, 3, rotationMatrix, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset+ 6, 6);
+ // FIXME do the last term, in the fifth column
+ }
+
+ /**
+ * This is equation 42
+ */
+ private void computeFootMeasurementJacobian(boolean isInContact, StateVariables baseState, StateVariables footState, FrameVector3DReadOnly gravityVector, int rowOffset, int colOffset, DMatrixRMaj jacobianToPack)
+ {
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ toRotationMatrix(baseState.orientation, rotationMatrix);
+ CommonOps_DDRM.transpose(rotationMatrix);
+
+ Vector3D footPositionError = new Vector3D();
+ Vector3D footVelocityError = new Vector3D();
+ footPositionError.sub(footState.translation, baseState.translation);
+ footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(footPositionError);
+ baseState.orientation.inverseTransform(footVelocityError);
+
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ toSkewSymmetricMatrix(footPositionError, skewMatrix);
+
+ // First row
+ CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset);
+
+ // FIXME do the second row
+
+ // Third row
+ CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset + 6, colOffset + 3);
+
+ if (!isInContact)
+ return;
+
+ // Fourth row
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + 9, colOffset + 3);
+
+ // Fifth row
+ Vector3D gravity = new Vector3D(gravityVector);
+ footState.orientation.inverseTransform(gravity);
+ toSkewSymmetricMatrix(gravityVector, skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset + 12, colOffset + 6);
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + 12, colOffset + 9);
+ }
+
+ static void toSkewSymmetricMatrix(Vector3DReadOnly vector, DMatrixRMaj matrixToPack)
+ {
+ matrixToPack.zero();
+ matrixToPack.set(0, 1, -vector.getZ());
+ matrixToPack.set(0, 2, vector.getY());
+ matrixToPack.set(1, 0, vector.getZ());
+ matrixToPack.set(1, 2, -vector.getX());
+ matrixToPack.set(2, 0, -vector.getY());
+ matrixToPack.set(2, 1, -vector.getX());
+ }
}
From 77da4335fa57e9ab8d2c445ebea74de351d20cfe Mon Sep 17 00:00:00 2001
From: Robert
Date: Fri, 29 Aug 2025 16:54:47 -0500
Subject: [PATCH 05/18] started breaking up the EKF, and getting things to run
---
.../ExtendedKalmanFilter.java | 5 +
.../ihmc/robotics/sensors/IMUDefinition.java | 9 +-
.../ihmc/sensorProcessing/imu/IMUSensor.java | 5 +-
.../stateEstimation/IMUSensorReadOnly.java | 3 +-
.../DRCKinematicsBasedStateEstimator.java | 6 +-
.../odomEKF/MeasurementModel.java | 244 +++++++
.../odomEKF/ObservationModel.java | 21 +
.../odomEKF/OdometryKalmanFilter.java | 618 ++++--------------
.../odomEKF/OdometryTools.java | 90 +++
.../odomEKF/ProcessModel.java | 123 ++++
.../odomEKF/OdometryKalmanFilterTest.java | 57 --
.../odomEKF/OdometryToolsTest.java | 124 ++++
12 files changed, 764 insertions(+), 541 deletions(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
delete mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
create mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index 6b90c58c4b47..a330c7c34f87 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -210,6 +210,11 @@ public ExtendedKalmanFilter(DMatrixRMaj initialState, DMatrixRMaj initialCovaria
covariance.set(initialCovariance);
}
+ public void initializeState(DMatrixRMaj state)
+ {
+ this.state.set(state);
+ }
+
public int getStateSize()
{
return stateSize;
diff --git a/ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/sensors/IMUDefinition.java b/ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/sensors/IMUDefinition.java
index df1b2287416d..2f87bee20642 100644
--- a/ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/sensors/IMUDefinition.java
+++ b/ihmc-robotics-toolkit/src/main/java/us/ihmc/robotics/sensors/IMUDefinition.java
@@ -4,6 +4,7 @@
import us.ihmc.euclid.referenceFrame.tools.ReferenceFrameTools;
import us.ihmc.euclid.transform.RigidBodyTransform;
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
public class IMUDefinition
@@ -11,7 +12,7 @@ public class IMUDefinition
private final String name;
private final RigidBodyBasics rigidBody;
private final RigidBodyTransform transformFromIMUToJoint;
- private final ReferenceFrame imuFrame;
+ private final MovingReferenceFrame imuFrame;
public IMUDefinition(String name, RigidBodyBasics rigidBody, RigidBodyTransformReadOnly transformFromIMUToJoint)
{
@@ -19,8 +20,8 @@ public IMUDefinition(String name, RigidBodyBasics rigidBody, RigidBodyTransformR
this.rigidBody = rigidBody;
this.transformFromIMUToJoint = new RigidBodyTransform(transformFromIMUToJoint);
- ReferenceFrame frameAfterJoint = rigidBody.getParentJoint().getFrameAfterJoint();
- imuFrame = ReferenceFrameTools.constructFrameWithUnchangingTransformToParent(name, frameAfterJoint, transformFromIMUToJoint);
+ MovingReferenceFrame frameAfterJoint = rigidBody.getParentJoint().getFrameAfterJoint();
+ imuFrame = MovingReferenceFrame.constructFrameFixedInParent(name, frameAfterJoint, transformFromIMUToJoint);
}
public String getName()
@@ -38,7 +39,7 @@ public RigidBodyTransformReadOnly getTransformFromIMUToJoint()
return transformFromIMUToJoint;
}
- public ReferenceFrame getIMUFrame()
+ public MovingReferenceFrame getIMUFrame()
{
return imuFrame;
}
diff --git a/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/imu/IMUSensor.java b/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/imu/IMUSensor.java
index d69575551967..5fffa5c85b42 100644
--- a/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/imu/IMUSensor.java
+++ b/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/imu/IMUSensor.java
@@ -10,6 +10,7 @@
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
import us.ihmc.robotics.sensors.IMUDefinition;
import us.ihmc.sensorProcessing.simulatedSensors.SensorNoiseParameters;
@@ -19,7 +20,7 @@ public class IMUSensor implements IMUSensorReadOnly
{
private final String sensorName;
- private final ReferenceFrame measurementFrame;
+ private final MovingReferenceFrame measurementFrame;
private final RigidBodyBasics measurementLink;
private final Quaternion orientationMeasurement = new Quaternion();
@@ -68,7 +69,7 @@ public String getSensorName()
}
@Override
- public ReferenceFrame getMeasurementFrame()
+ public MovingReferenceFrame getMeasurementFrame()
{
return measurementFrame;
}
diff --git a/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/stateEstimation/IMUSensorReadOnly.java b/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/stateEstimation/IMUSensorReadOnly.java
index c1e048fde073..06056ed439ac 100644
--- a/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/stateEstimation/IMUSensorReadOnly.java
+++ b/ihmc-sensor-processing/src/main/java/us/ihmc/sensorProcessing/stateEstimation/IMUSensorReadOnly.java
@@ -7,13 +7,14 @@
import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
public interface IMUSensorReadOnly
{
public abstract String getSensorName();
- public abstract ReferenceFrame getMeasurementFrame();
+ public abstract MovingReferenceFrame getMeasurementFrame();
public abstract RigidBodyBasics getMeasurementLink();
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
index 68792aaba966..1f0ed3240994 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/DRCKinematicsBasedStateEstimator.java
@@ -344,8 +344,7 @@ public void doControl()
for (int i = 0; i < footSwitchList.size(); i++)
footSwitchList.get(i).update();
- // TODO should this also consume some of the foot switch data?
- odometry.compute();
+
switch (operatingMode.getEnumValue())
{
@@ -358,6 +357,9 @@ public void doControl()
break;
}
+ // TODO should this also consume some of the foot switch data?
+ odometry.compute();
+
if (momentumStateUpdater != null)
momentumStateUpdater.update();
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
new file mode 100644
index 000000000000..6aca27f48f58
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -0,0 +1,244 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.tools.QuaternionTools;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.MeasuredVariables;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.StateVariables;
+import us.ihmc.yoVariables.euclid.YoQuaternion;
+import us.ihmc.yoVariables.euclid.YoVector3D;
+import us.ihmc.yoVariables.providers.DoubleProvider;
+import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoBoolean;
+
+class MeasurementModel
+{
+ private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
+
+ private final StateVariables baseState;
+ private final StateVariables footState;
+ private final MeasuredVariables footMeasurements;
+ private final FrameVector3DReadOnly gravityVector;
+
+ // State variables
+ public final YoVector3D footPredictedRelativePosition;
+ public final YoVector3D footPredictedRelativeOrientationError;
+ public final YoVector3D footPredictedLinearVelocityError;
+ public final YoVector3D footPredictedLinearVelocity;
+ public final YoVector3D footPredictedGravityVector;
+ public final YoQuaternion footOrientationInBaseFrame;
+
+ public final YoBoolean isInContact;
+ private final DoubleProvider contactThreshold;
+
+ // Temp variables
+ private final Quaternion footOrientationError = new Quaternion();
+ private final Vector3D velocityError = new Vector3D();
+
+ public MeasurementModel(String prefix,
+ StateVariables baseState,
+ StateVariables footState,
+ MeasuredVariables footMeasurements,
+ FrameVector3DReadOnly gravityVector,
+ DoubleProvider contactThreshold,
+ YoRegistry registry)
+ {
+ this.baseState = baseState;
+ this.footState = footState;
+ this.footMeasurements = footMeasurements;
+ this.gravityVector = gravityVector;
+ this.contactThreshold = contactThreshold;
+
+ footPredictedRelativePosition = new YoVector3D(prefix + "PredictedRelativePosition", registry);
+ footPredictedRelativeOrientationError = new YoVector3D(prefix + "PredictedRelativeOrientationError", registry);
+ footPredictedLinearVelocityError = new YoVector3D(prefix + "PredictedLinearVelocityError", registry);
+ footPredictedLinearVelocity = new YoVector3D(prefix + "PredictedLinearVelocity", registry);
+ footPredictedGravityVector = new YoVector3D(prefix + "PredictedAccelMeasure", registry);
+ footOrientationInBaseFrame = new YoQuaternion(prefix + "OrientationInBaseFrame", registry);
+ isInContact = new YoBoolean(prefix + "IsInContact", registry);
+ }
+
+ /**
+ * This is equation 35
+ */
+ public void update(DMatrixRMaj footCovariance)
+ {
+ isInContact.set(isFootInContact(footCovariance));
+
+ // First row. Update the predicted foot position of the foot relative to the base in the base frame
+ footPredictedRelativePosition.sub(footState.translation, baseState.translation);
+ baseState.orientation.transform(footPredictedRelativePosition); // FIXME is this right? transforms do more operations than I think is expected
+
+ // Update the predicted foot orientation of the foot relative to the base in the base frame
+ QuaternionTools.multiplyConjugateLeft(baseState.orientation, footState.orientation, footOrientationInBaseFrame);
+
+ // Compute the error between the predicted and measured foot orientation
+ QuaternionTools.multiplyConjugateLeft(footMeasurements.orientationMeasurement, footOrientationInBaseFrame, footOrientationError);
+ footOrientationError.getRotationVector(footPredictedRelativeOrientationError);
+
+ // Third row. Compute the linear velocity error in the base frame
+ velocityError.sub(baseState.linearVelocity, footState.linearVelocity);
+ baseState.orientation.inverseTransform(velocityError);
+
+ footPredictedLinearVelocityError.cross(baseState.unbiasedGyro, footMeasurements.positionMeasurement);
+ footPredictedLinearVelocityError.scale(-1.0);
+ footPredictedLinearVelocityError.sub(velocityError);
+ footPredictedLinearVelocityError.add(footMeasurements.linearVelocity);
+
+ if (!isInContact.getBooleanValue())
+ return;
+
+ // Fourth row. Set the predicted linear velocity
+ footPredictedLinearVelocity.set(footState.linearVelocity);
+
+ // Fifth row. Set the predicted gravity vector
+ footState.orientation.transform(gravityVector, footPredictedGravityVector);
+ if (OdometryKalmanFilter.includeBias)
+ footPredictedGravityVector.add(footState.accelBias);
+ }
+
+ private final DMatrixRMaj vector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempVector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempScalar = new DMatrixRMaj(3, 1);
+
+ private boolean isFootInContact(DMatrixRMaj footCovariance)
+ {
+ footState.translation.get(vector);
+
+ CommonOps_DDRM.mult(footCovariance, vector, tempVector);
+ CommonOps_DDRM.multTransA(vector, tempVector, tempScalar);
+
+ // tests the mahalonobis distance of the foot velocity being below a certain threshold.
+ return tempScalar.get(0, 0) < contactThreshold.getValue();
+ }
+
+ public void get(int start, DMatrixRMaj measurementToPack)
+ {
+ footPredictedRelativePosition.get(start, measurementToPack);
+ footPredictedRelativeOrientationError.get(start + 3, measurementToPack);
+ footPredictedLinearVelocityError.get(start + 6, measurementToPack);
+
+ if (isInContact.getBooleanValue())
+ return;
+
+ footPredictedLinearVelocity.get(start + 9, measurementToPack);
+ footPredictedGravityVector.get(start + 12, measurementToPack);
+ }
+
+ /**
+ * This is equation 41
+ */
+ public void computeBaseMeasurementJacobian(StateVariables baseState,
+ StateVariables footState,
+ MeasuredVariables footMeasurements,
+ int rowOffset,
+ DMatrixRMaj jacobianToPack)
+ {
+ // FIXME garbage
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj rotationMatrixTranspose = new DMatrixRMaj(3, 3);
+ OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrix);
+ CommonOps_DDRM.transpose(rotationMatrix, rotationMatrixTranspose);
+
+ Vector3D footPositionError = new Vector3D();
+ Vector3D footVelocityError = new Vector3D();
+ footPositionError.sub(footState.translation, baseState.translation);
+ footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(footPositionError);
+ baseState.orientation.inverseTransform(footVelocityError);
+
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ OdometryTools.toSkewSymmetricMatrix(footPositionError, skewMatrix);
+
+ // First row
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 0, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
+
+ // Second row
+ rowOffset += 3;
+ DMatrixRMaj left = new DMatrixRMaj(3, 3);
+ DMatrixRMaj right = new DMatrixRMaj(3, 3);
+ Quaternion orientation = new Quaternion(footState.orientation);
+ orientation.multiplyConjugateThis(baseState.orientation);
+ OdometryKalmanFilter.l3Operator(orientation, left);
+ OdometryKalmanFilter.r3Operator(footMeasurements.orientationMeasurement, right);
+ MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, rowOffset, 6);
+
+ // Third row
+ rowOffset += 3;
+ OdometryTools.toSkewSymmetricMatrix(footVelocityError, skewMatrix);
+
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 3, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
+
+ if (OdometryKalmanFilter.includeBias)
+ {
+ OdometryTools.toSkewSymmetricMatrix(footMeasurements.positionMeasurement, skewMatrix);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 12, skewMatrix, 0, 0, 3, 3, -1.0);
+ }
+ }
+
+ /**
+ * This is equation 42
+ */
+ public void computeFootMeasurementJacobian(StateVariables baseState,
+ StateVariables footState,
+ MeasuredVariables footMeasurements,
+ FrameVector3DReadOnly gravityVector,
+ int rowOffset,
+ int colOffset,
+ DMatrixRMaj jacobianToPack)
+ {
+ // FIXME garbage
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrix);
+ CommonOps_DDRM.transpose(rotationMatrix);
+
+ Vector3D footPositionError = new Vector3D();
+ Vector3D footVelocityError = new Vector3D();
+ footPositionError.sub(footState.translation, baseState.translation);
+ footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(footPositionError);
+ baseState.orientation.inverseTransform(footVelocityError);
+
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ OdometryTools.toSkewSymmetricMatrix(footPositionError, skewMatrix);
+
+ // First row
+ CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset);
+
+ // Second row
+ rowOffset += 3;
+ DMatrixRMaj left = new DMatrixRMaj(3, 3);
+ Quaternion orientation = new Quaternion(footMeasurements.orientationMeasurement);
+ orientation.multiplyConjugateOther(baseState.orientation);
+ orientation.multiply(footState.orientation);
+ OdometryKalmanFilter.l3Operator(orientation, left);
+ CommonOps_DDRM.insert(left, jacobianToPack, rowOffset, colOffset + 6);
+
+ // Third row
+ rowOffset += 3;
+ CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset + 3);
+
+ if (isInContact.getBooleanValue())
+ return;
+
+ // Fourth row
+ rowOffset += 3;
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset, colOffset + 3);
+
+ // Fifth row
+ rowOffset += 3;
+ Vector3D gravity = new Vector3D(gravityVector);
+ footState.orientation.inverseTransform(gravity);
+ OdometryTools.toSkewSymmetricMatrix(gravityVector, skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, colOffset + 6);
+
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset, colOffset + 9);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
new file mode 100644
index 000000000000..617db46f5a41
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
@@ -0,0 +1,21 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.MeasuredVariables;
+
+public class ObservationModel
+{
+ // State providers
+ private final MeasuredVariables footMeasurements;
+
+ public ObservationModel(MeasuredVariables footMeasurements)
+ {
+ this.footMeasurements = footMeasurements;
+ }
+
+ public void get(int startRow, DMatrixRMaj observation)
+ {
+ footMeasurements.positionMeasurement.get(startRow, observation);
+ footMeasurements.accelMeasurement.get(startRow + 6, observation);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index bd54ad83d356..db8577b5a8eb 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -12,23 +12,21 @@
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
-import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
import us.ihmc.mecano.spatial.Twist;
import us.ihmc.parameterEstimation.ExtendedKalmanFilter;
import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
-import us.ihmc.yoVariables.euclid.YoVector3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePose3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
import us.ihmc.yoVariables.providers.BooleanProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
-import us.ihmc.yoVariables.variable.YoBoolean;
import us.ihmc.yoVariables.variable.YoDouble;
import java.util.ArrayList;
@@ -39,6 +37,8 @@
*/
public class OdometryKalmanFilter extends ExtendedKalmanFilter
{
+ static final boolean includeBias = false;
+
// Constants and providers
private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
@@ -60,7 +60,10 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final YoDouble measuredOrientationCovariance = new YoDouble("measuredOrientationCovariance", registry);
private final YoDouble velocityErrorCovariance = new YoDouble("velocityErrorCovariance", registry);
private final YoDouble contactVelocityCovariance = new YoDouble("contactVelocityCovariance", registry);
- private final YoDouble contactOrientationCovariance = new YoDouble("contactOrientationCovariance", registry);
+ private final YoDouble contactAccelerationCovariance = new YoDouble("contactAccelerationMeasureCovariance", registry);
+
+ private final YoDouble contactThreshold = new YoDouble("contactVarianceThreshold", registry);
+
// Internal variables for state holding
private final YoFrameVector3D yoBaseAngularVelocityMeasurement;
@@ -86,7 +89,7 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final List processModels = new ArrayList<>();
private final List measurementModels = new ArrayList<>();
- private final List observations = new ArrayList<>();
+ private final List observationModels = new ArrayList<>();
private final YoFrameQuaternion filteredBaseOrientation;
private final YoFramePoint3D filteredBaseTranslation;
@@ -103,7 +106,6 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final DMatrixRMaj processCovariance;
private final DMatrixRMaj measurementCovariance;
-
private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
@@ -116,24 +118,24 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
{
super(OdometryIndexHelper.getStateSizePerLink() * (1 + feetIMUs.size()), OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size());
- // INitialize covariance
+ // Initialize covariance
processCovariance = new DMatrixRMaj(getStateSize(), getStateSize());
measurementCovariance = new DMatrixRMaj(getMeasurementSize(), getMeasurementSize());
MatrixTools.setDiagonal(processCovariance, 1e-2);
MatrixTools.setDiagonal(measurementCovariance, 1e-3);
- setProcessCovariance(processCovariance);
- setMeasurementCovariance(measurementCovariance);
- translationCovariance.set(1e-4);
- velocityCovariance.set(1e-3);
- orientationCovariance.set(1e-4);
- biasCovariance.set(1e-6);
+ translationCovariance.set(1e-3);
+ velocityCovariance.set(1e-2);
+ orientationCovariance.set(1e-2);
+ biasCovariance.set(1e-4);
measuredTranslationCovariance.set(1e-5);
- measuredOrientationCovariance.set(1e-5);
- velocityErrorCovariance.set(1e-4);
+ measuredOrientationCovariance.set(1e-2);
+ velocityErrorCovariance.set(1e-3);
contactVelocityCovariance.set(1e-5);
- contactOrientationCovariance.set(1e-6);
+ contactAccelerationCovariance.set(1.0);
+
+ contactThreshold.set(Double.MAX_VALUE);
this.baseIMU = baseIMU;
this.feetIMUs = feetIMUs;
@@ -146,41 +148,37 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
CMatrix = new DMatrixRMaj(getMeasurementSize(), getStateSize());
observationVector = new DMatrixRMaj(OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size(), 1);
- gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
+ gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, Math.abs(gravitationalAcceleration)));
- baseMeasurement = new MeasuredVariables("base", baseIMU, imuBiasProvider, registry);
- baseProcessState = new StateVariables("baseProcessState");
- basePredictedState = new StateVariables("basePredicted");
- processModels.add(new ProcessModel(baseMeasurement,
- baseProcessState,
+ baseMeasurement = new MeasuredVariables("base", baseIMU, baseIMU, imuBiasProvider, registry);
+ baseProcessState = new StateVariables("baseProcessState", baseIMU.getMeasurementFrame(), registry);
+ basePredictedState = new StateVariables("basePredicted", baseIMU.getMeasurementFrame(), registry);
+ processModels.add(new ProcessModel(baseProcessState,
basePredictedState,
cancelGravityFromAccelerationMeasurement,
gravityVector,
estimatorDT));
for (int i = 0; i < feetIMUs.size(); i++)
{
- MeasuredVariables footMeasurements = new MeasuredVariables("foot" + i, feetIMUs.get(i), imuBiasProvider, registry);
- StateVariables footProcessState = new StateVariables("foot" + i + "ProcessState");
- StateVariables footPredictedState = new StateVariables("foot" + i + "PredictedState");
+ MeasuredVariables footMeasurements = new MeasuredVariables("foot" + i, baseIMU, feetIMUs.get(i), imuBiasProvider, registry);
+ StateVariables footProcessState = new StateVariables("foot" + i + "ProcessState", feetIMUs.get(i).getMeasurementFrame(), registry);
+ StateVariables footPredictedState = new StateVariables("foot" + i + "PredictedState", feetIMUs.get(i).getMeasurementFrame(), registry);
feetMeasurements.add(footMeasurements);
feetProcessState.add(footProcessState);
feetPredictedState.add(footPredictedState);
- observations.add(new Observation("foot" + i + "Observation", baseIMU, feetIMUs.get(i), footMeasurements, registry));
-
- processModels.add(new ProcessModel(footMeasurements,
- footProcessState,
+ observationModels.add(new ObservationModel(footMeasurements));
+ processModels.add(new ProcessModel(footProcessState,
footPredictedState,
cancelGravityFromAccelerationMeasurement,
gravityVector,
estimatorDT));
measurementModels.add(new MeasurementModel("foot" + i,
basePredictedState,
- baseMeasurement,
footPredictedState,
- baseIMU,
- feetIMUs.get(i),
+ footMeasurements,
gravityVector,
+ contactThreshold,
registry));
}
@@ -201,248 +199,59 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
parentRegistry.addChild(registry);
}
- private static class ProcessModel
+ private void initialize()
{
- // State providers
- private final MeasuredVariables measuredVariables;
- private final StateVariables currentState;
- private final StateVariables predictedState;
- private final BooleanProvider cancelGravityFromAccelerationMeasurement;
- private final FrameVector3DReadOnly gravityVector;
- private final double estimatorDt;
+ baseProcessState.initialize();
+ for (int i = 0; i < feetProcessState.size(); i++)
+ feetProcessState.get(i).initialize();
- // Temp variables
- private final Vector3D unbiasedAcceleration = new Vector3D();
- private final Vector3D unbiasedAngularVelocity = new Vector3D();
- private final Vector3D integratedVelocity = new Vector3D();
- private final Quaternion integratedRotation = new Quaternion();
-
- public ProcessModel(MeasuredVariables measuredVariables,
- StateVariables processState,
- StateVariables predictedState,
- BooleanProvider cancelGravityFromAccelerationMeasurement,
- FrameVector3DReadOnly gravityVector,
- double estimatorDt)
+ // update the predicted state from the yo variablized state
+ baseProcessState.get(OdometryIndexHelper.getBasePositionIndex(), state);
+ for (int i = 0; i < feetIMUs.size(); i++)
{
- this.measuredVariables = measuredVariables;
- this.currentState = processState;
- this.predictedState = predictedState;
- this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
- this.gravityVector = gravityVector;
- this.estimatorDt = estimatorDt;
+ feetProcessState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), state);
}
- public void update()
- {
- unbiasedAcceleration.sub(measuredVariables.linearAccelerationMeasurement, currentState.accelBias);
- unbiasedAngularVelocity.sub(measuredVariables.gyroMeasurement, currentState.gyroBias);
- // transform the acceleration to world
- currentState.orientation.transform(unbiasedAcceleration);
- if (cancelGravityFromAccelerationMeasurement.getValue())
- {
- // remove gravity from acceleration
- unbiasedAcceleration.sub(gravityVector);
- }
-
- // First order integration of the position state
- predictedState.translation.scaleAdd(estimatorDt, currentState.linearVelocity, currentState.translation);
-
- // Integration of the velocity state
- predictedState.linearVelocity.scaleAdd(estimatorDt, unbiasedAcceleration, currentState.linearVelocity);
-
- // First order integration of the angular state along the SO(3) manifold
- integratedVelocity.setAndScale(estimatorDt, unbiasedAngularVelocity);
- OdometryIndexHelper.toQuaternionFromRotationVector(integratedVelocity, integratedRotation);
- predictedState.orientation.multiply(currentState.orientation, integratedRotation);
-
- // Propagate the bias
- predictedState.accelBias.set(currentState.accelBias);
- predictedState.gyroBias.set(currentState.gyroBias);
- }
+ initializeState(state);
}
- private static class Observation
+ static class MeasuredVariables
{
// State providers
private final IMUSensorReadOnly baseIMU;
- private final IMUSensorReadOnly footIMU;
- private final MeasuredVariables footMeasurements;
-
- // State variables
- private final YoVector3D footPositionFromKinematics;
-
- // Temp variables
- private final FramePoint3D footPosition = new FramePoint3D();
-
- public Observation(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOnly footIMU, MeasuredVariables footMeasurements, YoRegistry registry)
- {
- this.baseIMU = baseIMU;
- this.footIMU = footIMU;
- this.footMeasurements = footMeasurements;
-
- footPositionFromKinematics = new YoVector3D(prefix + "PositionFromKinematics", registry);
- }
-
- public void update()
- {
- footPosition.setToZero(footIMU.getMeasurementFrame());
- footPosition.changeFrame(baseIMU.getMeasurementFrame());
-
- footPositionFromKinematics.set(footPosition);
- }
-
- public void get(int startRow, DMatrixRMaj observation)
- {
- footPositionFromKinematics.get(startRow, observation);
- footMeasurements.linearAccelerationMeasurement.get(startRow + 6, observation);
- }
- }
-
- private static class MeasurementModel
- {
- private final StateVariables baseState;
- private final MeasuredVariables baseMeasurement;
- private final StateVariables footState;
- private final IMUSensorReadOnly baseIMU;
- private final IMUSensorReadOnly footIMU;
- private final FrameVector3DReadOnly gravityVector;
-
- // State variables
- public final YoVector3D footPredictedRelativePosition;
- public final YoVector3D footPredictedRelativeOrientation;
- public final YoVector3D footPredictedLinearVelocityError;
- public final YoVector3D footPredictedLinearVelocity;
- public final YoVector3D footPredictedGravityVector;
-
- public final YoBoolean isInContact;
-
- // Temp variables
- private final FramePose3D footPose = new FramePose3D();
- private final Quaternion footOrientationError = new Quaternion();
- private final Vector3D velocityError = new Vector3D();
- private final Vector3D unbiasedAngularVelocity = new Vector3D();
-
- public MeasurementModel(String prefix,
- StateVariables baseState,
- MeasuredVariables baseMeasurement,
- StateVariables footState,
- IMUSensorReadOnly baseIMU,
- IMUSensorReadOnly footIMU,
- FrameVector3DReadOnly gravityVector,
- YoRegistry registry)
- {
- this.baseState = baseState;
- this.baseMeasurement = baseMeasurement;
- this.footState = footState;
- this.baseIMU = baseIMU;
- this.footIMU = footIMU;
- this.gravityVector = gravityVector;
-
- footPredictedRelativePosition = new YoVector3D(prefix + "PredictedRelativePosition", registry);
- footPredictedRelativeOrientation = new YoVector3D(prefix + "PredictedRelativeOrientation", registry);
- footPredictedLinearVelocityError = new YoVector3D(prefix + "PredictedLinearVelocityError", registry);
- footPredictedLinearVelocity = new YoVector3D(prefix + "PredictedLinearVelocity", registry);
- footPredictedGravityVector = new YoVector3D(prefix + "PredictedGravityVector", registry);
- isInContact = new YoBoolean(prefix + "IsInContact", registry);
- }
-
- /**
- * This is equation 35
- */
- public void update(DMatrixRMaj footCovariance)
- {
- isInContact.set(isFootInContact(footCovariance));
-
- // First row. Update the predicted foot position of the foot relative to the base in the base frame
- footPredictedRelativePosition.sub(footState.translation, baseState.translation);
- baseState.orientation.transform(footPredictedRelativePosition);
-
- // Second row. get the foot pose relative to the base link, which should be entirely based on kinematics
- footPose.setToZero(footIMU.getMeasurementFrame());
- footPose.changeFrame(baseIMU.getMeasurementFrame());
-
- // Update the predicted foot orientation of the foot relative to the base in the base frame
- footOrientationError.set(footPose.getOrientation());
- footOrientationError.multiplyConjugateBoth(baseState.orientation);
- footOrientationError.multiply(footState.orientation);
- OdometryIndexHelper.logMap(footOrientationError, footPredictedRelativeOrientation);
-
- // Compute the linear velocity error in the base frame
- unbiasedAngularVelocity.sub(baseMeasurement.gyroMeasurement, baseState.gyroBias);
- velocityError.sub(baseState.linearVelocity, footState.linearVelocity);
- baseState.orientation.inverseTransform(velocityError);
-
- // FIXME this likely changes based on the contact conditions
- footPredictedLinearVelocityError.cross(unbiasedAngularVelocity, footPose.getPosition());
- footPredictedLinearVelocityError.scale(-1.0);
- footPredictedLinearVelocityError.sub(velocityError);
-
- // FIXME get the estimated foot linear velocity from the kinematics, and add it to here. That is, get the one using the MovingReferenceFrame, relative to the base IMU.
-
- if (isInContact.getBooleanValue())
- return;
-
- // Fourth row. Set the predicted linear velocity
- footPredictedLinearVelocity.set(footState.linearVelocity);
-
- // Fifth row. Set the predicted gravity vector
- footState.orientation.transform(gravityVector, footPredictedGravityVector);
- footPredictedGravityVector.add(footState.accelBias);
- }
-
- private final DMatrixRMaj vector = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj tempVector = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj tempScalar = new DMatrixRMaj(3, 1);
-
- private boolean isFootInContact(DMatrixRMaj footCovariance)
- {
- footState.translation.get(vector);
-
- CommonOps_DDRM.mult(footCovariance, vector, tempVector);
- CommonOps_DDRM.multTransA(vector, tempVector, tempScalar);
-
- // tests the mahalonobis distance of the foot velocity being below a certain threshold.
- return tempScalar.get(0, 0) < 0.2; // FIXME extract magic number
- }
-
- public void get(int start, DMatrixRMaj measurementToPack)
- {
- footPredictedRelativePosition.get(start, measurementToPack);
- footPredictedRelativeOrientation.get(start + 3, measurementToPack);
- footPredictedLinearVelocityError.get(start + 6, measurementToPack);
- if (isInContact.getBooleanValue())
- return;
-
- footPredictedLinearVelocity.get(start + 9, measurementToPack);
- footPredictedGravityVector.get(start + 12, measurementToPack);
- }
- }
-
- private static class MeasuredVariables
- {
- // State providers
private final IMUSensorReadOnly imu;
private final IMUBiasProvider imuBiasProvider;
// State recorders
public final YoFrameVector3D gyroMeasurementInWorld;
public final YoFrameVector3D gyroMeasurement;
- public final YoFrameVector3D linearAccelerationMeasurementInWorld;
- public final YoFrameVector3D linearAccelerationMeasurement;
+ public final YoFrameVector3D accelMeasurementInWorld;
+ public final YoFrameVector3D accelMeasurement;
+
+ public final YoFramePoint3D positionMeasurement;
+ public final YoFrameQuaternion orientationMeasurement;
+ public final YoFrameVector3D linearVelocity;
// Temp variables
private final FrameVector3D linearAcceleration = new FrameVector3D();
private final FrameVector3D angularVelocity = new FrameVector3D();
+ private final Twist twist = new Twist();
- public MeasuredVariables(String prefix, IMUSensorReadOnly imu, IMUBiasProvider imuBiasProvider, YoRegistry registry)
+ public MeasuredVariables(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOnly imu, IMUBiasProvider imuBiasProvider, YoRegistry registry)
{
+ this.baseIMU = baseIMU;
this.imu = imu;
this.imuBiasProvider = imuBiasProvider;
gyroMeasurementInWorld = new YoFrameVector3D(prefix + "GyroMeasurementInWorld", worldFrame, registry);
gyroMeasurement = new YoFrameVector3D(prefix + "GyroMeasurement", imu.getMeasurementFrame(), registry);
- linearAccelerationMeasurementInWorld = new YoFrameVector3D(prefix + "LinearAccelerationMeasurementInWorld", worldFrame, registry);
- linearAccelerationMeasurement = new YoFrameVector3D(prefix + "LinearAccelerationMeasurement", imu.getMeasurementFrame(), registry);
+ accelMeasurementInWorld = new YoFrameVector3D(prefix + "AccelMeasurementInWorld", worldFrame, registry);
+ accelMeasurement = new YoFrameVector3D(prefix + "AccelMeasurement", imu.getMeasurementFrame(), registry);
+
+ positionMeasurement = new YoFramePoint3D(prefix + "PositionMeasurement", baseIMU.getMeasurementFrame(), registry);
+ orientationMeasurement = new YoFrameQuaternion(prefix + "OrientationMeasurement", baseIMU.getMeasurementFrame(), registry);
+
+ linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
}
public void update()
@@ -464,12 +273,19 @@ public void update()
linearAcceleration.setReferenceFrame(imu.getMeasurementFrame());
linearAcceleration.sub(accelRawInput, accelBiasInput);
- linearAccelerationMeasurementInWorld.setMatchingFrame(linearAcceleration);
- linearAccelerationMeasurement.setMatchingFrame(linearAcceleration);
+ accelMeasurementInWorld.setMatchingFrame(linearAcceleration);
+ accelMeasurement.setMatchingFrame(linearAcceleration);
+
+ positionMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
+ orientationMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
+
+ imu.getMeasurementFrame().getTwistRelativeToOther(baseIMU.getMeasurementFrame(), twist);
+ twist.changeFrame(worldFrame);
+ linearVelocity.set(twist.getLinearPart());
}
}
- private class StateVariables
+ static class StateVariables
{
public final YoFramePoint3D translation;
public final YoFrameVector3D linearVelocity;
@@ -477,22 +293,51 @@ private class StateVariables
public final YoFrameVector3D accelBias;
public final YoFrameVector3D gyroBias;
- public StateVariables(String prefix)
+ public final YoFrameVector3D unbiasedAccel;
+ public final YoFrameVector3D unbiasedGyro;
+
+ private final MovingReferenceFrame sensorFrame;
+
+ public StateVariables(String prefix, MovingReferenceFrame sensorFrame, YoRegistry registry)
{
+ this.sensorFrame = sensorFrame;
+
translation = new YoFramePoint3D(prefix + "Translation", worldFrame, registry);
linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
orientation = new YoFrameQuaternion(prefix + "Orientation", worldFrame, registry);
- accelBias = new YoFrameVector3D(prefix + "AccelBias", worldFrame, registry);
- gyroBias = new YoFrameVector3D(prefix + "GyroBias", worldFrame, registry);
+ accelBias = new YoFrameVector3D(prefix + "AccelBias", sensorFrame, registry);
+ gyroBias = new YoFrameVector3D(prefix + "GyroBias", sensorFrame, registry);
+ unbiasedAccel = new YoFrameVector3D(prefix + "UnbiasedAccel", sensorFrame, registry);
+ unbiasedGyro = new YoFrameVector3D(prefix + "UnbiasedGyro", sensorFrame, registry);
+ }
+
+ public void initialize()
+ {
+ this.translation.setFromReferenceFrame(sensorFrame);
+ this.orientation.setFromReferenceFrame(sensorFrame);
+ linearVelocity.setMatchingFrame(sensorFrame.getTwistOfFrame().getLinearPart());
+ accelBias.setToZero();
+ gyroBias.setToZero();
}
- public void set(int start, DMatrixRMaj state)
+ public void set(int start, DMatrixRMaj state, MeasuredVariables measuredVariables)
{
translation.set(start + OdometryIndexHelper.getStatePositionIndex(), state);
linearVelocity.set(start + OdometryIndexHelper.getStateVelocityIndex(), state);
orientation.set(start + OdometryIndexHelper.getStateOrientationIndex(), state);
accelBias.set(start + OdometryIndexHelper.getStateAccelerationBiasIndex(), state);
gyroBias.set(start + OdometryIndexHelper.getStateGyroBiasIndex(), state);
+
+ if (OdometryKalmanFilter.includeBias)
+ {
+ unbiasedAccel.sub(measuredVariables.accelMeasurement, accelBias);
+ unbiasedGyro.sub(measuredVariables.gyroMeasurement, gyroBias);
+ }
+ else
+ {
+ unbiasedAccel.set(measuredVariables.accelMeasurement);
+ unbiasedGyro.set(measuredVariables.gyroMeasurement);
+ }
}
public void get(int start, DMatrixRMaj stateToPack)
@@ -505,6 +350,8 @@ public void get(int start, DMatrixRMaj stateToPack)
}
}
+ private boolean initialized = false;
+
public void compute()
{
updateCovariance();
@@ -513,11 +360,17 @@ public void compute()
for (int i = 0; i < feetMeasurements.size(); i++)
feetMeasurements.get(i).update();
- for (int i = 0; i < observations.size(); i++)
+ for (int i = 0; i < observationModels.size(); i++)
+ {
+ observationModels.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), observationVector);
+ }
+
+ if (!initialized)
{
- observations.get(i).update();
- observations.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), observationVector);
+ initialize();
+ initialized = true;
}
+
updateRootJointTwistAngularPart(); // TODO maybe do this after we update everything based on the estimate?
DMatrixRMaj stateEstimate = calculateEstimate(observationVector);
@@ -561,21 +414,20 @@ private void updateCovariance()
for (int i = 0; i < feetIMUs.size(); i++)
{
int index = i * OdometryIndexHelper.getObservationSizePerLink();
- measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
- measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
- measurementCovariance.set(index, index++, measuredTranslationCovariance.getValue());
- measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
- measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
- measurementCovariance.set(index, index++, measuredOrientationCovariance.getValue());
- measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
- measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
- measurementCovariance.set(index, index++, velocityErrorCovariance.getValue());
- measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
- measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
- measurementCovariance.set(index, index++, contactVelocityCovariance.getValue());
- measurementCovariance.set(index, index++, contactOrientationCovariance.getValue());
- measurementCovariance.set(index, index++, contactOrientationCovariance.getValue());
- measurementCovariance.set(index, index, contactOrientationCovariance.getValue());
+ for (int j = 0; j < 3; j++)
+ measurementCovariance.set(index + j, index + j, measuredTranslationCovariance.getValue());
+ index += 3;
+ for (int j = 0; j < 3; j++)
+ measurementCovariance.set(index + j, index + j, measuredOrientationCovariance.getValue());
+ index += 3;
+ for (int j = 0; j < 3; j++)
+ measurementCovariance.set(index + j, index + j, velocityErrorCovariance.getValue());
+ index += 3;
+ for (int j = 0; j < 3; j++)
+ measurementCovariance.set(index + j, index + j, contactVelocityCovariance.getValue());
+ index += 3;
+ for (int j = 0; j < 3; j++)
+ measurementCovariance.set(index + j, index + j, contactAccelerationCovariance.getValue());
}
setProcessCovariance(processCovariance);
@@ -661,10 +513,10 @@ public DMatrixRMaj processModel(DMatrixRMaj state)
predictedState.zero();
// update the yo variables representing the state
- baseProcessState.set(OdometryIndexHelper.getBasePositionIndex(), state);
+ baseProcessState.set(OdometryIndexHelper.getBasePositionIndex(), state, baseMeasurement);
for (int i = 0; i < feetIMUs.size(); i++)
{
- feetProcessState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state);
+ feetProcessState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state, feetMeasurements.get(i));
}
// do the prediction
@@ -672,10 +524,10 @@ public DMatrixRMaj processModel(DMatrixRMaj state)
processModels.get(i).update();
// update the predicted state from the yo variablized state
- basePredictedState.get(OdometryIndexHelper.getBasePositionIndex(), state);
+ basePredictedState.get(OdometryIndexHelper.getBasePositionIndex(), predictedState);
for (int i = 0; i < feetIMUs.size(); i++)
{
- feetPredictedState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), state);
+ feetPredictedState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), predictedState);
}
// return the predicted state
@@ -706,15 +558,13 @@ protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
{
// make sure to call zero, as the compute process model stuff has a bunch of add operators.
AMatrix.zero();
- int rowOffset = 0;
- int colOffset = 0;
- computeProcessJacobian(baseMeasurement, baseProcessState, estimatorDT, rowOffset, colOffset, AMatrix);
+ int offset = 0;
+ ProcessModel.computeProcessJacobian(baseProcessState, estimatorDT, offset, AMatrix);
for (int i = 0; i < feetIMUs.size(); i++)
{
- rowOffset += OdometryIndexHelper.getStateSizePerLink();
- colOffset += OdometryIndexHelper.getStateSizePerLink();
- computeProcessJacobian(feetMeasurements.get(i), feetProcessState.get(i), estimatorDT, rowOffset, colOffset, AMatrix);
+ offset += OdometryIndexHelper.getStateSizePerLink();
+ ProcessModel.computeProcessJacobian(feetProcessState.get(i), estimatorDT, offset, AMatrix);
}
return AMatrix;
@@ -726,12 +576,12 @@ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
// make sure to call zero, as the compute process model stuff has a bunch of add operators.
CMatrix.zero();
int rowOffset = 0;
- int colOffset = 0;
+ int colOffset = OdometryIndexHelper.getStateSizePerLink();
for (int i = 0; i < feetIMUs.size(); i++)
{
- boolean footIsInContact = measurementModels.get(i).isInContact.getBooleanValue();
- computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), rowOffset, CMatrix);
- computeFootMeasurementJacobian(footIsInContact, basePredictedState, feetPredictedState.get(i), gravityVector, rowOffset, colOffset + 15, CMatrix);
+ MeasurementModel measurementModel = measurementModels.get(i);
+ measurementModel.computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetMeasurements.get(i), rowOffset, CMatrix);
+ measurementModel.computeFootMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetMeasurements.get(i), gravityVector, rowOffset, colOffset, CMatrix);
rowOffset += OdometryIndexHelper.getObservationSizePerLink();
colOffset += OdometryIndexHelper.getStateSizePerLink();
}
@@ -739,198 +589,16 @@ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
return CMatrix;
}
- static void toRotationMatrix(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
+ static void l3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
{
- toRotationMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
- }
-
- static void toRotationMatrix(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
- {
- double qx2 = qx * qx;
- double qy2 = qy * qy;
- double qz2 = qz * qz;
- double qs2 = qs * qs;
-
- rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
- rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
- rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
-
- double xy = qx * qy;
- double xz = qx * qz;
- double yz = qy * qz;
- double xw = qx * qs;
- double yw = qy * qs;
- double zw = qz * qs;
-
- rotationMatrix.set(0, 1, 2.0 * (xy - zw));
- rotationMatrix.set(1, 0, 2.0 * (xy + zw));
-
- rotationMatrix.set(0, 2, 2.0 * (xz + yw));
- rotationMatrix.set(2, 0, 2.0 * (xz - yw));
-
- rotationMatrix.set(1, 2, 2.0 * (yz - xw));
- rotationMatrix.set(2, 1, 2.0 * (yz + xw));
- }
-
- static void toRotationMatrixInverse(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
- {
- toRotationMatrixInverse(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
- }
-
- static void toRotationMatrixInverse(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
- {
- double qx2 = qx * qx;
- double qy2 = qy * qy;
- double qz2 = qz * qz;
- double qs2 = qs * qs;
-
- rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
- rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
- rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
-
- double xy = qx * qy;
- double xz = qx * qz;
- double yz = qy * qz;
- double xw = qx * qs;
- double yw = qy * qs;
- double zw = qz * qs;
-
- rotationMatrix.set(1, 0, 2.0 * (xy - zw));
- rotationMatrix.set(0, 1, 2.0 * (xy + zw));
-
- rotationMatrix.set(2, 0, 2.0 * (xz + yw));
- rotationMatrix.set(0, 2, 2.0 * (xz - yw));
-
- rotationMatrix.set(2, 1, 2.0 * (yz - xw));
- rotationMatrix.set(1, 2, 2.0 * (yz + xw));
- }
-
- /**
- * This is equation 39
- */
- static void computeProcessJacobian(MeasuredVariables measuredVariables, StateVariables stateVariables, double estimateDT, int rowOffset, int colOffset, DMatrixRMaj jacobianToPack)
- {
- // Do the partial derivative with respect to the base position state
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + OdometryIndexHelper.getStatePositionIndex(), colOffset + OdometryIndexHelper.getStatePositionIndex());
- MatrixTools.setMatrixBlock(jacobianToPack,
- rowOffset + OdometryIndexHelper.getStatePositionIndex(),
- colOffset + OdometryIndexHelper.getStateVelocityIndex(),
- eye3x3,
- 0,
- 0,
- 3,
- 3,
- estimateDT);
-
- // Do the partial derivative with respect to the base velocity state FIXME lots of garbage
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateVelocityIndex());
-
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- Vector3D unbiasedAcceleration = new Vector3D();
- unbiasedAcceleration.sub(measuredVariables.linearAccelerationMeasurement, stateVariables.accelBias);
- toRotationMatrix(stateVariables.orientation, rotationMatrix);
- toSkewSymmetricMatrix(unbiasedAcceleration, skewMatrix);
- MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex());
-
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateVelocityIndex(), colOffset + OdometryIndexHelper.getStateGyroBiasIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
-
- // Do the partial derivative with respect to the orientation state
- Vector3D unbiasedVelocity = new Vector3D();
- unbiasedVelocity.sub(measuredVariables.gyroMeasurement, stateVariables.gyroBias);
- toSkewSymmetricMatrix(unbiasedVelocity, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
- MatrixTools.addMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateOrientationIndex(), eye3x3, 0, 0, 3, 3, 1.0);
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateOrientationIndex(), colOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, -estimateDT);
-
- // Do the partial derivative with respect to the acceleration bias state
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), colOffset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, 1.0);
- // Do the partial derivative with respect to the gyro bias state
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + OdometryIndexHelper.getStateGyroBiasIndex(), colOffset + OdometryIndexHelper.getStateGyroBiasIndex(), eye3x3, 0, 0, 3, 3, 1.0);
- }
-
- /**
- * This is equation 41
- */
- private void computeBaseMeasurementJacobian(StateVariables baseState, StateVariables footState, int rowOffset, DMatrixRMaj jacobianToPack)
- {
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- toRotationMatrix(baseState.orientation, rotationMatrix);
- CommonOps_DDRM.transpose(rotationMatrix);
-
- Vector3D footPositionError = new Vector3D();
- Vector3D footVelocityError = new Vector3D();
- footPositionError.sub(footState.translation, baseState.translation);
- footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
- baseState.orientation.inverseTransform(footPositionError);
- baseState.orientation.inverseTransform(footVelocityError);
-
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- toSkewSymmetricMatrix(footPositionError, skewMatrix);
-
- // First row
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 0, rotationMatrix, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
-
- // FIXME do the second row
-
- // Third row
- toSkewSymmetricMatrix(footVelocityError, skewMatrix);
-
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset + 6, 3, rotationMatrix, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset+ 6, 6);
- // FIXME do the last term, in the fifth column
- }
-
- /**
- * This is equation 42
- */
- private void computeFootMeasurementJacobian(boolean isInContact, StateVariables baseState, StateVariables footState, FrameVector3DReadOnly gravityVector, int rowOffset, int colOffset, DMatrixRMaj jacobianToPack)
- {
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- toRotationMatrix(baseState.orientation, rotationMatrix);
- CommonOps_DDRM.transpose(rotationMatrix);
-
- Vector3D footPositionError = new Vector3D();
- Vector3D footVelocityError = new Vector3D();
- footPositionError.sub(footState.translation, baseState.translation);
- footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
- baseState.orientation.inverseTransform(footPositionError);
- baseState.orientation.inverseTransform(footVelocityError);
-
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- toSkewSymmetricMatrix(footPositionError, skewMatrix);
-
- // First row
- CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset);
-
- // FIXME do the second row
-
- // Third row
- CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset + 6, colOffset + 3);
-
- if (!isInContact)
- return;
-
- // Fourth row
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + 9, colOffset + 3);
-
- // Fifth row
- Vector3D gravity = new Vector3D(gravityVector);
- footState.orientation.inverseTransform(gravity);
- toSkewSymmetricMatrix(gravityVector, skewMatrix);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset + 12, colOffset + 6);
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset + 12, colOffset + 9);
+ OdometryTools.toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
+ CommonOps_DDRM.addEquals(matrixToPack, quaternion.getS(), eye3x3);
}
- static void toSkewSymmetricMatrix(Vector3DReadOnly vector, DMatrixRMaj matrixToPack)
+ static void r3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
{
- matrixToPack.zero();
- matrixToPack.set(0, 1, -vector.getZ());
- matrixToPack.set(0, 2, vector.getY());
- matrixToPack.set(1, 0, vector.getZ());
- matrixToPack.set(1, 2, -vector.getX());
- matrixToPack.set(2, 0, -vector.getY());
- matrixToPack.set(2, 1, -vector.getX());
+ OdometryTools.toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
+ CommonOps_DDRM.scale(-1.0, matrixToPack);
+ CommonOps_DDRM.addEquals(matrixToPack, quaternion.getS(), eye3x3);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
new file mode 100644
index 000000000000..0cc8fb159d74
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
@@ -0,0 +1,90 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.euclid.tuple3D.interfaces.Tuple3DReadOnly;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+
+public class OdometryTools
+{
+ static void toRotationMatrixInverse(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
+ {
+ double qx2 = qx * qx;
+ double qy2 = qy * qy;
+ double qz2 = qz * qz;
+ double qs2 = qs * qs;
+
+ rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
+ rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
+ rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
+
+ double xy = qx * qy;
+ double xz = qx * qz;
+ double yz = qy * qz;
+ double xw = qx * qs;
+ double yw = qy * qs;
+ double zw = qz * qs;
+
+ rotationMatrix.set(1, 0, 2.0 * (xy - zw));
+ rotationMatrix.set(0, 1, 2.0 * (xy + zw));
+
+ rotationMatrix.set(2, 0, 2.0 * (xz + yw));
+ rotationMatrix.set(0, 2, 2.0 * (xz - yw));
+
+ rotationMatrix.set(2, 1, 2.0 * (yz - xw));
+ rotationMatrix.set(1, 2, 2.0 * (yz + xw));
+ }
+
+ static void toRotationMatrixInverse(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
+ {
+ toRotationMatrixInverse(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
+ }
+
+ static void toSkewSymmetricMatrix(double x, double y, double z, DMatrixRMaj matrixToPack)
+ {
+ matrixToPack.zero();
+ matrixToPack.set(0, 1, -z);
+ matrixToPack.set(0, 2, y);
+ matrixToPack.set(1, 0, z);
+ matrixToPack.set(1, 2, -x);
+ matrixToPack.set(2, 0, -y);
+ matrixToPack.set(2, 1, x);
+ }
+
+ static void toSkewSymmetricMatrix(Tuple3DReadOnly vector, DMatrixRMaj matrixToPack)
+ {
+ toSkewSymmetricMatrix(vector.getX(), vector.getY(), vector.getZ(), matrixToPack);
+ }
+
+ static void toRotationMatrix(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
+ {
+ double qx2 = qx * qx;
+ double qy2 = qy * qy;
+ double qz2 = qz * qz;
+ double qs2 = qs * qs;
+
+ rotationMatrix.set(0, 0, qx2 - qy2 - qz2 + qs2);
+ rotationMatrix.set(1, 1, -qx2 + qy2 - qz2 + qs2);
+ rotationMatrix.set(2, 2, -qx2 - qy2 + qz2 + qs2);
+
+ double xy = qx * qy;
+ double xz = qx * qz;
+ double yz = qy * qz;
+ double xw = qx * qs;
+ double yw = qy * qs;
+ double zw = qz * qs;
+
+ rotationMatrix.set(0, 1, 2.0 * (xy - zw));
+ rotationMatrix.set(1, 0, 2.0 * (xy + zw));
+
+ rotationMatrix.set(0, 2, 2.0 * (xz + yw));
+ rotationMatrix.set(2, 0, 2.0 * (xz - yw));
+
+ rotationMatrix.set(1, 2, 2.0 * (yz - xw));
+ rotationMatrix.set(2, 1, 2.0 * (yz + xw));
+ }
+
+ static void toRotationMatrix(QuaternionReadOnly quaternion, DMatrixRMaj rotationMatrix)
+ {
+ toRotationMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
new file mode 100644
index 000000000000..ad068252645a
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
@@ -0,0 +1,123 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.tools.QuaternionTools;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.StateVariables;
+import us.ihmc.yoVariables.providers.BooleanProvider;
+
+class ProcessModel
+{
+ private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
+
+ // State providers
+ private final StateVariables currentState;
+ private final StateVariables predictedState;
+ private final BooleanProvider cancelGravityFromAccelerationMeasurement;
+ private final FrameVector3DReadOnly gravityVector;
+ private final double estimatorDt;
+
+ // Temp variables
+ private final Vector3D integratedVelocity = new Vector3D();
+ private final Vector3D unbiasedAcceleration = new Vector3D();
+ private final Quaternion integratedRotation = new Quaternion();
+
+ public ProcessModel(StateVariables processState,
+ StateVariables predictedState,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
+ FrameVector3DReadOnly gravityVector,
+ double estimatorDt)
+ {
+ this.currentState = processState;
+ this.predictedState = predictedState;
+ this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
+ this.gravityVector = gravityVector;
+ this.estimatorDt = estimatorDt;
+ }
+
+ public void update()
+ {
+ // transform the acceleration to world
+ unbiasedAcceleration.set(currentState.unbiasedAccel);
+ currentState.orientation.transform(unbiasedAcceleration);
+ if (cancelGravityFromAccelerationMeasurement.getValue())
+ {
+ // remove gravity from acceleration
+ unbiasedAcceleration.sub(gravityVector);
+ }
+
+ // First order integration of the position state
+ predictedState.translation.scaleAdd(estimatorDt, currentState.linearVelocity, currentState.translation);
+
+ // Integration of the velocity state
+ predictedState.linearVelocity.scaleAdd(estimatorDt, unbiasedAcceleration, currentState.linearVelocity);
+
+ // First order integration of the angular state along the SO(3) manifold
+ integratedVelocity.setAndScale(estimatorDt, currentState.unbiasedGyro);
+ integratedRotation.setRotationVector(integratedVelocity);
+ QuaternionTools.multiply(currentState.orientation, integratedRotation, predictedState.orientation);
+
+ // Propagate the bias
+ predictedState.accelBias.set(currentState.accelBias);
+ predictedState.gyroBias.set(currentState.gyroBias);
+ }
+
+ /**
+ * This is equation 39
+ */
+ public static void computeProcessJacobian(StateVariables stateVariables, double estimateDT, int offset, DMatrixRMaj jacobianToPack)
+ {
+ // Do the partial derivative with respect to the base position state
+ int rowStart = offset + OdometryIndexHelper.getStatePositionIndex();
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStatePositionIndex());
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateVelocityIndex(), eye3x3, 0, 0, 3, 3, estimateDT);
+
+ // Do the partial derivative with respect to the base velocity state FIXME lots of garbage
+ rowStart = offset + OdometryIndexHelper.getStateVelocityIndex();
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateVelocityIndex());
+
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ OdometryTools.toRotationMatrix(stateVariables.orientation, rotationMatrix);
+ OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedAccel, skewMatrix);
+ MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex());
+
+ if (OdometryKalmanFilter.includeBias)
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateGyroBiasIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+
+ // Do the partial derivative with respect to the orientation state
+ rowStart = offset + OdometryIndexHelper.getStateOrientationIndex();
+ OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedGyro, skewMatrix);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+ if (OdometryKalmanFilter.includeBias)
+ {
+ MatrixTools.addMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex(), eye3x3, 0, 0, 3, 3, 1.0);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, -estimateDT);
+ }
+
+ // Do the partial derivative with respect to the acceleration bias state
+ MatrixTools.setMatrixBlock(jacobianToPack,
+ offset + OdometryIndexHelper.getStateAccelerationBiasIndex(),
+ offset + OdometryIndexHelper.getStateAccelerationBiasIndex(),
+ eye3x3,
+ 0,
+ 0,
+ 3,
+ 3,
+ 1.0);
+ // Do the partial derivative with respect to the gyro bias state
+ MatrixTools.setMatrixBlock(jacobianToPack,
+ offset + OdometryIndexHelper.getStateGyroBiasIndex(),
+ offset + OdometryIndexHelper.getStateGyroBiasIndex(),
+ eye3x3,
+ 0,
+ 0,
+ 3,
+ 3,
+ 1.0);
+ }
+}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
deleted file mode 100644
index a53fb87195d4..000000000000
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
-
-
-import org.ejml.EjmlUnitTests;
-import org.ejml.data.DMatrixRMaj;
-import org.ejml.dense.row.CommonOps_DDRM;
-import org.junit.jupiter.api.Test;
-import us.ihmc.euclid.matrix.RotationMatrix;
-import us.ihmc.euclid.tools.EuclidCoreRandomTools;
-import us.ihmc.euclid.tuple4D.Quaternion;
-
-import java.util.Random;
-
-public class OdometryKalmanFilterTest
-{
- @Test
- public void testToRotationMatrix()
- {
- Random random = new Random(1738L);
-
- for (int i = 0; i < 1000; i++)
- {
- Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
-
- OdometryKalmanFilter.toRotationMatrix(quaternion, rotationMatrix);
-
- RotationMatrix mat = new RotationMatrix(quaternion);
- DMatrixRMaj expected = new DMatrixRMaj(3, 3);
- mat.get(expected);
-
- EjmlUnitTests.assertEquals(expected, rotationMatrix, 1e-8);
- }
- }
-
- @Test
- public void testToRotationMatrixInverse()
- {
- Random random = new Random(1738L);
-
- for (int i = 0; i < 1000; i++)
- {
- Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
- DMatrixRMaj rotationMatrixInverse = new DMatrixRMaj(3, 3);
-
- OdometryKalmanFilter.toRotationMatrixInverse(quaternion, rotationMatrixInverse);
-
- RotationMatrix mat = new RotationMatrix(quaternion);
- DMatrixRMaj rotationExpected = new DMatrixRMaj(3, 3);
- mat.get(rotationExpected);
-
- CommonOps_DDRM.invert(rotationExpected);
-
- EjmlUnitTests.assertEquals(rotationExpected, rotationMatrixInverse, 1e-8);
- }
- }
-}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
new file mode 100644
index 000000000000..76d58f545c32
--- /dev/null
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
@@ -0,0 +1,124 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+
+import org.ejml.EjmlUnitTests;
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import org.junit.jupiter.api.Test;
+import us.ihmc.euclid.matrix.RotationMatrix;
+import us.ihmc.euclid.tools.EuclidCoreRandomTools;
+import us.ihmc.euclid.tools.EuclidCoreTestTools;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple4D.Quaternion;
+
+import java.util.Random;
+
+public class OdometryToolsTest
+{
+ @Test
+ public void testToSkewSymmetric()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Vector3D a = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D b = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D cExpected = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D c = EuclidCoreRandomTools.nextVector3D(random);
+
+ cExpected.cross(a, b);
+
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+
+ DMatrixRMaj bVector = new DMatrixRMaj(3, 1);
+ b.get(bVector);
+ DMatrixRMaj cVector = new DMatrixRMaj(3, 1);
+ OdometryTools.toSkewSymmetricMatrix(a, skewMatrix);
+
+ CommonOps_DDRM.mult(skewMatrix, bVector, cVector);
+ c.set(cVector);
+
+ EuclidCoreTestTools.assertEquals(cExpected, c, 1e-8);
+ }
+ }
+
+ @Test
+ public void testTransform()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion a = EuclidCoreRandomTools.nextQuaternion(random);
+ Vector3D b = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D cExpected = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D c = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D cInverseExpected = EuclidCoreRandomTools.nextVector3D(random);
+ Vector3D cInverse = EuclidCoreRandomTools.nextVector3D(random);
+
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj rotationMatrixTranspose = new DMatrixRMaj(3, 3);
+ DMatrixRMaj bVector = new DMatrixRMaj(3, 1);
+ DMatrixRMaj cVector = new DMatrixRMaj(3, 1);
+ DMatrixRMaj cInverseVector = new DMatrixRMaj(3, 1);
+ OdometryTools.toRotationMatrix(a, rotationMatrix);
+ CommonOps_DDRM.transpose(rotationMatrix, rotationMatrixTranspose);
+ OdometryTools.toRotationMatrix(a, rotationMatrix);
+ b.get(bVector);
+
+ CommonOps_DDRM.mult(rotationMatrix, bVector, cVector);
+ CommonOps_DDRM.mult(rotationMatrixTranspose, bVector, cInverseVector);
+ c.set(cVector);
+ cInverse.set(cInverseVector);
+
+ a.transform(b, cExpected);
+ a.inverseTransform(b, cInverseExpected);
+
+ EuclidCoreTestTools.assertEquals(cExpected, c, 1e-5);
+ EuclidCoreTestTools.assertEquals(cInverseExpected, cInverse, 1e-5);
+ }
+ }
+
+ @Test
+ public void testToRotationMatrix()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+
+ OdometryTools.toRotationMatrix(quaternion, rotationMatrix);
+
+ RotationMatrix mat = new RotationMatrix(quaternion);
+ DMatrixRMaj expected = new DMatrixRMaj(3, 3);
+ mat.get(expected);
+
+ EjmlUnitTests.assertEquals(expected, rotationMatrix, 1e-8);
+ }
+ }
+
+ @Test
+ public void testToRotationMatrixInverse()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion quaternion = EuclidCoreRandomTools.nextQuaternion(random);
+ DMatrixRMaj rotationMatrixInverse = new DMatrixRMaj(3, 3);
+
+ OdometryTools.toRotationMatrixInverse(quaternion, rotationMatrixInverse);
+
+ RotationMatrix mat = new RotationMatrix(quaternion);
+ DMatrixRMaj rotationExpected = new DMatrixRMaj(3, 3);
+ mat.get(rotationExpected);
+
+ CommonOps_DDRM.invert(rotationExpected);
+
+ EjmlUnitTests.assertEquals(rotationExpected, rotationMatrixInverse, 1e-8);
+ }
+ }
+}
From a3cda93b44a1969e4e6efa6b1bc0b41c8e701c61 Mon Sep 17 00:00:00 2001
From: Robert
Date: Fri, 29 Aug 2025 17:11:23 -0500
Subject: [PATCH 06/18] started doing more cleanup
---
.../odomEKF/MeasurementModel.java | 29 +++++++++--------
.../odomEKF/ObservationModel.java | 4 +--
.../odomEKF/OdometryIndexHelper.java | 13 ++++++++
.../odomEKF/OdometryKalmanFilter.java | 31 +++++++++++++------
4 files changed, 52 insertions(+), 25 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index 6aca27f48f58..182c93e374a7 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -15,6 +15,8 @@
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
class MeasurementModel
{
private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
@@ -118,15 +120,15 @@ private boolean isFootInContact(DMatrixRMaj footCovariance)
public void get(int start, DMatrixRMaj measurementToPack)
{
- footPredictedRelativePosition.get(start, measurementToPack);
- footPredictedRelativeOrientationError.get(start + 3, measurementToPack);
- footPredictedLinearVelocityError.get(start + 6, measurementToPack);
+ footPredictedRelativePosition.get(start + measurementRelativeTranslationIndex, measurementToPack);
+ footPredictedRelativeOrientationError.get(start + measurementRelativeOrientationErrorIndex, measurementToPack);
+ footPredictedLinearVelocityError.get(start + measurementRelativeVelocityIndex, measurementToPack);
if (isInContact.getBooleanValue())
return;
- footPredictedLinearVelocity.get(start + 9, measurementToPack);
- footPredictedGravityVector.get(start + 12, measurementToPack);
+ footPredictedLinearVelocity.get(start + measurementContactVelocityIndex, measurementToPack);
+ footPredictedGravityVector.get(start + measurementAccelIndex, measurementToPack);
}
/**
@@ -155,30 +157,31 @@ public void computeBaseMeasurementJacobian(StateVariables baseState,
OdometryTools.toSkewSymmetricMatrix(footPositionError, skewMatrix);
// First row
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 0, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
+ int row = rowOffset + measurementRelativeTranslationIndex;
+ MatrixTools.setMatrixBlock(jacobianToPack, row, stateTranslationIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, stateOrientationIndex);
// Second row
- rowOffset += 3;
+ row = rowOffset + measurementRelativeOrientationErrorIndex;
DMatrixRMaj left = new DMatrixRMaj(3, 3);
DMatrixRMaj right = new DMatrixRMaj(3, 3);
Quaternion orientation = new Quaternion(footState.orientation);
orientation.multiplyConjugateThis(baseState.orientation);
OdometryKalmanFilter.l3Operator(orientation, left);
OdometryKalmanFilter.r3Operator(footMeasurements.orientationMeasurement, right);
- MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, rowOffset, 6);
+ MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, row, stateOrientationIndex);
// Third row
- rowOffset += 3;
+ row = rowOffset + measurementRelativeVelocityIndex;
OdometryTools.toSkewSymmetricMatrix(footVelocityError, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 3, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, 6);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, stateLinearVelocityIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, stateOrientationIndex);
if (OdometryKalmanFilter.includeBias)
{
OdometryTools.toSkewSymmetricMatrix(footMeasurements.positionMeasurement, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, rowOffset, 12, skewMatrix, 0, 0, 3, 3, -1.0);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, stateGyroBiasIndex, skewMatrix, 0, 0, 3, 3, -1.0);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
index 617db46f5a41..8cc452e5785c 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
@@ -15,7 +15,7 @@ public ObservationModel(MeasuredVariables footMeasurements)
public void get(int startRow, DMatrixRMaj observation)
{
- footMeasurements.positionMeasurement.get(startRow, observation);
- footMeasurements.accelMeasurement.get(startRow + 6, observation);
+ footMeasurements.positionMeasurement.get(startRow + OdometryIndexHelper.measurementRelativeTranslationIndex, observation);
+ footMeasurements.accelMeasurement.get(startRow + OdometryIndexHelper.measurementAccelIndex, observation);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
index 939b97161e6f..5c5c39d954d9 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
@@ -8,6 +8,19 @@
public class OdometryIndexHelper
{
+ static final int stateSizePerLink = 16;
+ static final int stateTranslationIndex = 0;
+ static final int stateLinearVelocityIndex = 3;
+ static final int stateOrientationIndex = 6;
+ static final int stateAccelBiasIndex = 10;
+ static final int stateGyroBiasIndex = 13;
+
+ static final int measurementRelativeTranslationIndex = 0;
+ static final int measurementRelativeOrientationErrorIndex = 3;
+ static final int measurementRelativeVelocityIndex = 6;
+ static final int measurementContactVelocityIndex = 9;
+ static final int measurementAccelIndex = 12;
+
public static int getStateSizePerLink()
{
return 16;
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index db8577b5a8eb..3dff3693bff4 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -124,16 +124,27 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
MatrixTools.setDiagonal(processCovariance, 1e-2);
MatrixTools.setDiagonal(measurementCovariance, 1e-3);
- translationCovariance.set(1e-3);
- velocityCovariance.set(1e-2);
- orientationCovariance.set(1e-2);
- biasCovariance.set(1e-4);
-
- measuredTranslationCovariance.set(1e-5);
- measuredOrientationCovariance.set(1e-2);
- velocityErrorCovariance.set(1e-3);
- contactVelocityCovariance.set(1e-5);
- contactAccelerationCovariance.set(1.0);
+// translationCovariance.set(1e-3);
+// velocityCovariance.set(1e-2);
+// orientationCovariance.set(1e-2);
+// biasCovariance.set(1e-4);
+//
+// measuredTranslationCovariance.set(1e-5);
+// measuredOrientationCovariance.set(1e-2);
+// velocityErrorCovariance.set(1e-3);
+// contactVelocityCovariance.set(1e-5);
+// contactAccelerationCovariance.set(1.0);
+
+ translationCovariance.set(1e-4);
+ velocityCovariance.set(1e-4);
+ orientationCovariance.set(1e-4);
+ biasCovariance.set(1e-4);
+
+ measuredTranslationCovariance.set(1e-4);
+ measuredOrientationCovariance.set(1e-4);
+ velocityErrorCovariance.set(1e-4);
+ contactVelocityCovariance.set(1e-4);
+ contactAccelerationCovariance.set(1e-4);
contactThreshold.set(Double.MAX_VALUE);
From acb972c0d695bd36fd4a28265d6a88577e06b69d Mon Sep 17 00:00:00 2001
From: Robert
Date: Sun, 31 Aug 2025 23:55:08 -0500
Subject: [PATCH 07/18] did lots of little bug fixes
---
.../ExtendedKalmanFilter.java | 58 ++-
.../odomEKF/MeasurementModel.java | 212 +++++------
.../odomEKF/MeasurementResidualVariables.java | 35 ++
.../odomEKF/MeasurementVariables.java | 40 ++
.../odomEKF/ObservationModel.java | 12 +-
.../odomEKF/OdometryIndexHelper.java | 139 +------
.../odomEKF/OdometryKalmanFilter.java | 341 ++++++------------
.../odomEKF/OdometryTools.java | 53 +++
.../odomEKF/ProcessModel.java | 68 ++--
.../odomEKF/SensedVariables.java | 91 +++++
.../odomEKF/StateCorrectionVariables.java | 39 ++
.../odomEKF/StateVariables.java | 78 ++++
12 files changed, 638 insertions(+), 528 deletions(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementResidualVariables.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateCorrectionVariables.java
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index a330c7c34f87..4b65b5f4cad3 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -82,7 +82,7 @@ public abstract class ExtendedKalmanFilter
/**
* The residual 'y' between the actual observation (i.e. direct from the sensors), and the predicted measurement from the prediction step of the filter.
*/
- private final DMatrixRMaj measurementResidual;
+ protected final DMatrixRMaj measurementResidual;
/**
* Container variable used for garbage free operations.
*/
@@ -108,7 +108,10 @@ public abstract class ExtendedKalmanFilter
* Container variable used for garbage free operations.
*/
private final DMatrixRMaj kalmanGainContainer;
-
+ /**
+ * This is the update to the predicted state, based on the Kalman feedback.
+ */
+ protected final DMatrixRMaj stateCorrection;
/**
* The predicted estimate of the state according to the measurement model of the filter.
*/
@@ -145,32 +148,40 @@ public abstract class ExtendedKalmanFilter
private final DMatrixRMaj josephGainTermContainer;
private final int stateSize;
+ private final int errorSize;
private final int measurementSize;
public ExtendedKalmanFilter(int stateSize, int measurementSize)
+ {
+ this(stateSize, stateSize, measurementSize);
+ }
+
+ public ExtendedKalmanFilter(int stateSize, int errorSize, int measurementSize)
{
this.stateSize = stateSize;
+ this.errorSize = errorSize;
this.measurementSize = measurementSize;
- processCovariance = new DMatrixRMaj(stateSize, stateSize);
+ processCovariance = new DMatrixRMaj(errorSize, errorSize);
measurementCovariance = new DMatrixRMaj(measurementSize, measurementSize);
- processJacobian = new DMatrixRMaj(stateSize, stateSize);
- measurementJacobian = new DMatrixRMaj(measurementSize, stateSize);
+ processJacobian = new DMatrixRMaj(stateSize, errorSize);
+ measurementJacobian = new DMatrixRMaj(measurementSize, errorSize);
state = new DMatrixRMaj(stateSize, 1);
predictedState = new DMatrixRMaj(stateSize, 1);
- covariance = new DMatrixRMaj(stateSize, stateSize);
- predictedCovariance = new DMatrixRMaj(stateSize, stateSize);
- predictedCovarianceContainer = new DMatrixRMaj(stateSize, stateSize);
+ covariance = new DMatrixRMaj(errorSize, errorSize);
+ predictedCovariance = new DMatrixRMaj(errorSize, errorSize);
+ predictedCovarianceContainer = new DMatrixRMaj(errorSize, errorSize);
measurementResidual = new DMatrixRMaj(measurementSize, 1);
- residualCovarianceContainer = new DMatrixRMaj(measurementSize, measurementSize);
+ residualCovarianceContainer = new DMatrixRMaj(errorSize, errorSize);
residualCovariance = new DMatrixRMaj(measurementSize, measurementSize);
inverseResidualCovariance = new DMatrixRMaj(measurementSize, measurementSize);
solver = new LinearSolverSafe<>(LinearSolverFactory_DDRM.symmPosDef(measurementSize));
kalmanGain = new DMatrixRMaj(stateSize, measurementSize);
kalmanGainContainer = new DMatrixRMaj(stateSize, measurementSize);
+ stateCorrection = new DMatrixRMaj(errorSize, 1);
updatedState = new DMatrixRMaj(stateSize, 1);
updatedCovariance = new DMatrixRMaj(stateSize, stateSize);
updatedCovarianceContainer = new DMatrixRMaj(stateSize, stateSize);
@@ -178,11 +189,11 @@ public ExtendedKalmanFilter(int stateSize, int measurementSize)
normalizedInnovation = new DMatrixRMaj(measurementSize, 1);
normalizedInnovationContainer = new DMatrixRMaj(measurementSize, 1);
- josephTransposeTerm = new DMatrixRMaj(stateSize, stateSize);
- josephTransposeTermContainer = new DMatrixRMaj(stateSize, stateSize);
- josephIdentity = CommonOps_DDRM.identity(stateSize);
- josephGainTerm = new DMatrixRMaj(stateSize, stateSize);
- josephGainTermContainer = new DMatrixRMaj(stateSize, stateSize);
+ josephTransposeTerm = new DMatrixRMaj(errorSize, errorSize);
+ josephTransposeTermContainer = new DMatrixRMaj(errorSize, measurementSize);
+ josephIdentity = CommonOps_DDRM.identity(errorSize);
+ josephGainTerm = new DMatrixRMaj(errorSize, errorSize);
+ josephGainTermContainer = new DMatrixRMaj(errorSize, errorSize);
}
public ExtendedKalmanFilter(DMatrixRMaj initialState, DMatrixRMaj initialCovariance, DMatrixRMaj processCovariance, DMatrixRMaj measurementCovariance)
@@ -220,6 +231,11 @@ public int getStateSize()
return stateSize;
}
+ public int getErrorSize()
+ {
+ return errorSize;
+ }
+
public int getMeasurementSize()
{
return measurementSize;
@@ -317,6 +333,15 @@ private void applyInnovationGate()
covariance.set(predictedCovariance);
}
+ /**
+ * This is broken into its own function, in case the state exists on some manifold. This is the case for, for example, quaternions. In that event, this
+ * function should overriden.
+ */
+ protected void applyStateCorrection(DMatrixRMaj state, DMatrixRMaj modification, DMatrixRMaj modifiedState)
+ {
+ CommonOps_DDRM.add(state, modification, modifiedState);
+ }
+
/**
* If the observation is valid, the actual observation, as well as the measurement model and its noise profile, are used to correct the
* prediction made in the {@link #predictionStep()}.
@@ -326,8 +351,8 @@ protected void updateStep()
calculateKalmanGain();
// x_(k|k) = x_(k|k-1) + K_k * y_k
- updatedState.set(predictedState);
- CommonOps_DDRM.multAdd(kalmanGain, measurementResidual, updatedState);
+ CommonOps_DDRM.mult(kalmanGain, measurementResidual, stateCorrection);
+ applyStateCorrection(predictedState, stateCorrection, updatedState);
calculateUpdatedCovariance();
state.set(updatedState);
@@ -343,6 +368,7 @@ private void calculateResidualCovarianceAndInverse()
CommonOps_DDRM.addEquals(residualCovariance, measurementCovariance);
solver.setA(residualCovariance);
+ // fixme this doesn't work.
CommonOps_DDRM.invert(residualCovariance, inverseResidualCovariance);
// solver.invert(inverseResidualCovariance);
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index 182c93e374a7..81b9730322dc 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -7,10 +7,7 @@
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.matrixlib.MatrixTools;
-import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.MeasuredVariables;
-import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.StateVariables;
import us.ihmc.yoVariables.euclid.YoQuaternion;
-import us.ihmc.yoVariables.euclid.YoVector3D;
import us.ihmc.yoVariables.providers.DoubleProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
@@ -23,15 +20,11 @@ class MeasurementModel
private final StateVariables baseState;
private final StateVariables footState;
- private final MeasuredVariables footMeasurements;
+ private final SensedVariables footSensing;
+ private final MeasurementVariables predictedMeasurements;
private final FrameVector3DReadOnly gravityVector;
// State variables
- public final YoVector3D footPredictedRelativePosition;
- public final YoVector3D footPredictedRelativeOrientationError;
- public final YoVector3D footPredictedLinearVelocityError;
- public final YoVector3D footPredictedLinearVelocity;
- public final YoVector3D footPredictedGravityVector;
public final YoQuaternion footOrientationInBaseFrame;
public final YoBoolean isInContact;
@@ -39,27 +32,31 @@ class MeasurementModel
// Temp variables
private final Quaternion footOrientationError = new Quaternion();
+ private final Vector3D positionError = new Vector3D();
private final Vector3D velocityError = new Vector3D();
+ private final Vector3D tempVector = new Vector3D();
+ private final DMatrixRMaj rotationMatrixTranspose = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj left = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj right = new DMatrixRMaj(3, 3);
+ private final Quaternion tempRotation = new Quaternion();
public MeasurementModel(String prefix,
StateVariables baseState,
StateVariables footState,
- MeasuredVariables footMeasurements,
+ SensedVariables footSensing,
+ MeasurementVariables predictedMeasurement,
FrameVector3DReadOnly gravityVector,
DoubleProvider contactThreshold,
YoRegistry registry)
{
this.baseState = baseState;
this.footState = footState;
- this.footMeasurements = footMeasurements;
+ this.footSensing = footSensing;
+ this.predictedMeasurements = predictedMeasurement;
this.gravityVector = gravityVector;
this.contactThreshold = contactThreshold;
- footPredictedRelativePosition = new YoVector3D(prefix + "PredictedRelativePosition", registry);
- footPredictedRelativeOrientationError = new YoVector3D(prefix + "PredictedRelativeOrientationError", registry);
- footPredictedLinearVelocityError = new YoVector3D(prefix + "PredictedLinearVelocityError", registry);
- footPredictedLinearVelocity = new YoVector3D(prefix + "PredictedLinearVelocity", registry);
- footPredictedGravityVector = new YoVector3D(prefix + "PredictedAccelMeasure", registry);
footOrientationInBaseFrame = new YoQuaternion(prefix + "OrientationInBaseFrame", registry);
isInContact = new YoBoolean(prefix + "IsInContact", registry);
}
@@ -69,66 +66,58 @@ public MeasurementModel(String prefix,
*/
public void update(DMatrixRMaj footCovariance)
{
- isInContact.set(isFootInContact(footCovariance));
+ isInContact.set(computeIsFootInContact(footCovariance));
// First row. Update the predicted foot position of the foot relative to the base in the base frame
- footPredictedRelativePosition.sub(footState.translation, baseState.translation);
- baseState.orientation.transform(footPredictedRelativePosition); // FIXME is this right? transforms do more operations than I think is expected
+ predictedMeasurements.relativePosition.sub(footState.translation, baseState.translation);
+ baseState.orientation.transform(predictedMeasurements.relativePosition); // FIXME is this right? transforms do more operations than I think is expected
// Update the predicted foot orientation of the foot relative to the base in the base frame
QuaternionTools.multiplyConjugateLeft(baseState.orientation, footState.orientation, footOrientationInBaseFrame);
// Compute the error between the predicted and measured foot orientation
- QuaternionTools.multiplyConjugateLeft(footMeasurements.orientationMeasurement, footOrientationInBaseFrame, footOrientationError);
- footOrientationError.getRotationVector(footPredictedRelativeOrientationError);
+ QuaternionTools.multiplyConjugateLeft(footSensing.orientationMeasurement, footOrientationInBaseFrame, footOrientationError);
+ footOrientationError.getRotationVector(predictedMeasurements.relativeOrientationError);
// Third row. Compute the linear velocity error in the base frame
velocityError.sub(baseState.linearVelocity, footState.linearVelocity);
baseState.orientation.inverseTransform(velocityError);
- footPredictedLinearVelocityError.cross(baseState.unbiasedGyro, footMeasurements.positionMeasurement);
- footPredictedLinearVelocityError.scale(-1.0);
- footPredictedLinearVelocityError.sub(velocityError);
- footPredictedLinearVelocityError.add(footMeasurements.linearVelocity);
+ predictedMeasurements.relativeLinearVelocityError.cross(baseState.unbiasedGyro, footSensing.positionMeasurement);
+ predictedMeasurements.relativeLinearVelocityError.scale(-1.0);
+ predictedMeasurements.relativeLinearVelocityError.sub(velocityError);
+ predictedMeasurements.relativeLinearVelocityError.add(footSensing.linearVelocity);
if (!isInContact.getBooleanValue())
return;
// Fourth row. Set the predicted linear velocity
- footPredictedLinearVelocity.set(footState.linearVelocity);
+ predictedMeasurements.contactVelocity.set(footState.linearVelocity);
// Fifth row. Set the predicted gravity vector
- footState.orientation.transform(gravityVector, footPredictedGravityVector);
+ footState.orientation.transform(gravityVector, predictedMeasurements.accelMeasure);
if (OdometryKalmanFilter.includeBias)
- footPredictedGravityVector.add(footState.accelBias);
+ predictedMeasurements.accelMeasure.add(footState.accelBias);
}
private final DMatrixRMaj vector = new DMatrixRMaj(3, 1);
- private final DMatrixRMaj tempVector = new DMatrixRMaj(3, 1);
+ private final DMatrixRMaj tempDVector = new DMatrixRMaj(3, 1);
private final DMatrixRMaj tempScalar = new DMatrixRMaj(3, 1);
- private boolean isFootInContact(DMatrixRMaj footCovariance)
+ private boolean computeIsFootInContact(DMatrixRMaj footCovariance)
{
footState.translation.get(vector);
- CommonOps_DDRM.mult(footCovariance, vector, tempVector);
- CommonOps_DDRM.multTransA(vector, tempVector, tempScalar);
+ CommonOps_DDRM.mult(footCovariance, vector, tempDVector);
+ CommonOps_DDRM.multTransA(vector, tempDVector, tempScalar);
// tests the mahalonobis distance of the foot velocity being below a certain threshold.
return tempScalar.get(0, 0) < contactThreshold.getValue();
}
- public void get(int start, DMatrixRMaj measurementToPack)
+ public boolean getIsFootInContact()
{
- footPredictedRelativePosition.get(start + measurementRelativeTranslationIndex, measurementToPack);
- footPredictedRelativeOrientationError.get(start + measurementRelativeOrientationErrorIndex, measurementToPack);
- footPredictedLinearVelocityError.get(start + measurementRelativeVelocityIndex, measurementToPack);
-
- if (isInContact.getBooleanValue())
- return;
-
- footPredictedLinearVelocity.get(start + measurementContactVelocityIndex, measurementToPack);
- footPredictedGravityVector.get(start + measurementAccelIndex, measurementToPack);
+ return isInContact.getBooleanValue();
}
/**
@@ -136,52 +125,45 @@ public void get(int start, DMatrixRMaj measurementToPack)
*/
public void computeBaseMeasurementJacobian(StateVariables baseState,
StateVariables footState,
- MeasuredVariables footMeasurements,
+ SensedVariables footMeasurements,
int rowOffset,
DMatrixRMaj jacobianToPack)
{
- // FIXME garbage
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- DMatrixRMaj rotationMatrixTranspose = new DMatrixRMaj(3, 3);
- OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrix);
- CommonOps_DDRM.transpose(rotationMatrix, rotationMatrixTranspose);
-
- Vector3D footPositionError = new Vector3D();
- Vector3D footVelocityError = new Vector3D();
- footPositionError.sub(footState.translation, baseState.translation);
- footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
- baseState.orientation.inverseTransform(footPositionError);
- baseState.orientation.inverseTransform(footVelocityError);
-
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- OdometryTools.toSkewSymmetricMatrix(footPositionError, skewMatrix);
-
- // First row
+ OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrixTranspose);
+ CommonOps_DDRM.transpose(rotationMatrixTranspose);
+
+
+ positionError.sub(footState.translation, baseState.translation);
+ velocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(positionError);
+ baseState.orientation.inverseTransform(velocityError);
+
+ OdometryTools.toSkewSymmetricMatrix(positionError, skewMatrix);
+
+ // First row. Partial of relative foot position w.r.t. the base state
int row = rowOffset + measurementRelativeTranslationIndex;
- MatrixTools.setMatrixBlock(jacobianToPack, row, stateTranslationIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, stateOrientationIndex);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, errorTranslationIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, errorOrientationIndex);
- // Second row
+ // Second row. Partial of relative foot orientation error w.r.t. the base state
row = rowOffset + measurementRelativeOrientationErrorIndex;
- DMatrixRMaj left = new DMatrixRMaj(3, 3);
- DMatrixRMaj right = new DMatrixRMaj(3, 3);
- Quaternion orientation = new Quaternion(footState.orientation);
- orientation.multiplyConjugateThis(baseState.orientation);
- OdometryKalmanFilter.l3Operator(orientation, left);
+ tempRotation.set(footState.orientation);
+ tempRotation.multiplyConjugateThis(baseState.orientation);
+ OdometryKalmanFilter.l3Operator(tempRotation, left);
OdometryKalmanFilter.r3Operator(footMeasurements.orientationMeasurement, right);
- MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, row, stateOrientationIndex);
+ MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, row, errorOrientationIndex);
- // Third row
+ // Third row. Partial of relative foot velocity error w.r.t. the base state
row = rowOffset + measurementRelativeVelocityIndex;
- OdometryTools.toSkewSymmetricMatrix(footVelocityError, skewMatrix);
+ OdometryTools.toSkewSymmetricMatrix(velocityError, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, row, stateLinearVelocityIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, stateOrientationIndex);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, errorLinearVelocityIndex, rotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, errorOrientationIndex);
if (OdometryKalmanFilter.includeBias)
{
OdometryTools.toSkewSymmetricMatrix(footMeasurements.positionMeasurement, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, row, stateGyroBiasIndex, skewMatrix, 0, 0, 3, 3, -1.0);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, errorGyroBiasIndex, skewMatrix, 0, 0, 3, 3, -1.0);
}
}
@@ -190,58 +172,54 @@ public void computeBaseMeasurementJacobian(StateVariables baseState,
*/
public void computeFootMeasurementJacobian(StateVariables baseState,
StateVariables footState,
- MeasuredVariables footMeasurements,
+ SensedVariables footMeasurements,
FrameVector3DReadOnly gravityVector,
int rowOffset,
int colOffset,
DMatrixRMaj jacobianToPack)
{
- // FIXME garbage
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrix);
- CommonOps_DDRM.transpose(rotationMatrix);
-
- Vector3D footPositionError = new Vector3D();
- Vector3D footVelocityError = new Vector3D();
- footPositionError.sub(footState.translation, baseState.translation);
- footVelocityError.sub(footState.linearVelocity, baseState.linearVelocity);
- baseState.orientation.inverseTransform(footPositionError);
- baseState.orientation.inverseTransform(footVelocityError);
-
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- OdometryTools.toSkewSymmetricMatrix(footPositionError, skewMatrix);
-
- // First row
- CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset);
-
- // Second row
- rowOffset += 3;
- DMatrixRMaj left = new DMatrixRMaj(3, 3);
- Quaternion orientation = new Quaternion(footMeasurements.orientationMeasurement);
- orientation.multiplyConjugateOther(baseState.orientation);
- orientation.multiply(footState.orientation);
- OdometryKalmanFilter.l3Operator(orientation, left);
- CommonOps_DDRM.insert(left, jacobianToPack, rowOffset, colOffset + 6);
-
- // Third row
- rowOffset += 3;
- CommonOps_DDRM.insert(rotationMatrix, jacobianToPack, rowOffset, colOffset + 3);
-
- if (isInContact.getBooleanValue())
+ OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrixTranspose);
+ CommonOps_DDRM.transpose(rotationMatrixTranspose);
+
+ positionError.sub(footState.translation, baseState.translation);
+ velocityError.sub(footState.linearVelocity, baseState.linearVelocity);
+ baseState.orientation.inverseTransform(positionError);
+ baseState.orientation.inverseTransform(velocityError);
+
+ OdometryTools.toSkewSymmetricMatrix(positionError, skewMatrix);
+
+ // First row. Partial of relative contact position w.r.t. foot state
+ int row = rowOffset + measurementRelativeTranslationIndex;
+ CommonOps_DDRM.insert(rotationMatrixTranspose, jacobianToPack, row, colOffset + errorTranslationIndex);
+
+ // Second row. Partial of relative orientation error w.r.t. foot state
+ row = rowOffset + measurementRelativeOrientationErrorIndex;
+ tempRotation.set(footMeasurements.orientationMeasurement);
+ tempRotation.multiplyConjugateOther(baseState.orientation);
+ tempRotation.multiply(footState.orientation);
+ OdometryKalmanFilter.l3Operator(tempRotation, left);
+ CommonOps_DDRM.insert(left, jacobianToPack, row, colOffset + errorOrientationIndex);
+
+ // Third row. Partial of relative velocity error w.r.t. foot state
+ row = rowOffset + measurementRelativeVelocityIndex;
+ CommonOps_DDRM.insert(rotationMatrixTranspose, jacobianToPack, row, colOffset + errorLinearVelocityIndex);
+
+ if (!isInContact.getBooleanValue())
return;
- // Fourth row
- rowOffset += 3;
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset, colOffset + 3);
+ // Fourth row. Partial of contact velocity w.r.t. foot state
+ // The contact velocity is directly the foot velocity when it's in contact.
+ row = rowOffset + measurementContactVelocityIndex;
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, row, colOffset + errorLinearVelocityIndex);
- // Fifth row
- rowOffset += 3;
- Vector3D gravity = new Vector3D(gravityVector);
- footState.orientation.inverseTransform(gravity);
- OdometryTools.toSkewSymmetricMatrix(gravityVector, skewMatrix);
- CommonOps_DDRM.insert(skewMatrix, jacobianToPack, rowOffset, colOffset + 6);
+ // Fifth row. Partial of contact acceleration w.r.t. foot state
+ row = rowOffset + measurementAccelIndex;
+ tempVector.set(gravityVector);
+ footState.orientation.inverseTransform(tempVector);
+ OdometryTools.toSkewSymmetricMatrix(tempVector, skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianToPack, row, colOffset + errorOrientationIndex);
if (OdometryKalmanFilter.includeBias)
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowOffset, colOffset + 9);
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, row, colOffset + errorAccelBiasIndex);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementResidualVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementResidualVariables.java
new file mode 100644
index 000000000000..debe6134a654
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementResidualVariables.java
@@ -0,0 +1,35 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.yoVariables.euclid.YoVector3D;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
+class MeasurementResidualVariables
+{
+ // State variables
+ public final YoVector3D footPositionResidual;
+ public final YoVector3D footOrientationResidual;
+ public final YoVector3D linearVelocityResidual;
+ public final YoVector3D contactVelocityResidual;
+ public final YoVector3D contactAccelResidual;
+
+ public MeasurementResidualVariables(String prefix, YoRegistry registry)
+ {
+ footPositionResidual = new YoVector3D(prefix + "PositionResidual", registry);
+ footOrientationResidual = new YoVector3D(prefix + "OrientationResidual", registry);
+ linearVelocityResidual = new YoVector3D(prefix + "LinearVelocityResidual", registry);
+ contactVelocityResidual = new YoVector3D(prefix + "ContactVelocityResidual", registry);
+ contactAccelResidual = new YoVector3D(prefix + "ContactAccelResidual", registry);
+ }
+
+ public void set(int start, DMatrixRMaj residual)
+ {
+ footPositionResidual.set(start + measurementRelativeTranslationIndex, residual);
+ footOrientationResidual.set(start + measurementRelativeOrientationErrorIndex, residual);
+ linearVelocityResidual.set(start + measurementRelativeVelocityIndex, residual);
+ contactVelocityResidual.set(start + measurementContactVelocityIndex, residual);
+ contactAccelResidual.set(start + measurementAccelIndex, residual);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
new file mode 100644
index 000000000000..8c579577c615
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
@@ -0,0 +1,40 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.yoVariables.euclid.YoVector3D;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.measurementAccelIndex;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.measurementContactVelocityIndex;
+
+class MeasurementVariables
+{
+ public final YoVector3D relativePosition;
+ public final YoVector3D relativeOrientationError;
+ public final YoVector3D relativeLinearVelocityError;
+ public final YoVector3D contactVelocity;
+ public final YoVector3D accelMeasure;
+
+ public MeasurementVariables(String prefix, YoRegistry registry)
+ {
+ relativePosition = new YoVector3D(prefix + "RelativePosition", registry);
+ relativeOrientationError = new YoVector3D(prefix + "RelativeOrientationError", registry);
+ relativeLinearVelocityError = new YoVector3D(prefix + "RelativeLinearVelocityError", registry);
+ contactVelocity = new YoVector3D(prefix + "ContactVelocity", registry);
+ accelMeasure = new YoVector3D(prefix + "AccelMeasure", registry);
+ }
+
+ public void get(boolean isInContact, int start, DMatrixRMaj measurementToPack)
+ {
+ relativePosition.get(start + measurementRelativeTranslationIndex, measurementToPack);
+ relativeOrientationError.get(start + measurementRelativeOrientationErrorIndex, measurementToPack);
+ relativeLinearVelocityError.get(start + measurementRelativeVelocityIndex, measurementToPack);
+
+ if (!isInContact)
+ return;
+
+ contactVelocity.get(start + measurementContactVelocityIndex, measurementToPack);
+ accelMeasure.get(start + measurementAccelIndex, measurementToPack);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
index 8cc452e5785c..220f63eca37a 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ObservationModel.java
@@ -1,21 +1,23 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
import org.ejml.data.DMatrixRMaj;
-import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.MeasuredVariables;
+
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.measurementAccelIndex;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.measurementRelativeTranslationIndex;
public class ObservationModel
{
// State providers
- private final MeasuredVariables footMeasurements;
+ private final SensedVariables footMeasurements;
- public ObservationModel(MeasuredVariables footMeasurements)
+ public ObservationModel(SensedVariables footMeasurements)
{
this.footMeasurements = footMeasurements;
}
public void get(int startRow, DMatrixRMaj observation)
{
- footMeasurements.positionMeasurement.get(startRow + OdometryIndexHelper.measurementRelativeTranslationIndex, observation);
- footMeasurements.accelMeasurement.get(startRow + OdometryIndexHelper.measurementAccelIndex, observation);
+ footMeasurements.positionMeasurement.get(startRow + measurementRelativeTranslationIndex, observation);
+ footMeasurements.accelMeasurement.get(startRow + measurementAccelIndex, observation);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
index 5c5c39d954d9..ae1222f2e200 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryIndexHelper.java
@@ -1,11 +1,5 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
-import us.ihmc.euclid.tools.EuclidCoreTools;
-import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
-import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
-import us.ihmc.euclid.tuple4D.interfaces.QuaternionBasics;
-import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
-
public class OdometryIndexHelper
{
static final int stateSizePerLink = 16;
@@ -15,137 +9,22 @@ public class OdometryIndexHelper
static final int stateAccelBiasIndex = 10;
static final int stateGyroBiasIndex = 13;
+ static final int errorSizePerLink = 15;
+ static final int errorTranslationIndex = 0;
+ static final int errorLinearVelocityIndex = 3;
+ static final int errorOrientationIndex = 6;
+ static final int errorAccelBiasIndex = 9;
+ static final int errorGyroBiasIndex = 12;
+
+ static final int measurementSizePerLink = 15;
static final int measurementRelativeTranslationIndex = 0;
static final int measurementRelativeOrientationErrorIndex = 3;
static final int measurementRelativeVelocityIndex = 6;
static final int measurementContactVelocityIndex = 9;
static final int measurementAccelIndex = 12;
- public static int getStateSizePerLink()
- {
- return 16;
- }
-
- public static int getObservationSizePerLink()
- {
- return 15;
- }
-
- public static int getStatePositionIndex()
- {
- return 0;
- }
-
- public static int getStateVelocityIndex()
- {
- return 3;
- }
-
- public static int getStateOrientationIndex()
- {
- return 6;
- }
-
- public static int getStateAccelerationBiasIndex()
- {
- return 10;
- }
-
- public static int getStateGyroBiasIndex()
- {
- return 13;
- }
-
- public static int getBasePositionIndex()
- {
- return getStatePositionIndex();
- }
-
- public static int getBaseVelocityIndex()
- {
- return getStateVelocityIndex();
- }
-
- public static int getBaseOrientationIndex()
- {
- return getStateOrientationIndex();
- }
-
- public static int getBaseAccelerationBiasIndex()
- {
- return getStateAccelerationBiasIndex();
- }
-
- public static int getBaseGyroBiasIndex()
- {
- return getStateGyroBiasIndex();
- }
-
public static int getFootPositionIndex(int footNumber)
{
- return getStateSizePerLink() * (footNumber + 1) + getStatePositionIndex();
- }
-
- public static int getFootVelocityIndex(int footNumber)
- {
- return getFootPositionIndex(footNumber) + getStateVelocityIndex();
- }
-
- public static int getFootOrientationIndex(int footNumber)
- {
- return getFootPositionIndex(footNumber) + getStateOrientationIndex();
- }
-
- public static int getFootAccelerationBiasIndex(int footNumber)
- {
- return getFootPositionIndex(footNumber) + getStateAccelerationBiasIndex();
- }
-
- public static int getFootGyroBiasIndex(int footNumber)
- {
- return getFootPositionIndex(footNumber) + getStateGyroBiasIndex();
- }
-
- public static void toQuaternionFromRotationVector(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
- {
- double magnitude = rotation.norm();
- if (magnitude < 1e-7)
- {
- quaternionToPack.setToZero();
- return;
- }
-
- double s = Math.sin(magnitude * 0.5);
- double qs = Math.cos(magnitude * 0.5);
- double qx = s * rotation.getX() / magnitude;
- double qy = s * rotation.getY() / magnitude;
- double qz = s * rotation.getZ() / magnitude;
- quaternionToPack.set(qx, qy, qz, qs);
- }
-
- public static void toQuaternionFromRotationVectorSmallAngle(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
- {
- double magnitude = rotation.norm();
- double s = magnitude * 0.5;
- double qs = 1.0;
- double qx = s * rotation.getX() / magnitude;
- double qy = s * rotation.getY() / magnitude;
- double qz = s * rotation.getZ() / magnitude;
- quaternionToPack.set(qx, qy, qz, qs);
- }
-
- public static void logMap(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
- {
- // TODO is this just a transform to the axis-angle representation?
- double norm = EuclidCoreTools.norm(quaternion.getX(), quaternion.getY(), quaternion.getZ());
- double scale = 2.0 * Math.atan2(norm, quaternion.getS()) / norm;
- rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
- rotationToPack.scale(scale);
- }
-
- public static void logMapSmallAngle(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
- {
- rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
- rotationToPack.scale(2.0);
+ return stateSizePerLink * (footNumber + 1) + stateTranslationIndex;
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index 3dff3693bff4..c05fbd310876 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -11,10 +11,9 @@
import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
import us.ihmc.euclid.tuple3D.Vector3D;
-import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import us.ihmc.matrixlib.MatrixTools;
-import us.ihmc.mecano.frames.MovingReferenceFrame;
import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
import us.ihmc.mecano.spatial.Twist;
@@ -32,6 +31,8 @@
import java.util.ArrayList;
import java.util.List;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
/**
* The process model in this class is defined as
*/
@@ -64,7 +65,6 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final YoDouble contactThreshold = new YoDouble("contactVarianceThreshold", registry);
-
// Internal variables for state holding
private final YoFrameVector3D yoBaseAngularVelocityMeasurement;
@@ -79,13 +79,17 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final DMatrixRMaj AMatrix;
private final DMatrixRMaj CMatrix;
- private final MeasuredVariables baseMeasurement;
- private final List feetMeasurements = new ArrayList<>();
+ private final SensedVariables baseSensing;
+ private final List feetSensing = new ArrayList<>();
private final StateVariables baseProcessState;
private final StateVariables basePredictedState;
+ private final StateCorrectionVariables baseStateCorrection;
private final List feetProcessState = new ArrayList<>();
private final List feetPredictedState = new ArrayList<>();
+ private final List feetStateCorrections = new ArrayList<>();
+ private final List feetMeasurementResiduals = new ArrayList<>();
+ private final List feetMeasurements = new ArrayList<>();
private final List processModels = new ArrayList<>();
private final List measurementModels = new ArrayList<>();
@@ -116,35 +120,36 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
double gravitationalAcceleration,
YoRegistry parentRegistry)
{
- super(OdometryIndexHelper.getStateSizePerLink() * (1 + feetIMUs.size()), OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size());
+ super(stateSizePerLink * (1 + feetIMUs.size()), errorSizePerLink * (1 + feetIMUs.size()) , measurementSizePerLink * feetIMUs.size());
+ int links = 1 + feetIMUs.size();
// Initialize covariance
- processCovariance = new DMatrixRMaj(getStateSize(), getStateSize());
+ processCovariance = new DMatrixRMaj(getErrorSize(), getErrorSize());
measurementCovariance = new DMatrixRMaj(getMeasurementSize(), getMeasurementSize());
MatrixTools.setDiagonal(processCovariance, 1e-2);
MatrixTools.setDiagonal(measurementCovariance, 1e-3);
-// translationCovariance.set(1e-3);
-// velocityCovariance.set(1e-2);
-// orientationCovariance.set(1e-2);
-// biasCovariance.set(1e-4);
+ translationCovariance.set(1e-3);
+ velocityCovariance.set(1e-2);
+ orientationCovariance.set(1e-2);
+ biasCovariance.set(1e-4);
+
+ measuredTranslationCovariance.set(1e-5);
+ measuredOrientationCovariance.set(1e-2);
+ velocityErrorCovariance.set(1e-3);
+ contactVelocityCovariance.set(1e-5);
+ contactAccelerationCovariance.set(1.0);
+
+// translationCovariance.set(1e-4);
+// velocityCovariance.set(1e-4);
+// orientationCovariance.set(1e-4);
+// biasCovariance.set(1e-4);
//
-// measuredTranslationCovariance.set(1e-5);
-// measuredOrientationCovariance.set(1e-2);
-// velocityErrorCovariance.set(1e-3);
-// contactVelocityCovariance.set(1e-5);
-// contactAccelerationCovariance.set(1.0);
-
- translationCovariance.set(1e-4);
- velocityCovariance.set(1e-4);
- orientationCovariance.set(1e-4);
- biasCovariance.set(1e-4);
-
- measuredTranslationCovariance.set(1e-4);
- measuredOrientationCovariance.set(1e-4);
- velocityErrorCovariance.set(1e-4);
- contactVelocityCovariance.set(1e-4);
- contactAccelerationCovariance.set(1e-4);
+// measuredTranslationCovariance.set(1e-4);
+// measuredOrientationCovariance.set(1e-4);
+// velocityErrorCovariance.set(1e-4);
+// contactVelocityCovariance.set(1e-4);
+// contactAccelerationCovariance.set(1e-4);
contactThreshold.set(Double.MAX_VALUE);
@@ -155,15 +160,16 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
predictedState = new DMatrixRMaj(getStateSize(), 1);
predictedMeasurement = new DMatrixRMaj(getMeasurementSize(), 1);
- AMatrix = new DMatrixRMaj(getStateSize(), getStateSize());
- CMatrix = new DMatrixRMaj(getMeasurementSize(), getStateSize());
- observationVector = new DMatrixRMaj(OdometryIndexHelper.getObservationSizePerLink() * feetIMUs.size(), 1);
+ AMatrix = new DMatrixRMaj(getErrorSize(), getErrorSize());
+ CMatrix = new DMatrixRMaj(getMeasurementSize(), getErrorSize());
+ observationVector = new DMatrixRMaj(getMeasurementSize(), 1);
gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, Math.abs(gravitationalAcceleration)));
- baseMeasurement = new MeasuredVariables("base", baseIMU, baseIMU, imuBiasProvider, registry);
+ baseSensing = new SensedVariables("base", baseIMU, baseIMU, imuBiasProvider, registry);
baseProcessState = new StateVariables("baseProcessState", baseIMU.getMeasurementFrame(), registry);
basePredictedState = new StateVariables("basePredicted", baseIMU.getMeasurementFrame(), registry);
+ baseStateCorrection = new StateCorrectionVariables("baseStateCorrection", baseIMU.getMeasurementFrame(), registry);
processModels.add(new ProcessModel(baseProcessState,
basePredictedState,
cancelGravityFromAccelerationMeasurement,
@@ -171,14 +177,20 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
estimatorDT));
for (int i = 0; i < feetIMUs.size(); i++)
{
- MeasuredVariables footMeasurements = new MeasuredVariables("foot" + i, baseIMU, feetIMUs.get(i), imuBiasProvider, registry);
+ SensedVariables footSensing = new SensedVariables("foot" + i, baseIMU, feetIMUs.get(i), imuBiasProvider, registry);
StateVariables footProcessState = new StateVariables("foot" + i + "ProcessState", feetIMUs.get(i).getMeasurementFrame(), registry);
StateVariables footPredictedState = new StateVariables("foot" + i + "PredictedState", feetIMUs.get(i).getMeasurementFrame(), registry);
- feetMeasurements.add(footMeasurements);
+ StateCorrectionVariables footStateCorrection = new StateCorrectionVariables("foot" + i + "StateCorrection", feetIMUs.get(i).getMeasurementFrame(), registry);
+ MeasurementVariables footMeasurement = new MeasurementVariables("foot" + i + "Predicted", registry);
+ MeasurementResidualVariables footMeasurementResidual = new MeasurementResidualVariables("foot" + i + "Residual", registry);
+ feetSensing.add(footSensing);
+ feetMeasurements.add(footMeasurement);
feetProcessState.add(footProcessState);
feetPredictedState.add(footPredictedState);
+ feetStateCorrections.add(footStateCorrection);
+ feetMeasurementResiduals.add(footMeasurementResidual);
- observationModels.add(new ObservationModel(footMeasurements));
+ observationModels.add(new ObservationModel(footSensing));
processModels.add(new ProcessModel(footProcessState,
footPredictedState,
cancelGravityFromAccelerationMeasurement,
@@ -187,7 +199,8 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
measurementModels.add(new MeasurementModel("foot" + i,
basePredictedState,
footPredictedState,
- footMeasurements,
+ footSensing,
+ footMeasurement,
gravityVector,
contactThreshold,
registry));
@@ -217,7 +230,7 @@ private void initialize()
feetProcessState.get(i).initialize();
// update the predicted state from the yo variablized state
- baseProcessState.get(OdometryIndexHelper.getBasePositionIndex(), state);
+ baseProcessState.get(stateTranslationIndex, state);
for (int i = 0; i < feetIMUs.size(); i++)
{
feetProcessState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), state);
@@ -226,154 +239,19 @@ private void initialize()
initializeState(state);
}
- static class MeasuredVariables
- {
- // State providers
- private final IMUSensorReadOnly baseIMU;
- private final IMUSensorReadOnly imu;
- private final IMUBiasProvider imuBiasProvider;
-
- // State recorders
- public final YoFrameVector3D gyroMeasurementInWorld;
- public final YoFrameVector3D gyroMeasurement;
- public final YoFrameVector3D accelMeasurementInWorld;
- public final YoFrameVector3D accelMeasurement;
-
- public final YoFramePoint3D positionMeasurement;
- public final YoFrameQuaternion orientationMeasurement;
- public final YoFrameVector3D linearVelocity;
-
- // Temp variables
- private final FrameVector3D linearAcceleration = new FrameVector3D();
- private final FrameVector3D angularVelocity = new FrameVector3D();
- private final Twist twist = new Twist();
-
- public MeasuredVariables(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOnly imu, IMUBiasProvider imuBiasProvider, YoRegistry registry)
- {
- this.baseIMU = baseIMU;
- this.imu = imu;
- this.imuBiasProvider = imuBiasProvider;
-
- gyroMeasurementInWorld = new YoFrameVector3D(prefix + "GyroMeasurementInWorld", worldFrame, registry);
- gyroMeasurement = new YoFrameVector3D(prefix + "GyroMeasurement", imu.getMeasurementFrame(), registry);
- accelMeasurementInWorld = new YoFrameVector3D(prefix + "AccelMeasurementInWorld", worldFrame, registry);
- accelMeasurement = new YoFrameVector3D(prefix + "AccelMeasurement", imu.getMeasurementFrame(), registry);
-
- positionMeasurement = new YoFramePoint3D(prefix + "PositionMeasurement", baseIMU.getMeasurementFrame(), registry);
- orientationMeasurement = new YoFrameQuaternion(prefix + "OrientationMeasurement", baseIMU.getMeasurementFrame(), registry);
-
- linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
- }
-
- public void update()
- {
- // Update gyro measure
- FrameVector3DReadOnly gyroBiasInput = imuBiasProvider.getAngularVelocityBiasInIMUFrame(imu);
- Vector3DReadOnly gyroRawInput = imu.getAngularVelocityMeasurement();
-
- angularVelocity.setReferenceFrame(imu.getMeasurementFrame());
- angularVelocity.sub(gyroRawInput, gyroBiasInput);
-
- gyroMeasurementInWorld.setMatchingFrame(angularVelocity);
- gyroMeasurement.setMatchingFrame(angularVelocity);
-
- // Update the accelerometer measure
- FrameVector3DReadOnly accelBiasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(imu);
- Vector3DReadOnly accelRawInput = imu.getLinearAccelerationMeasurement();
-
- linearAcceleration.setReferenceFrame(imu.getMeasurementFrame());
- linearAcceleration.sub(accelRawInput, accelBiasInput);
-
- accelMeasurementInWorld.setMatchingFrame(linearAcceleration);
- accelMeasurement.setMatchingFrame(linearAcceleration);
-
- positionMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
- orientationMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
-
- imu.getMeasurementFrame().getTwistRelativeToOther(baseIMU.getMeasurementFrame(), twist);
- twist.changeFrame(worldFrame);
- linearVelocity.set(twist.getLinearPart());
- }
- }
-
- static class StateVariables
- {
- public final YoFramePoint3D translation;
- public final YoFrameVector3D linearVelocity;
- public final YoFrameQuaternion orientation;
- public final YoFrameVector3D accelBias;
- public final YoFrameVector3D gyroBias;
-
- public final YoFrameVector3D unbiasedAccel;
- public final YoFrameVector3D unbiasedGyro;
-
- private final MovingReferenceFrame sensorFrame;
-
- public StateVariables(String prefix, MovingReferenceFrame sensorFrame, YoRegistry registry)
- {
- this.sensorFrame = sensorFrame;
-
- translation = new YoFramePoint3D(prefix + "Translation", worldFrame, registry);
- linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
- orientation = new YoFrameQuaternion(prefix + "Orientation", worldFrame, registry);
- accelBias = new YoFrameVector3D(prefix + "AccelBias", sensorFrame, registry);
- gyroBias = new YoFrameVector3D(prefix + "GyroBias", sensorFrame, registry);
- unbiasedAccel = new YoFrameVector3D(prefix + "UnbiasedAccel", sensorFrame, registry);
- unbiasedGyro = new YoFrameVector3D(prefix + "UnbiasedGyro", sensorFrame, registry);
- }
-
- public void initialize()
- {
- this.translation.setFromReferenceFrame(sensorFrame);
- this.orientation.setFromReferenceFrame(sensorFrame);
- linearVelocity.setMatchingFrame(sensorFrame.getTwistOfFrame().getLinearPart());
- accelBias.setToZero();
- gyroBias.setToZero();
- }
-
- public void set(int start, DMatrixRMaj state, MeasuredVariables measuredVariables)
- {
- translation.set(start + OdometryIndexHelper.getStatePositionIndex(), state);
- linearVelocity.set(start + OdometryIndexHelper.getStateVelocityIndex(), state);
- orientation.set(start + OdometryIndexHelper.getStateOrientationIndex(), state);
- accelBias.set(start + OdometryIndexHelper.getStateAccelerationBiasIndex(), state);
- gyroBias.set(start + OdometryIndexHelper.getStateGyroBiasIndex(), state);
-
- if (OdometryKalmanFilter.includeBias)
- {
- unbiasedAccel.sub(measuredVariables.accelMeasurement, accelBias);
- unbiasedGyro.sub(measuredVariables.gyroMeasurement, gyroBias);
- }
- else
- {
- unbiasedAccel.set(measuredVariables.accelMeasurement);
- unbiasedGyro.set(measuredVariables.gyroMeasurement);
- }
- }
-
- public void get(int start, DMatrixRMaj stateToPack)
- {
- translation.get(start + OdometryIndexHelper.getStatePositionIndex(), stateToPack);
- linearVelocity.get(start + OdometryIndexHelper.getStateVelocityIndex(), stateToPack);
- orientation.get(start + OdometryIndexHelper.getStateOrientationIndex(), stateToPack);
- accelBias.get(start + OdometryIndexHelper.getStateAccelerationBiasIndex(), stateToPack);
- gyroBias.get(start + OdometryIndexHelper.getStateGyroBiasIndex(), stateToPack);
- }
- }
-
private boolean initialized = false;
public void compute()
{
updateCovariance();
- baseMeasurement.update();
- for (int i = 0; i < feetMeasurements.size(); i++)
- feetMeasurements.get(i).update();
+ baseSensing.update();
+ for (int i = 0; i < feetSensing.size(); i++)
+ feetSensing.get(i).update();
for (int i = 0; i < observationModels.size(); i++)
{
- observationModels.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), observationVector);
+ observationModels.get(i).get(i * measurementSizePerLink, observationVector);
}
if (!initialized)
@@ -403,42 +281,22 @@ private void updateCovariance()
{
for (int i = 0; i < feetIMUs.size() + 1; i++)
{
- int index = i * OdometryIndexHelper.getStateSizePerLink();
- processCovariance.set(index, index++, translationCovariance.getValue());
- processCovariance.set(index, index++, translationCovariance.getValue());
- processCovariance.set(index, index++, translationCovariance.getValue());
- processCovariance.set(index, index++, velocityCovariance.getValue());
- processCovariance.set(index, index++, velocityCovariance.getValue());
- processCovariance.set(index, index++, velocityCovariance.getValue());
- processCovariance.set(index, index++, orientationCovariance.getValue());
- processCovariance.set(index, index++, orientationCovariance.getValue());
- processCovariance.set(index, index++, orientationCovariance.getValue());
- processCovariance.set(index, index++, orientationCovariance.getValue());
- processCovariance.set(index, index++, biasCovariance.getValue());
- processCovariance.set(index, index++, biasCovariance.getValue());
- processCovariance.set(index, index++, biasCovariance.getValue());
- processCovariance.set(index, index++, biasCovariance.getValue());
- processCovariance.set(index, index++, biasCovariance.getValue());
- processCovariance.set(index, index, biasCovariance.getValue());
+ int index = i * errorSizePerLink;
+ OdometryTools.setDiagonals(index + errorTranslationIndex, 3, translationCovariance.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorLinearVelocityIndex, 3, velocityCovariance.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorOrientationIndex, 3, orientationCovariance.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorAccelBiasIndex, 3, biasCovariance.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorGyroBiasIndex, 3, biasCovariance.getValue(), processCovariance);
}
for (int i = 0; i < feetIMUs.size(); i++)
{
- int index = i * OdometryIndexHelper.getObservationSizePerLink();
- for (int j = 0; j < 3; j++)
- measurementCovariance.set(index + j, index + j, measuredTranslationCovariance.getValue());
- index += 3;
- for (int j = 0; j < 3; j++)
- measurementCovariance.set(index + j, index + j, measuredOrientationCovariance.getValue());
- index += 3;
- for (int j = 0; j < 3; j++)
- measurementCovariance.set(index + j, index + j, velocityErrorCovariance.getValue());
- index += 3;
- for (int j = 0; j < 3; j++)
- measurementCovariance.set(index + j, index + j, contactVelocityCovariance.getValue());
- index += 3;
- for (int j = 0; j < 3; j++)
- measurementCovariance.set(index + j, index + j, contactAccelerationCovariance.getValue());
+ int index = i * measurementSizePerLink;
+ OdometryTools.setDiagonals(index + measurementRelativeTranslationIndex, 3, measuredTranslationCovariance.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementRelativeOrientationErrorIndex, 3, measuredOrientationCovariance.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementRelativeVelocityIndex, 3, velocityErrorCovariance.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementContactVelocityIndex, 3, contactVelocityCovariance.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementAccelIndex, 3, contactAccelerationCovariance.getValue(), measurementCovariance);
}
setProcessCovariance(processCovariance);
@@ -496,9 +354,9 @@ private void updateRootJointTwistAngularPart()
}
private final FramePoint3D imuToRoot = new FramePoint3D();
+
private final FrameVector3D addedAngular = new FrameVector3D();
private final FrameVector3D momentArm = new FrameVector3D();
-
private void updateRootJointTwistLinearPart(FrameVector3DReadOnly imuLinearVelocity,
FrameVector3DReadOnly imuAngularVelocity,
FixedFrameVector3DBasics rootLinearVelocityToPack)
@@ -518,16 +376,56 @@ private void updateRootJointTwistLinearPart(FrameVector3DReadOnly imuLinearVeloc
rootLinearVelocityToPack.add(addedAngular);
}
+ @Override
+ protected void postSolveHook()
+ {
+ // Update some yo variables
+ baseStateCorrection.set(0, stateCorrection);
+ for (int i = 0; i < feetIMUs.size(); i++)
+ {
+ feetStateCorrections.get(i).set((i + 1) * errorSizePerLink, stateCorrection);
+ feetMeasurementResiduals.get(i).set(i * measurementSizePerLink, measurementResidual);
+ }
+ }
+
+ @Override
+ protected void applyStateCorrection(DMatrixRMaj state, DMatrixRMaj correction, DMatrixRMaj correctedState)
+ {
+ correctedState.set(state);
+ for (int i = 0; i < feetIMUs.size() + 1; i++)
+ {
+ int destStart = stateSizePerLink * i;
+ int sourceStart = errorSizePerLink * i;
+ // Update the translation
+ MatrixTools.addMatrixBlock(correctedState, destStart + stateTranslationIndex, 0, correction, sourceStart + errorTranslationIndex, 0, 3, 1, 1.0);
+ // Update the linear velocity
+ MatrixTools.addMatrixBlock(correctedState, destStart + stateLinearVelocityIndex, 0, correction, sourceStart + errorLinearVelocityIndex, 0, 3, 1, 1.0);
+ // Update the orientation
+ // FIXME garbage
+ Vector3D rotationVector = new Vector3D();
+ rotationVector.set(sourceStart + errorOrientationIndex, correction);
+ Quaternion correctionRotation = new Quaternion(rotationVector);
+ Quaternion rotation = new Quaternion();
+ rotation.set(destStart + stateOrientationIndex, state);
+ rotation.multiply(correctionRotation);
+ rotation.get(destStart + stateOrientationIndex, correctedState);
+ // Update the accel bias
+ MatrixTools.addMatrixBlock(correctedState, destStart + stateAccelBiasIndex, 0, correction, sourceStart + errorAccelBiasIndex, 0, 3, 1, 1.0);
+ // Update the gyro bias
+ MatrixTools.addMatrixBlock(correctedState, destStart + stateGyroBiasIndex, 0, correction, sourceStart + errorGyroBiasIndex, 0, 3, 1, 1.0);
+ }
+ }
+
@Override
public DMatrixRMaj processModel(DMatrixRMaj state)
{
predictedState.zero();
// update the yo variables representing the state
- baseProcessState.set(OdometryIndexHelper.getBasePositionIndex(), state, baseMeasurement);
+ baseProcessState.set(stateTranslationIndex, state, baseSensing);
for (int i = 0; i < feetIMUs.size(); i++)
{
- feetProcessState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state, feetMeasurements.get(i));
+ feetProcessState.get(i).set(OdometryIndexHelper.getFootPositionIndex(i), state, feetSensing.get(i));
}
// do the prediction
@@ -535,7 +433,7 @@ public DMatrixRMaj processModel(DMatrixRMaj state)
processModels.get(i).update();
// update the predicted state from the yo variablized state
- basePredictedState.get(OdometryIndexHelper.getBasePositionIndex(), predictedState);
+ basePredictedState.get(stateTranslationIndex, predictedState);
for (int i = 0; i < feetIMUs.size(); i++)
{
feetPredictedState.get(i).get(OdometryIndexHelper.getFootPositionIndex(i), predictedState);
@@ -553,11 +451,11 @@ public DMatrixRMaj measurementModel(DMatrixRMaj predictedState)
// Note that this bypasses the use of predicted state vector, as the predicted state variables are computed in the {@link #processModel(DMatrixRMaj)} call
for (int i = 0; i < feetIMUs.size(); i++)
{
- int row = OdometryIndexHelper.getFootVelocityIndex(i);
+ int row = errorSizePerLink * (i + 1) + errorLinearVelocityIndex;
MatrixTools.setMatrixBlock(footCovariance, 0, 0, getCovariance(), row, row, 3, 3, 1.0);
measurementModels.get(i).update(footCovariance);
- measurementModels.get(i).get(i * OdometryIndexHelper.getObservationSizePerLink(), predictedMeasurement);
+ feetMeasurements.get(i).get(measurementModels.get(i).getIsFootInContact(), i * measurementSizePerLink, predictedMeasurement);
}
// return the predicted measurement
@@ -570,12 +468,11 @@ protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
// make sure to call zero, as the compute process model stuff has a bunch of add operators.
AMatrix.zero();
int offset = 0;
- ProcessModel.computeProcessJacobian(baseProcessState, estimatorDT, offset, AMatrix);
- for (int i = 0; i < feetIMUs.size(); i++)
+ for (int i = 0; i < feetIMUs.size() + 1; i++)
{
- offset += OdometryIndexHelper.getStateSizePerLink();
- ProcessModel.computeProcessJacobian(feetProcessState.get(i), estimatorDT, offset, AMatrix);
+ processModels.get(i).computeProcessJacobian(baseProcessState, estimatorDT, offset, AMatrix);
+ offset += errorSizePerLink;
}
return AMatrix;
@@ -587,14 +484,14 @@ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
// make sure to call zero, as the compute process model stuff has a bunch of add operators.
CMatrix.zero();
int rowOffset = 0;
- int colOffset = OdometryIndexHelper.getStateSizePerLink();
+ int colOffset = errorSizePerLink;
for (int i = 0; i < feetIMUs.size(); i++)
{
MeasurementModel measurementModel = measurementModels.get(i);
- measurementModel.computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetMeasurements.get(i), rowOffset, CMatrix);
- measurementModel.computeFootMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetMeasurements.get(i), gravityVector, rowOffset, colOffset, CMatrix);
- rowOffset += OdometryIndexHelper.getObservationSizePerLink();
- colOffset += OdometryIndexHelper.getStateSizePerLink();
+ measurementModel.computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetSensing.get(i), rowOffset, CMatrix);
+ measurementModel.computeFootMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetSensing.get(i), gravityVector, rowOffset, colOffset, CMatrix);
+ rowOffset += measurementSizePerLink;
+ colOffset += errorSizePerLink;
}
return CMatrix;
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
index 0cc8fb159d74..fcdc140d3ea9 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
@@ -1,7 +1,11 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
import org.ejml.data.DMatrixRMaj;
+import us.ihmc.euclid.tools.EuclidCoreTools;
import us.ihmc.euclid.tuple3D.interfaces.Tuple3DReadOnly;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionBasics;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
public class OdometryTools
@@ -87,4 +91,53 @@ static void toRotationMatrix(QuaternionReadOnly quaternion, DMatrixRMaj rotation
{
toRotationMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), quaternion.getS(), rotationMatrix);
}
+
+ static void setDiagonals(int start, int elements, double value, DMatrixRMaj matrix)
+ {
+ for (int i = start; i < start + elements; i++)
+ matrix.set(i, i, value);
+ }
+
+ public static void toQuaternionFromRotationVector(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
+ {
+ double magnitude = rotation.norm();
+ if (magnitude < 1e-7)
+ {
+ quaternionToPack.setToZero();
+ return;
+ }
+
+ double s = Math.sin(magnitude * 0.5);
+ double qs = Math.cos(magnitude * 0.5);
+ double qx = s * rotation.getX() / magnitude;
+ double qy = s * rotation.getY() / magnitude;
+ double qz = s * rotation.getZ() / magnitude;
+ quaternionToPack.set(qx, qy, qz, qs);
+ }
+
+ public static void toQuaternionFromRotationVectorSmallAngle(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
+ {
+ double magnitude = rotation.norm();
+ double s = magnitude * 0.5;
+ double qs = 1.0;
+ double qx = s * rotation.getX() / magnitude;
+ double qy = s * rotation.getY() / magnitude;
+ double qz = s * rotation.getZ() / magnitude;
+ quaternionToPack.set(qx, qy, qz, qs);
+ }
+
+ public static void logMap(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
+ {
+ // TODO is this just a transform to the axis-angle representation?
+ double norm = EuclidCoreTools.norm(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ double scale = 2.0 * Math.atan2(norm, quaternion.getS()) / norm;
+ rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ rotationToPack.scale(scale);
+ }
+
+ public static void logMapSmallAngle(QuaternionReadOnly quaternion, Vector3DBasics rotationToPack)
+ {
+ rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ rotationToPack.scale(2.0);
+ }
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
index ad068252645a..754597fd6aaf 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
@@ -7,12 +7,14 @@
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.matrixlib.MatrixTools;
-import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryKalmanFilter.StateVariables;
import us.ihmc.yoVariables.providers.BooleanProvider;
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
class ProcessModel
{
private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
+ private final DMatrixRMaj deltaT;
// State providers
private final StateVariables currentState;
@@ -25,6 +27,9 @@ class ProcessModel
private final Vector3D integratedVelocity = new Vector3D();
private final Vector3D unbiasedAcceleration = new Vector3D();
private final Quaternion integratedRotation = new Quaternion();
+ private final DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+
public ProcessModel(StateVariables processState,
StateVariables predictedState,
@@ -37,6 +42,9 @@ public ProcessModel(StateVariables processState,
this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
this.gravityVector = gravityVector;
this.estimatorDt = estimatorDt;
+
+ deltaT = new DMatrixRMaj(eye3x3);
+ CommonOps_DDRM.scale(estimatorDt, deltaT);
}
public void update()
@@ -69,55 +77,39 @@ public void update()
/**
* This is equation 39
*/
- public static void computeProcessJacobian(StateVariables stateVariables, double estimateDT, int offset, DMatrixRMaj jacobianToPack)
+ public void computeProcessJacobian(StateVariables stateVariables, double estimateDT, int offset, DMatrixRMaj jacobianToPack)
{
- // Do the partial derivative with respect to the base position state
- int rowStart = offset + OdometryIndexHelper.getStatePositionIndex();
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStatePositionIndex());
- MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateVelocityIndex(), eye3x3, 0, 0, 3, 3, estimateDT);
+ // First row. Partial derivative with respect to the base position state
+ // p_k+1 = p_k + v_k deltaT
+ int rowStart = offset + errorTranslationIndex;
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + errorTranslationIndex);
+ CommonOps_DDRM.insert(deltaT, jacobianToPack, rowStart, offset + errorLinearVelocityIndex);
- // Do the partial derivative with respect to the base velocity state FIXME lots of garbage
- rowStart = offset + OdometryIndexHelper.getStateVelocityIndex();
- CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateVelocityIndex());
+ // Second row. Partial derivative with respect to the base velocity state
+ // v_k+1 = v_k + deltaT * (R * a - g)
+ rowStart = offset + errorLinearVelocityIndex;
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + errorLinearVelocityIndex);
- DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
- DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
OdometryTools.toRotationMatrix(stateVariables.orientation, rotationMatrix);
OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedAccel, skewMatrix);
- MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex());
+ MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowStart, offset + errorOrientationIndex);
if (OdometryKalmanFilter.includeBias)
- MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateGyroBiasIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + errorAccelBiasIndex, rotationMatrix, 0, 0, 3, 3, -estimateDT);
- // Do the partial derivative with respect to the orientation state
- rowStart = offset + OdometryIndexHelper.getStateOrientationIndex();
+ // Third row. Partial derivative with respect to the orientation state
+ rowStart = offset + errorOrientationIndex;
OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedGyro, skewMatrix);
- MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex(), rotationMatrix, 0, 0, 3, 3, -estimateDT);
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + errorOrientationIndex);
+ MatrixTools.addMatrixBlock(jacobianToPack, rowStart, offset + errorOrientationIndex, skewMatrix, 0, 0, 3, 3, -estimateDT);
if (OdometryKalmanFilter.includeBias)
{
- MatrixTools.addMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateOrientationIndex(), eye3x3, 0, 0, 3, 3, 1.0);
- MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + OdometryIndexHelper.getStateAccelerationBiasIndex(), eye3x3, 0, 0, 3, 3, -estimateDT);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + errorGyroBiasIndex, deltaT, 0, 0, 3, 3, -1.0);
}
- // Do the partial derivative with respect to the acceleration bias state
- MatrixTools.setMatrixBlock(jacobianToPack,
- offset + OdometryIndexHelper.getStateAccelerationBiasIndex(),
- offset + OdometryIndexHelper.getStateAccelerationBiasIndex(),
- eye3x3,
- 0,
- 0,
- 3,
- 3,
- 1.0);
- // Do the partial derivative with respect to the gyro bias state
- MatrixTools.setMatrixBlock(jacobianToPack,
- offset + OdometryIndexHelper.getStateGyroBiasIndex(),
- offset + OdometryIndexHelper.getStateGyroBiasIndex(),
- eye3x3,
- 0,
- 0,
- 3,
- 3,
- 1.0);
+ // Fourth row. Partial derivative with respect to the acceleration bias state
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, offset + errorAccelBiasIndex, offset + errorAccelBiasIndex);
+ // Fifth row. Partial derivative with respect to the gyro bias state
+ CommonOps_DDRM.insert(eye3x3, jacobianToPack, offset + errorGyroBiasIndex, offset + errorGyroBiasIndex);
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
new file mode 100644
index 000000000000..7d38a65007e5
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
@@ -0,0 +1,91 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import us.ihmc.euclid.referenceFrame.FramePoint3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.mecano.spatial.Twist;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+class SensedVariables
+{
+ private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+
+ // State providers
+ private final IMUSensorReadOnly baseIMU;
+ private final IMUSensorReadOnly imu;
+ private final IMUBiasProvider imuBiasProvider;
+
+ // State recorders
+ public final YoFrameVector3D gyroMeasurementInWorld;
+ public final YoFrameVector3D gyroMeasurement;
+ public final YoFrameVector3D accelMeasurementInWorld;
+ public final YoFrameVector3D accelMeasurement;
+
+ public final YoFramePoint3D positionMeasurement;
+ public final YoFrameQuaternion orientationMeasurement;
+ public final YoFrameVector3D linearVelocity;
+
+ // Temp variables
+ private final FrameVector3D linearAcceleration = new FrameVector3D();
+ private final FrameVector3D angularVelocity = new FrameVector3D();
+ private final Twist twist = new Twist();
+
+ public SensedVariables(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOnly imu, IMUBiasProvider imuBiasProvider, YoRegistry registry)
+ {
+ this.baseIMU = baseIMU;
+ this.imu = imu;
+ this.imuBiasProvider = imuBiasProvider;
+
+ gyroMeasurementInWorld = new YoFrameVector3D(prefix + "GyroMeasurementInWorld", worldFrame, registry);
+ gyroMeasurement = new YoFrameVector3D(prefix + "GyroMeasurement", imu.getMeasurementFrame(), registry);
+ accelMeasurementInWorld = new YoFrameVector3D(prefix + "AccelMeasurementInWorld", worldFrame, registry);
+ accelMeasurement = new YoFrameVector3D(prefix + "AccelMeasurement", imu.getMeasurementFrame(), registry);
+
+ positionMeasurement = new YoFramePoint3D(prefix + "PositionMeasurement", baseIMU.getMeasurementFrame(), registry);
+ orientationMeasurement = new YoFrameQuaternion(prefix + "OrientationMeasurement", baseIMU.getMeasurementFrame(), registry);
+
+ linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
+ }
+
+ public void update()
+ {
+ // Update gyro measure
+ FrameVector3DReadOnly gyroBiasInput = imuBiasProvider.getAngularVelocityBiasInIMUFrame(imu);
+ Vector3DReadOnly gyroRawInput = imu.getAngularVelocityMeasurement();
+
+ angularVelocity.setReferenceFrame(imu.getMeasurementFrame());
+ angularVelocity.sub(gyroRawInput, gyroBiasInput);
+
+ gyroMeasurementInWorld.setMatchingFrame(angularVelocity);
+ gyroMeasurement.setMatchingFrame(angularVelocity);
+
+ // Update the accelerometer measure
+ FrameVector3DReadOnly accelBiasInput = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(imu);
+ Vector3DReadOnly accelRawInput = imu.getLinearAccelerationMeasurement();
+
+ linearAcceleration.setReferenceFrame(imu.getMeasurementFrame());
+ linearAcceleration.sub(accelRawInput, accelBiasInput);
+
+ accelMeasurementInWorld.setMatchingFrame(linearAcceleration);
+ accelMeasurement.setMatchingFrame(linearAcceleration);
+
+ positionMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
+ orientationMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
+
+ FramePoint3D imuInWorld = new FramePoint3D(imu.getMeasurementFrame());
+ FramePoint3D baseInWorld = new FramePoint3D(baseIMU.getMeasurementFrame());
+ imuInWorld.changeFrame(worldFrame);
+ baseInWorld.changeFrame(worldFrame);
+
+ imu.getMeasurementFrame().getTwistRelativeToOther(baseIMU.getMeasurementFrame(), twist);
+ twist.changeFrame(worldFrame);
+ linearVelocity.set(twist.getLinearPart());
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateCorrectionVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateCorrectionVariables.java
new file mode 100644
index 000000000000..77f95ed757ba
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateCorrectionVariables.java
@@ -0,0 +1,39 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
+class StateCorrectionVariables
+{
+ private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+
+ public final YoFramePoint3D translation;
+ public final YoFrameVector3D linearVelocity;
+ public final YoFrameVector3D orientation;
+ public final YoFrameVector3D accelBias;
+ public final YoFrameVector3D gyroBias;
+
+ public StateCorrectionVariables(String prefix, MovingReferenceFrame sensorFrame, YoRegistry registry)
+ {
+ translation = new YoFramePoint3D(prefix + "TranslationCorrection", worldFrame, registry);
+ linearVelocity = new YoFrameVector3D(prefix + "LinearVelocityCorrection", worldFrame, registry);
+ orientation = new YoFrameVector3D(prefix + "OrientationCorrection", worldFrame, registry);
+ accelBias = new YoFrameVector3D(prefix + "AccelBiasCorrection", sensorFrame, registry);
+ gyroBias = new YoFrameVector3D(prefix + "GyroBiasCorrection", sensorFrame, registry);
+ }
+
+ public void set(int start, DMatrixRMaj state)
+ {
+ translation.set(start + errorTranslationIndex, state);
+ linearVelocity.set(start + errorLinearVelocityIndex, state);
+ orientation.set(start + errorOrientationIndex, state);
+ accelBias.set(start + errorAccelBiasIndex, state);
+ gyroBias.set(start + errorGyroBiasIndex, state);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
new file mode 100644
index 000000000000..1980cc940f47
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
@@ -0,0 +1,78 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
+
+class StateVariables
+{
+ private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+
+ public final YoFramePoint3D translation;
+ public final YoFrameVector3D linearVelocity;
+ public final YoFrameQuaternion orientation;
+ public final YoFrameVector3D accelBias;
+ public final YoFrameVector3D gyroBias;
+
+ public final YoFrameVector3D unbiasedAccel;
+ public final YoFrameVector3D unbiasedGyro;
+
+ private final MovingReferenceFrame sensorFrame;
+
+ public StateVariables(String prefix, MovingReferenceFrame sensorFrame, YoRegistry registry)
+ {
+ this.sensorFrame = sensorFrame;
+
+ translation = new YoFramePoint3D(prefix + "Translation", worldFrame, registry);
+ linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
+ orientation = new YoFrameQuaternion(prefix + "Orientation", worldFrame, registry);
+ accelBias = new YoFrameVector3D(prefix + "AccelBias", sensorFrame, registry);
+ gyroBias = new YoFrameVector3D(prefix + "GyroBias", sensorFrame, registry);
+ unbiasedAccel = new YoFrameVector3D(prefix + "UnbiasedAccel", sensorFrame, registry);
+ unbiasedGyro = new YoFrameVector3D(prefix + "UnbiasedGyro", sensorFrame, registry);
+ }
+
+ public void initialize()
+ {
+ this.translation.setFromReferenceFrame(sensorFrame);
+ this.orientation.setFromReferenceFrame(sensorFrame);
+ linearVelocity.setMatchingFrame(sensorFrame.getTwistOfFrame().getLinearPart());
+ accelBias.setToZero();
+ gyroBias.setToZero();
+ }
+
+ public void set(int start, DMatrixRMaj state, SensedVariables measuredVariables)
+ {
+ translation.set(start + stateTranslationIndex, state);
+ linearVelocity.set(start + stateLinearVelocityIndex, state);
+ orientation.set(start + stateOrientationIndex, state);
+ accelBias.set(start + stateAccelBiasIndex, state);
+ gyroBias.set(start + stateGyroBiasIndex, state);
+
+ if (OdometryKalmanFilter.includeBias)
+ {
+ unbiasedAccel.sub(measuredVariables.accelMeasurement, accelBias);
+ unbiasedGyro.sub(measuredVariables.gyroMeasurement, gyroBias);
+ }
+ else
+ {
+ unbiasedAccel.set(measuredVariables.accelMeasurement);
+ unbiasedGyro.set(measuredVariables.gyroMeasurement);
+ }
+ }
+
+ public void get(int start, DMatrixRMaj stateToPack)
+ {
+ translation.get(start + stateTranslationIndex, stateToPack);
+ linearVelocity.get(start + stateLinearVelocityIndex, stateToPack);
+ orientation.get(start + stateOrientationIndex, stateToPack);
+ accelBias.get(start + stateAccelBiasIndex, stateToPack);
+ gyroBias.get(start + stateGyroBiasIndex, stateToPack);
+ }
+}
From 63038d2dcb5aee31a66d79b1bab216ae66a02a6c Mon Sep 17 00:00:00 2001
From: Robert
Date: Mon, 1 Sep 2025 23:22:42 -0500
Subject: [PATCH 08/18] did a lot more work on writing some tests, which
uncovered some bugs
---
.../odomEKF/MeasurementModel.java | 39 +-
.../odomEKF/OdometryKalmanFilter.java | 21 +-
.../odomEKF/OdometryTools.java | 152 ++++++++
.../odomEKF/ProcessModel.java | 13 +-
.../odomEKF/StateVariables.java | 10 +-
.../odomEKF/MeasurementModelTest.java | 325 ++++++++++++++++
.../odomEKF/OdomTestTools.java | 48 +++
.../odomEKF/OdometryToolsTest.java | 1 +
.../odomEKF/ProcessModelTest.java | 347 ++++++++++++++++++
9 files changed, 907 insertions(+), 49 deletions(-)
create mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
create mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
create mode 100644 ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index 81b9730322dc..45473b51ac43 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -37,8 +37,11 @@ class MeasurementModel
private final Vector3D tempVector = new Vector3D();
private final DMatrixRMaj rotationMatrixTranspose = new DMatrixRMaj(3, 3);
private final DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
- private final DMatrixRMaj left = new DMatrixRMaj(3, 3);
- private final DMatrixRMaj right = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj left3x3 = new DMatrixRMaj(3, 3);
+ private final DMatrixRMaj left = new DMatrixRMaj(4, 4);
+ private final DMatrixRMaj right = new DMatrixRMaj(4, 4);
+ private final DMatrixRMaj lr = new DMatrixRMaj(4, 4);
+ private final DMatrixRMaj lr3x3 = new DMatrixRMaj(3, 3);
private final Quaternion tempRotation = new Quaternion();
public MeasurementModel(String prefix,
@@ -123,11 +126,7 @@ public boolean getIsFootInContact()
/**
* This is equation 41
*/
- public void computeBaseMeasurementJacobian(StateVariables baseState,
- StateVariables footState,
- SensedVariables footMeasurements,
- int rowOffset,
- DMatrixRMaj jacobianToPack)
+ public void computeBaseMeasurementJacobian(int rowOffset, DMatrixRMaj jacobianToPack)
{
OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrixTranspose);
CommonOps_DDRM.transpose(rotationMatrixTranspose);
@@ -147,11 +146,11 @@ public void computeBaseMeasurementJacobian(StateVariables baseState,
// Second row. Partial of relative foot orientation error w.r.t. the base state
row = rowOffset + measurementRelativeOrientationErrorIndex;
- tempRotation.set(footState.orientation);
- tempRotation.multiplyConjugateThis(baseState.orientation);
- OdometryKalmanFilter.l3Operator(tempRotation, left);
- OdometryKalmanFilter.r3Operator(footMeasurements.orientationMeasurement, right);
- MatrixTools.multAddBlock(-1.0, left, right, jacobianToPack, row, errorOrientationIndex);
+ QuaternionTools.multiplyConjugateLeft(footState.orientation, baseState.orientation, tempRotation);
+ OdometryTools.lOperator(tempRotation, left);
+ OdometryTools.rOperator(footSensing.orientationMeasurement, right);
+ CommonOps_DDRM.mult(left, right, lr);
+ MatrixTools.setMatrixBlock(jacobianToPack, row, errorOrientationIndex, lr, 1, 1, 3, 3, -1.0);
// Third row. Partial of relative foot velocity error w.r.t. the base state
row = rowOffset + measurementRelativeVelocityIndex;
@@ -162,7 +161,7 @@ public void computeBaseMeasurementJacobian(StateVariables baseState,
if (OdometryKalmanFilter.includeBias)
{
- OdometryTools.toSkewSymmetricMatrix(footMeasurements.positionMeasurement, skewMatrix);
+ OdometryTools.toSkewSymmetricMatrix(footSensing.positionMeasurement, skewMatrix);
MatrixTools.setMatrixBlock(jacobianToPack, row, errorGyroBiasIndex, skewMatrix, 0, 0, 3, 3, -1.0);
}
}
@@ -170,13 +169,7 @@ public void computeBaseMeasurementJacobian(StateVariables baseState,
/**
* This is equation 42
*/
- public void computeFootMeasurementJacobian(StateVariables baseState,
- StateVariables footState,
- SensedVariables footMeasurements,
- FrameVector3DReadOnly gravityVector,
- int rowOffset,
- int colOffset,
- DMatrixRMaj jacobianToPack)
+ public void computeFootMeasurementJacobian(int rowOffset, int colOffset, DMatrixRMaj jacobianToPack)
{
OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrixTranspose);
CommonOps_DDRM.transpose(rotationMatrixTranspose);
@@ -194,11 +187,11 @@ public void computeFootMeasurementJacobian(StateVariables baseState,
// Second row. Partial of relative orientation error w.r.t. foot state
row = rowOffset + measurementRelativeOrientationErrorIndex;
- tempRotation.set(footMeasurements.orientationMeasurement);
+ tempRotation.set(footSensing.orientationMeasurement);
tempRotation.multiplyConjugateOther(baseState.orientation);
tempRotation.multiply(footState.orientation);
- OdometryKalmanFilter.l3Operator(tempRotation, left);
- CommonOps_DDRM.insert(left, jacobianToPack, row, colOffset + errorOrientationIndex);
+ OdometryTools.l3Operator(tempRotation, left3x3);
+ CommonOps_DDRM.insert(left3x3, jacobianToPack, row, colOffset + errorOrientationIndex);
// Third row. Partial of relative velocity error w.r.t. foot state
row = rowOffset + measurementRelativeVelocityIndex;
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index c05fbd310876..242eacd4181b 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -12,7 +12,6 @@
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple4D.Quaternion;
-import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import us.ihmc.matrixlib.MatrixTools;
import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
@@ -39,6 +38,7 @@
public class OdometryKalmanFilter extends ExtendedKalmanFilter
{
static final boolean includeBias = false;
+ static final boolean usePredictedStateInJacobian = true;
// Constants and providers
private static final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
@@ -471,7 +471,7 @@ protected DMatrixRMaj linearizeProcessModel(DMatrixRMaj previousState)
for (int i = 0; i < feetIMUs.size() + 1; i++)
{
- processModels.get(i).computeProcessJacobian(baseProcessState, estimatorDT, offset, AMatrix);
+ processModels.get(i).computeProcessJacobian(offset, AMatrix);
offset += errorSizePerLink;
}
@@ -488,25 +488,12 @@ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
for (int i = 0; i < feetIMUs.size(); i++)
{
MeasurementModel measurementModel = measurementModels.get(i);
- measurementModel.computeBaseMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetSensing.get(i), rowOffset, CMatrix);
- measurementModel.computeFootMeasurementJacobian(basePredictedState, feetPredictedState.get(i), feetSensing.get(i), gravityVector, rowOffset, colOffset, CMatrix);
+ measurementModel.computeBaseMeasurementJacobian(rowOffset, CMatrix);
+ measurementModel.computeFootMeasurementJacobian(rowOffset, colOffset, CMatrix);
rowOffset += measurementSizePerLink;
colOffset += errorSizePerLink;
}
return CMatrix;
}
-
- static void l3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
- {
- OdometryTools.toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
- CommonOps_DDRM.addEquals(matrixToPack, quaternion.getS(), eye3x3);
- }
-
- static void r3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
- {
- OdometryTools.toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
- CommonOps_DDRM.scale(-1.0, matrixToPack);
- CommonOps_DDRM.addEquals(matrixToPack, quaternion.getS(), eye3x3);
- }
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
index fcdc140d3ea9..12c46880f1b7 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
@@ -1,15 +1,19 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
import us.ihmc.euclid.tools.EuclidCoreTools;
import us.ihmc.euclid.tuple3D.interfaces.Tuple3DReadOnly;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionBasics;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.matrixlib.MatrixTools;
public class OdometryTools
{
+ private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
+
static void toRotationMatrixInverse(double qx, double qy, double qz, double qs, DMatrixRMaj rotationMatrix)
{
double qx2 = qx * qx;
@@ -54,6 +58,17 @@ static void toSkewSymmetricMatrix(double x, double y, double z, DMatrixRMaj matr
matrixToPack.set(2, 1, x);
}
+ static void toSkewSymmetricMatrix(double x, double y, double z, int row, int col, DMatrixRMaj matrixToPack)
+ {
+ matrixToPack.zero();
+ matrixToPack.set(row, col + 1, -z);
+ matrixToPack.set(row, col + 2, y);
+ matrixToPack.set(row + 1, col, z);
+ matrixToPack.set(row + 1, col + 2, -x);
+ matrixToPack.set(row + 2, col, -y);
+ matrixToPack.set(row + 2, col + 1, x);
+ }
+
static void toSkewSymmetricMatrix(Tuple3DReadOnly vector, DMatrixRMaj matrixToPack)
{
toSkewSymmetricMatrix(vector.getX(), vector.getY(), vector.getZ(), matrixToPack);
@@ -140,4 +155,141 @@ public static void logMapSmallAngle(QuaternionReadOnly quaternion, Vector3DBasic
rotationToPack.set(quaternion.getX(), quaternion.getY(), quaternion.getZ());
rotationToPack.scale(2.0);
}
+
+ static DMatrixRMaj lOperator(DMatrixRMaj quaternion)
+ {
+ return lOperator(quaternion.get(0), quaternion.get(1), quaternion.get(2), quaternion.get(3));
+ }
+
+ static DMatrixRMaj lOperator(QuaternionReadOnly quaternion)
+ {
+ return lOperator(quaternion.getS(), quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ }
+
+ static DMatrixRMaj lOperator(double qs, double qx, double qy, double qz)
+ {
+ DMatrixRMaj lOperator = new DMatrixRMaj(4, 4);
+ lOperator(qs, qx, qy, qz, lOperator);
+ return lOperator;
+ }
+
+ static void lOperator(QuaternionReadOnly quaternion, DMatrixRMaj lOperatorToPack)
+ {
+ lOperator(quaternion.getS(), quaternion.getX(), quaternion.getY(), quaternion.getZ(), lOperatorToPack);
+ }
+
+ static void lOperator(double qs, double qx, double qy, double qz, DMatrixRMaj lOperatorToPack)
+ {
+ toSkewSymmetricMatrix(qx, qy, qz, 1, 1, lOperatorToPack);
+ for (int i = 1; i < 4; i++)
+ lOperatorToPack.add(i, i, qs);
+ lOperatorToPack.set(0, 0, qs);
+ lOperatorToPack.set(0, 1, -qx);
+ lOperatorToPack.set(0, 2, -qy);
+ lOperatorToPack.set(0, 3, -qz);
+ lOperatorToPack.set(1, 0, qx);
+ lOperatorToPack.set(2, 0, qy);
+ lOperatorToPack.set(3, 0, qz);
+ }
+
+ static DMatrixRMaj rOperator(DMatrixRMaj quaternion)
+ {
+ return rOperator(quaternion.get(0), quaternion.get(1), quaternion.get(2), quaternion.get(3));
+ }
+
+ static DMatrixRMaj rOperator(QuaternionReadOnly quaternion)
+ {
+ return rOperator(quaternion.getS(), quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ }
+
+ static DMatrixRMaj rOperator(double qs, double qx, double qy, double qz)
+ {
+ DMatrixRMaj rOperator = new DMatrixRMaj(4, 4);
+ rOperator(qs, qx, qy, qz, rOperator);
+ return rOperator;
+ }
+
+ static void rOperator(QuaternionReadOnly quaternion, DMatrixRMaj lOperatorToPack)
+ {
+ rOperator(quaternion.getS(), quaternion.getX(), quaternion.getY(), quaternion.getZ(), lOperatorToPack);
+ }
+
+ static void rOperator(double qs, double qx, double qy, double qz, DMatrixRMaj rOperatorToPack)
+ {
+ toSkewSymmetricMatrix(-qx, -qy, -qz, 1, 1, rOperatorToPack);
+ for (int i = 1; i < 4; i++)
+ rOperatorToPack.add(i, i, qs);
+ rOperatorToPack.set(0, 0, qs);
+ rOperatorToPack.set(0, 1, -qx);
+ rOperatorToPack.set(0, 2, -qy);
+ rOperatorToPack.set(0, 3, -qz);
+ rOperatorToPack.set(1, 0, qx);
+ rOperatorToPack.set(2, 0, qy);
+ rOperatorToPack.set(3, 0, qz);
+ }
+
+ static void l3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
+ {
+ toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
+ MatrixTools.addDiagonal(matrixToPack, quaternion.getS());
+ }
+
+ static void r3Operator(QuaternionReadOnly quaternion, DMatrixRMaj matrixToPack)
+ {
+ toSkewSymmetricMatrix(quaternion.getX(), quaternion.getY(), quaternion.getZ(), matrixToPack);
+ CommonOps_DDRM.scale(-1.0, matrixToPack);
+ MatrixTools.addDiagonal(matrixToPack, quaternion.getS());
+ }
+
+ static DMatrixRMaj exponentialMap(DMatrixRMaj vector)
+ {
+ return exponentialMap(vector.get(0), vector.get(1), vector.get(2));
+ }
+
+ static DMatrixRMaj exponentialMap(Vector3DReadOnly vector)
+ {
+ return exponentialMap(vector.getX(), vector.getY(), vector.getZ());
+ }
+
+ static DMatrixRMaj exponentialMap(double x, double y, double z)
+ {
+ DMatrixRMaj map = new DMatrixRMaj(4, 1);
+
+ double length = EuclidCoreTools.norm(x, y, z);
+
+ if (Math.abs(length) < 1e-5 )
+ {
+ map.set(0, 0, 1.0);
+ return map;
+ }
+ double s = Math.sin(length / 2.0);
+ map.set(0, 0, Math.cos(length / 2.0));
+ map.set(1, 0, s * x / length);
+ map.set(2, 0, s * y / length);
+ map.set(3, 0, s * z / length);
+
+ return map;
+ }
+
+ static DMatrixRMaj logMap(QuaternionReadOnly quaternion)
+ {
+ return logMap(quaternion.getS(), quaternion.getX(), quaternion.getY(), quaternion.getZ());
+ }
+
+ static DMatrixRMaj logMap(DMatrixRMaj quaternion)
+ {
+ return logMap(quaternion.get(0), quaternion.get(1), quaternion.get(2), quaternion.get(3));
+ }
+
+ static DMatrixRMaj logMap(double qs, double qx, double qy, double qz)
+ {
+ double length = EuclidCoreTools.norm(qx, qy, qz);
+ double multiplier = 2.0 * Math.atan2(length, qs) / length;
+ DMatrixRMaj ret = new DMatrixRMaj(3, 1);
+ ret.set(0, qx * multiplier);
+ ret.set(1, qy * multiplier);
+ ret.set(2, qz * multiplier);
+
+ return ret;
+ }
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
index 754597fd6aaf..b08887592814 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModel.java
@@ -6,6 +6,7 @@
import us.ihmc.euclid.tools.QuaternionTools;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.log.LogTools;
import us.ihmc.matrixlib.MatrixTools;
import us.ihmc.yoVariables.providers.BooleanProvider;
@@ -16,6 +17,8 @@ class ProcessModel
private static final DMatrixRMaj eye3x3 = CommonOps_DDRM.identity(3);
private final DMatrixRMaj deltaT;
+ private static final boolean usePredictedInJacobian = true;
+
// State providers
private final StateVariables currentState;
private final StateVariables predictedState;
@@ -77,8 +80,9 @@ public void update()
/**
* This is equation 39
*/
- public void computeProcessJacobian(StateVariables stateVariables, double estimateDT, int offset, DMatrixRMaj jacobianToPack)
+ public void computeProcessJacobian(int offset, DMatrixRMaj jacobianToPack)
{
+ StateVariables stateVariables = OdometryKalmanFilter.usePredictedStateInJacobian ? predictedState : currentState;
// First row. Partial derivative with respect to the base position state
// p_k+1 = p_k + v_k deltaT
int rowStart = offset + errorTranslationIndex;
@@ -90,18 +94,19 @@ public void computeProcessJacobian(StateVariables stateVariables, double estimat
rowStart = offset + errorLinearVelocityIndex;
CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + errorLinearVelocityIndex);
+
OdometryTools.toRotationMatrix(stateVariables.orientation, rotationMatrix);
OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedAccel, skewMatrix);
- MatrixTools.multAddBlock(-estimateDT, rotationMatrix, skewMatrix, jacobianToPack, rowStart, offset + errorOrientationIndex);
+ MatrixTools.multAddBlock(-estimatorDt, rotationMatrix, skewMatrix, jacobianToPack, rowStart, offset + errorOrientationIndex);
if (OdometryKalmanFilter.includeBias)
- MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + errorAccelBiasIndex, rotationMatrix, 0, 0, 3, 3, -estimateDT);
+ MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + errorAccelBiasIndex, rotationMatrix, 0, 0, 3, 3, -estimatorDt);
// Third row. Partial derivative with respect to the orientation state
rowStart = offset + errorOrientationIndex;
OdometryTools.toSkewSymmetricMatrix(stateVariables.unbiasedGyro, skewMatrix);
CommonOps_DDRM.insert(eye3x3, jacobianToPack, rowStart, offset + errorOrientationIndex);
- MatrixTools.addMatrixBlock(jacobianToPack, rowStart, offset + errorOrientationIndex, skewMatrix, 0, 0, 3, 3, -estimateDT);
+ MatrixTools.addMatrixBlock(jacobianToPack, rowStart, offset + errorOrientationIndex, skewMatrix, 0, 0, 3, 3, -estimatorDt);
if (OdometryKalmanFilter.includeBias)
{
MatrixTools.setMatrixBlock(jacobianToPack, rowStart, offset + errorGyroBiasIndex, deltaT, 0, 0, 3, 3, -1.0);
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
index 1980cc940f47..12e24dc5bd9e 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/StateVariables.java
@@ -47,7 +47,7 @@ public void initialize()
gyroBias.setToZero();
}
- public void set(int start, DMatrixRMaj state, SensedVariables measuredVariables)
+ public void set(int start, DMatrixRMaj state, SensedVariables sensedVariables)
{
translation.set(start + stateTranslationIndex, state);
linearVelocity.set(start + stateLinearVelocityIndex, state);
@@ -57,13 +57,13 @@ public void set(int start, DMatrixRMaj state, SensedVariables measuredVariables)
if (OdometryKalmanFilter.includeBias)
{
- unbiasedAccel.sub(measuredVariables.accelMeasurement, accelBias);
- unbiasedGyro.sub(measuredVariables.gyroMeasurement, gyroBias);
+ unbiasedAccel.sub(sensedVariables.accelMeasurement, accelBias);
+ unbiasedGyro.sub(sensedVariables.gyroMeasurement, gyroBias);
}
else
{
- unbiasedAccel.set(measuredVariables.accelMeasurement);
- unbiasedGyro.set(measuredVariables.gyroMeasurement);
+ unbiasedAccel.set(sensedVariables.accelMeasurement);
+ unbiasedGyro.set(sensedVariables.gyroMeasurement);
}
}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
new file mode 100644
index 000000000000..3107796d2959
--- /dev/null
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
@@ -0,0 +1,325 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.EjmlUnitTests;
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import org.junit.jupiter.api.Test;
+import us.ihmc.euclid.geometry.Pose3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.tools.EuclidCoreRandomTools;
+import us.ihmc.euclid.transform.RigidBodyTransform;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
+import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
+import us.ihmc.mecano.spatial.Twist;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class MeasurementModelTest
+{
+ private static final double gravityZ = 9.81;
+ private static final FrameVector3D gravity = new FrameVector3D(ReferenceFrame.getWorldFrame(), 0.0, 0.0, gravityZ);
+ private static final double estimatorDt = 0.01;
+
+ @Test
+ public void testUpdate()
+ {
+ fail("Need to write this.");
+ }
+ @Test
+ public void testComputeMeasurementJacobian()
+ {
+ int iters = 100;
+ Random random = new Random(1738L);
+
+ YoRegistry test = new YoRegistry(getClass().getSimpleName());
+ TestIMU baseIMU = new TestIMU("base");
+ TestIMU footIMU = new TestIMU("foot");
+
+ StateVariables baseState = new StateVariables("base", baseIMU.getMeasurementFrame(), test);
+ StateVariables footState = new StateVariables("foot", footIMU.getMeasurementFrame(), test);
+ SensedVariables baseSensing = new SensedVariables("baseSensed", baseIMU, baseIMU, new TestBiasProvider(), test);
+ SensedVariables footSensing = new SensedVariables("footSensed", baseIMU, footIMU, new TestBiasProvider(), test);
+ MeasurementVariables measurement = new MeasurementVariables("footMeasure", test);
+
+ MeasurementModel processModel = new MeasurementModel("foot0", baseState, footState, footSensing, measurement, gravity, () -> Double.MAX_VALUE, test);
+ DMatrixRMaj jacobian = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink * 2);
+
+ for (int iter = 0; iter < iters; iter++)
+ {
+ jacobian.zero();
+
+ OdomTestTools.setRandomSensed(random, footSensing);
+ footIMU.linearAcceleration.set(footSensing.accelMeasurement);
+ footIMU.angularVelocity.set(footSensing.gyroMeasurement);
+ footIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ footIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ footIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomSensed(random, baseSensing);
+ baseIMU.linearAcceleration.set(baseSensing.accelMeasurement);
+ baseIMU.angularVelocity.set(baseSensing.gyroMeasurement);
+ baseIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ baseIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ baseIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomState(random, baseSensing, baseState);
+ OdomTestTools.setRandomState(random, footSensing, footState);
+
+ processModel.computeBaseMeasurementJacobian( 0, jacobian);
+ processModel.computeFootMeasurementJacobian( 0, OdometryIndexHelper.errorSizePerLink, jacobian);
+
+ DMatrixRMaj jacobianExpected = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, 2 * OdometryIndexHelper.errorSizePerLink);
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj baseRotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj baseRotationMatrixTranspose = new DMatrixRMaj(3, 3);
+ DMatrixRMaj footRotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj footRotationMatrixTranspose = new DMatrixRMaj(3, 3);
+ OdometryTools.toRotationMatrix(baseState.orientation, baseRotationMatrix);
+ OdometryTools.toRotationMatrix(footState.orientation, footRotationMatrix);
+ CommonOps_DDRM.transpose(baseRotationMatrix, baseRotationMatrixTranspose);
+ CommonOps_DDRM.transpose(footRotationMatrix, footRotationMatrixTranspose);
+
+ // Row 1, base
+ MatrixTools.setMatrixBlock(jacobianExpected, 0, 0, baseRotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ DMatrixRMaj footPosition = new DMatrixRMaj(3, 1);
+ DMatrixRMaj basePosition = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footOffset = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footOffsetInBaseFrame = new DMatrixRMaj(3, 1);
+ footState.translation.get(footPosition);
+ baseState.translation.get(basePosition);
+ CommonOps_DDRM.subtract(footPosition, basePosition, footOffset);
+ CommonOps_DDRM.multTransA(baseRotationMatrix, footOffset, footOffsetInBaseFrame);
+ OdometryTools.toSkewSymmetricMatrix(footOffsetInBaseFrame.get(0), footOffsetInBaseFrame.get(1), footOffsetInBaseFrame.get(2), skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianExpected, 0, 6);
+
+ // Row 1, foot
+ CommonOps_DDRM.insert(baseRotationMatrixTranspose, jacobianExpected, 0, 15);
+
+ // Row 2, base
+ DMatrixRMaj left = OdometryTools.lOperator(multiplyLeftInverse(footState.orientation, baseState.orientation));
+ DMatrixRMaj right = OdometryTools.rOperator(footSensing.orientationMeasurement);
+ DMatrixRMaj block = new DMatrixRMaj(4, 4);
+ CommonOps_DDRM.mult(left, right, block);
+ MatrixTools.setMatrixBlock(jacobianExpected, 3, 6, block, 1, 1, 3, 3, -1.0);
+
+ // Row 2, foot
+ block = OdometryTools.lOperator(multiply(footSensing.orientationMeasurement, fromVector(multiplyLeftInverse(baseState.orientation, footState.orientation))));
+ MatrixTools.setMatrixBlock(jacobianExpected, 3, 21, block, 1, 1, 3, 3, 1.0);
+
+ // Row 3, base
+ MatrixTools.setMatrixBlock(jacobianExpected, 6, 3, baseRotationMatrixTranspose, 0, 0, 3, 3, -1.0);
+ DMatrixRMaj footVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj velocityError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj velocityErrorInBaseFrame = new DMatrixRMaj(3, 1);
+ footState.linearVelocity.get(footVelocity);
+ baseState.linearVelocity.get(baseVelocity);
+ CommonOps_DDRM.subtract(footVelocity, baseVelocity, velocityError);
+ CommonOps_DDRM.multTransA(baseRotationMatrix, velocityError, velocityErrorInBaseFrame);
+ OdometryTools.toSkewSymmetricMatrix(velocityErrorInBaseFrame.get(0), velocityErrorInBaseFrame.get(1), velocityErrorInBaseFrame.get(2), skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianExpected, 6, 6);
+ if (OdometryKalmanFilter.includeBias)
+ {
+ OdometryTools.toSkewSymmetricMatrix(footState.translation, skewMatrix);
+ MatrixTools.setMatrixBlock(jacobianExpected, 6, 12, skewMatrix, 0, 0, 3, 3, -1.0);
+ }
+
+ // Row 3, foot
+ CommonOps_DDRM.insert(baseRotationMatrixTranspose, jacobianExpected, 6, 18);
+
+ // Row 4, foot
+ if (OdometryKalmanFilter.includeBias)
+ {
+ for (int i = 0; i < 3; i++)
+ jacobianExpected.set(9 + i, 18 + i, 1.0);
+ }
+
+ // Row 5, foot
+ if (OdometryKalmanFilter.includeBias)
+ {
+ DMatrixRMaj gravityVector = new DMatrixRMaj(3, 1);
+ DMatrixRMaj rotatedGravityVector = new DMatrixRMaj(3, 1);
+ gravity.get(gravityVector);
+ CommonOps_DDRM.multTransA(footRotationMatrix, gravityVector, rotatedGravityVector);
+ OdometryTools.toSkewSymmetricMatrix(rotatedGravityVector.get(0), rotatedGravityVector.get(1), rotatedGravityVector.get(2), skewMatrix);
+ CommonOps_DDRM.insert(skewMatrix, jacobianExpected, 12, 21);
+
+ for (int i = 0; i < 3; i++)
+ jacobianExpected.set(12 + i, 24 + i, 1.0);
+ }
+
+ EjmlUnitTests.assertEquals(jacobianExpected, jacobian, 1e-5);
+ }
+ }
+
+ static DMatrixRMaj multiply(QuaternionReadOnly a, QuaternionReadOnly b)
+ {
+ DMatrixRMaj vector = new DMatrixRMaj(4, 1);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(a), toVector(b), vector);
+
+ return vector;
+ }
+
+ static DMatrixRMaj multiplyLeftInverse(QuaternionReadOnly a, QuaternionReadOnly b)
+ {
+ DMatrixRMaj vector = new DMatrixRMaj(4, 1);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(a.getS(), -a.getX(), -a.getY(), -a.getZ()), toVector(b), vector);
+
+ return vector;
+ }
+
+ static Quaternion fromVector(DMatrixRMaj quaternion)
+ {
+ return new Quaternion(quaternion.get(1), quaternion.get(2), quaternion.get(3), quaternion.get(0));
+ }
+
+ static DMatrixRMaj toVector(QuaternionReadOnly quaternionReadOnly)
+ {
+ DMatrixRMaj vector = new DMatrixRMaj(4, 1);
+ vector.set(0, quaternionReadOnly.getS());
+ vector.set(1, quaternionReadOnly.getX());
+ vector.set(2, quaternionReadOnly.getY());
+ vector.set(3, quaternionReadOnly.getZ());
+
+ return vector;
+ }
+
+
+
+ static class TestBiasProvider implements IMUBiasProvider
+ {
+
+ @Override
+ public FrameVector3DReadOnly getAngularVelocityBiasInIMUFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D(imu.getMeasurementFrame());
+ }
+
+ @Override
+ public FrameVector3DReadOnly getAngularVelocityBiasInWorldFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D();
+ }
+
+ @Override
+ public FrameVector3DReadOnly getLinearAccelerationBiasInIMUFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D(imu.getMeasurementFrame());
+ }
+
+ @Override
+ public FrameVector3DReadOnly getLinearAccelerationBiasInWorldFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D();
+ }
+ }
+
+ static class TestIMU implements IMUSensorReadOnly
+ {
+ private String name;
+ public final MovingReferenceFrame sensorFrame;
+ public final Pose3D pose = new Pose3D();
+ public final Vector3D angularVelocity = new Vector3D();
+ public final Vector3D linearVelocity = new Vector3D();
+ public final Vector3D linearAcceleration = new Vector3D();
+
+ public TestIMU(String name)
+ {
+ this.name = name;
+
+ sensorFrame = new MovingReferenceFrame(name, ReferenceFrame.getWorldFrame())
+ {
+ @Override
+ protected void updateTwistRelativeToParent(Twist twist)
+ {
+ twist.setToZero();
+ pose.transform(linearVelocity, twist.getLinearPart());
+ }
+
+ @Override
+ protected void updateTransformToParent(RigidBodyTransform rigidBodyTransform)
+ {
+ rigidBodyTransform.set(pose);
+ }
+ };
+ }
+
+ @Override
+ public String getSensorName()
+ {
+ return name;
+ }
+
+ @Override
+ public MovingReferenceFrame getMeasurementFrame()
+ {
+ return sensorFrame;
+ }
+
+ @Override
+ public RigidBodyBasics getMeasurementLink()
+ {
+ return null;
+ }
+
+ @Override
+ public QuaternionReadOnly getOrientationMeasurement()
+ {
+ return pose.getOrientation();
+ }
+
+ @Override
+ public Vector3DReadOnly getAngularVelocityMeasurement()
+ {
+ return angularVelocity;
+ }
+
+ @Override
+ public Vector3DReadOnly getLinearAccelerationMeasurement()
+ {
+ return linearAcceleration;
+ }
+
+ @Override
+ public void getOrientationNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getAngularVelocityNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getAngularVelocityBiasProcessNoiseCovariance(DMatrixRMaj biasProcessNoiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getLinearAccelerationNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getLinearAccelerationBiasProcessNoiseCovariance(DMatrixRMaj biasProcessNoiseCovarianceToPack)
+ {
+
+ }
+ }
+}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
new file mode 100644
index 000000000000..7c4dbf2ead66
--- /dev/null
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
@@ -0,0 +1,48 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.data.DMatrixRMaj;
+import us.ihmc.euclid.tools.EuclidCoreRandomTools;
+import us.ihmc.euclid.tools.EuclidCoreTools;
+
+import java.util.Random;
+
+public class OdomTestTools
+{
+ public static void setRandomSensed(Random random, SensedVariables sensedVariablesToPack)
+ {
+ sensedVariablesToPack.gyroMeasurement.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+ sensedVariablesToPack.accelMeasurement.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+ sensedVariablesToPack.positionMeasurement.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+ sensedVariablesToPack.orientationMeasurement.set(EuclidCoreRandomTools.nextQuaternion(random, 5.0));
+ sensedVariablesToPack.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+ }
+
+ public static DMatrixRMaj setRandomState(Random random, SensedVariables sensedVariables, StateVariables stateVariablesToPack)
+ {
+ stateVariablesToPack.translation.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0));
+ stateVariablesToPack.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ stateVariablesToPack.orientation.set(EuclidCoreRandomTools.nextQuaternion(random));
+ stateVariablesToPack.accelBias.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+ stateVariablesToPack.gyroBias.set(EuclidCoreRandomTools.nextVector3D(random, 5.0));
+
+ if (OdometryKalmanFilter.includeBias)
+ {
+ stateVariablesToPack.unbiasedAccel.sub(sensedVariables.accelMeasurement, stateVariablesToPack.accelBias);
+ stateVariablesToPack.unbiasedGyro.sub(sensedVariables.gyroMeasurement, stateVariablesToPack.gyroBias);
+ }
+ else
+ {
+ stateVariablesToPack.unbiasedAccel.set(sensedVariables.accelMeasurement);
+ stateVariablesToPack.unbiasedGyro.set(sensedVariables.gyroMeasurement);
+ }
+
+ DMatrixRMaj stateVector = new DMatrixRMaj(OdometryIndexHelper.stateSizePerLink, 1);
+ stateVariablesToPack.translation.get(stateVector);
+ stateVariablesToPack.linearVelocity.get(3, stateVector);
+ stateVariablesToPack.orientation.get(6, stateVector);
+ stateVariablesToPack.accelBias.get(10, stateVector);
+ stateVariablesToPack.gyroBias.get(13, stateVector);
+
+ return stateVector;
+ }
+}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
index 76d58f545c32..741ec03ed7df 100644
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
@@ -10,6 +10,7 @@
import us.ihmc.euclid.tools.EuclidCoreTestTools;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.matrixlib.MatrixTools;
import java.util.Random;
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
new file mode 100644
index 000000000000..229b7cab346f
--- /dev/null
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
@@ -0,0 +1,347 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import org.ejml.EjmlUnitTests;
+import org.ejml.data.DMatrixRMaj;
+import org.ejml.dense.row.CommonOps_DDRM;
+import org.junit.jupiter.api.Test;
+import us.ihmc.euclid.geometry.Pose3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.tools.EuclidCoreRandomTools;
+import us.ihmc.euclid.transform.RigidBodyTransform;
+import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
+import us.ihmc.log.LogTools;
+import us.ihmc.matrixlib.MatrixTools;
+import us.ihmc.mecano.frames.MovingReferenceFrame;
+import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
+import us.ihmc.mecano.spatial.Twist;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+import java.util.Random;
+
+public class ProcessModelTest
+{
+ private static final double gravityZ = 9.81;
+ private static final FrameVector3D gravity = new FrameVector3D(ReferenceFrame.getWorldFrame(), 0.0, 0.0, gravityZ);
+ private static final double estimatorDt = 0.01;
+
+ @Test
+ public void testUpdate()
+ {
+ int iters = 100;
+ Random random = new Random(1738L);
+
+ YoRegistry test = new YoRegistry(getClass().getSimpleName());
+ TestIMU baseIMU = new TestIMU("base");
+ TestIMU footIMU = new TestIMU("foot");
+
+ StateVariables processState = new StateVariables("process", footIMU.getMeasurementFrame(), test);
+ StateVariables predictedState = new StateVariables("predicted", footIMU.getMeasurementFrame(), test);
+ SensedVariables sensedVariables = new SensedVariables("foot", baseIMU, footIMU, new TestBiasProvider(), test);
+
+ ProcessModel processModel = new ProcessModel(processState, predictedState, () -> true, gravity, estimatorDt);
+ DMatrixRMaj jacobian = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink);
+
+ for (int iter = 0; iter < iters; iter++)
+ {
+ OdomTestTools.setRandomSensed(random, sensedVariables);
+ footIMU.linearAcceleration.set(sensedVariables.accelMeasurement);
+ footIMU.angularVelocity.set(sensedVariables.gyroMeasurement);
+ footIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ footIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ footIMU.sensorFrame.update();
+ baseIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ baseIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ baseIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomState(random, sensedVariables, processState);
+ OdomTestTools.setRandomState(random, sensedVariables, predictedState);
+
+ processModel.update();
+
+ DMatrixRMaj translation = new DMatrixRMaj(3, 1);
+ DMatrixRMaj linearVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj gravityVector = new DMatrixRMaj(3, 1);
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj acceleration = new DMatrixRMaj(3, 1);
+ DMatrixRMaj accelerationBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj accelerationUnbiased = new DMatrixRMaj(3, 1);
+ DMatrixRMaj accelerationUnbiasedInWorld = new DMatrixRMaj(3, 1);
+ DMatrixRMaj gyro = new DMatrixRMaj(3, 1);
+ DMatrixRMaj gyroBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj gyroUnbiased = new DMatrixRMaj(3, 1);
+
+ processState.translation.get(translation);
+ processState.linearVelocity.get(linearVelocity);
+ OdometryTools.toRotationMatrix(processState.orientation, rotationMatrix);
+ gravity.get(gravityVector);
+ sensedVariables.accelMeasurement.get(acceleration);
+ processState.accelBias.get(accelerationBias);
+ sensedVariables.gyroMeasurement.get(gyro);
+ processState.gyroBias.get(gyroBias);
+
+ DMatrixRMaj expectedPredictedTranslation = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedLinearVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedRotation = new DMatrixRMaj(4, 1);
+ DMatrixRMaj expectedPredictedAccelBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedGyroBias = new DMatrixRMaj(3, 1);
+
+ // First term
+ CommonOps_DDRM.add(estimatorDt, linearVelocity, translation, expectedPredictedTranslation);
+
+ // Second term
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.subtract(acceleration, accelerationBias, accelerationUnbiased);
+ else
+ accelerationUnbiased.set(acceleration);
+ CommonOps_DDRM.mult(rotationMatrix, accelerationUnbiased, accelerationUnbiasedInWorld);
+ CommonOps_DDRM.subtractEquals(accelerationUnbiasedInWorld, gravityVector);
+ CommonOps_DDRM.add(estimatorDt, accelerationUnbiasedInWorld, linearVelocity, expectedPredictedLinearVelocity);
+
+ // Third term
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.subtract(gyro, gyroBias, gyroUnbiased);
+ else
+ gyroUnbiased.set(gyro);
+ CommonOps_DDRM.scale(estimatorDt, gyroUnbiased);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(processState.orientation), OdometryTools.exponentialMap(gyroUnbiased), expectedPredictedRotation);
+
+ // Fourth term
+ expectedPredictedAccelBias.set(accelerationBias);
+
+ // Fifth term
+ expectedPredictedGyroBias.set(gyroBias);
+
+ DMatrixRMaj predictedTranslation = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedLinearVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedRotation = new DMatrixRMaj(4, 1);
+ DMatrixRMaj predictedAccelBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedGyroBias = new DMatrixRMaj(3, 1);
+
+ predictedState.translation.get(predictedTranslation);
+ predictedState.linearVelocity.get(predictedLinearVelocity);
+ predictedState.orientation.get(predictedRotation);
+ predictedState.accelBias.get(predictedAccelBias);
+ predictedState.gyroBias.get(predictedGyroBias);
+
+ EjmlUnitTests.assertEquals(expectedPredictedTranslation, predictedTranslation, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedLinearVelocity, predictedLinearVelocity, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedRotation, predictedRotation, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedAccelBias, predictedAccelBias, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedGyroBias, predictedGyroBias, 1e-6);
+ }
+ }
+ @Test
+ public void testComputeProcessJacobian()
+ {
+ int iters = 100;
+ Random random = new Random(1738L);
+
+ YoRegistry test = new YoRegistry(getClass().getSimpleName());
+ TestIMU baseIMU = new TestIMU("base");
+ TestIMU footIMU = new TestIMU("foot");
+
+ StateVariables processState = new StateVariables("process", footIMU.getMeasurementFrame(), test);
+ StateVariables predictedState = new StateVariables("predicted", footIMU.getMeasurementFrame(), test);
+ SensedVariables sensedVariables = new SensedVariables("foot", baseIMU, footIMU, new TestBiasProvider(), test);
+
+ ProcessModel processModel = new ProcessModel(processState, predictedState, () -> true, gravity, estimatorDt);
+ DMatrixRMaj jacobian = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink);
+
+ for (int iter = 0; iter < iters; iter++)
+ {
+ jacobian.zero();
+ OdomTestTools.setRandomSensed(random, sensedVariables);
+ footIMU.linearAcceleration.set(sensedVariables.accelMeasurement);
+ footIMU.angularVelocity.set(sensedVariables.gyroMeasurement);
+ footIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ footIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ footIMU.sensorFrame.update();
+ baseIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ baseIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ baseIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomState(random, sensedVariables, processState);
+ OdomTestTools.setRandomState(random, sensedVariables, predictedState);
+
+ processModel.computeProcessJacobian( 0, jacobian);
+
+ DMatrixRMaj jacobianExpected = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink);
+ // Row 1
+ CommonOps_DDRM.setIdentity(jacobianExpected);
+ for (int i = 0; i < 3; i++)
+ jacobianExpected.set(i, i + 3, estimatorDt);
+
+ // Row 2
+ DMatrixRMaj rotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+ OdometryTools.toRotationMatrix(predictedState.orientation, rotationMatrix);
+ DMatrixRMaj acceleration = new DMatrixRMaj(3, 1);
+ DMatrixRMaj accelerationBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj accelerationUnbiased = new DMatrixRMaj(3, 1);
+ sensedVariables.accelMeasurement.get(acceleration);
+ predictedState.accelBias.get(accelerationBias);
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.subtract(acceleration, accelerationBias, accelerationUnbiased);
+ else
+ accelerationUnbiased.set(acceleration);
+ CommonOps_DDRM.scale(estimatorDt, accelerationUnbiased);
+ OdometryTools.toSkewSymmetricMatrix(accelerationUnbiased.get(0), accelerationUnbiased.get(1), accelerationUnbiased.get(2), skewMatrix);
+ MatrixTools.multAddBlock(-1.0, rotationMatrix, skewMatrix, jacobianExpected, 3, 6);
+ if (OdometryKalmanFilter.includeBias)
+ MatrixTools.setMatrixBlock(jacobianExpected, 3, 9, rotationMatrix, 0, 0, 3, 3, -estimatorDt);
+
+ // Row 3
+ DMatrixRMaj velocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj velocityBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj velocityUnbiased = new DMatrixRMaj(3, 1);
+ sensedVariables.gyroMeasurement.get(velocity);
+ predictedState.gyroBias.get(velocityBias);
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.subtract(velocity, velocityBias, velocityUnbiased);
+ else
+ velocityUnbiased.set(velocity);
+ CommonOps_DDRM.scale(estimatorDt, velocityUnbiased);
+ OdometryTools.toSkewSymmetricMatrix(velocityUnbiased.get(0), velocityUnbiased.get(1), velocityUnbiased.get(2), skewMatrix);
+ MatrixTools.addMatrixBlock(jacobianExpected, 6, 6, skewMatrix, 0, 0, 3, 3, -1.0);
+ if (OdometryKalmanFilter.includeBias)
+ {
+ for (int i = 0; i < 3; i++)
+ jacobianExpected.set(i + 6, i + 12, -estimatorDt);
+ }
+
+ // Row 4 and 5 are already set
+ EjmlUnitTests.assertEquals(jacobianExpected, jacobian, 1e-5);
+ }
+ }
+
+ static class TestBiasProvider implements IMUBiasProvider
+ {
+
+ @Override
+ public FrameVector3DReadOnly getAngularVelocityBiasInIMUFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D(imu.getMeasurementFrame());
+ }
+
+ @Override
+ public FrameVector3DReadOnly getAngularVelocityBiasInWorldFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D();
+ }
+
+ @Override
+ public FrameVector3DReadOnly getLinearAccelerationBiasInIMUFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D(imu.getMeasurementFrame());
+ }
+
+ @Override
+ public FrameVector3DReadOnly getLinearAccelerationBiasInWorldFrame(IMUSensorReadOnly imu)
+ {
+ return new FrameVector3D();
+ }
+ }
+
+ static class TestIMU implements IMUSensorReadOnly
+ {
+ private String name;
+ public final MovingReferenceFrame sensorFrame;
+ public final Pose3D pose = new Pose3D();
+ public final Vector3D angularVelocity = new Vector3D();
+ public final Vector3D linearVelocity = new Vector3D();
+ public final Vector3D linearAcceleration = new Vector3D();
+
+ public TestIMU(String name)
+ {
+ this.name = name;
+
+ sensorFrame = new MovingReferenceFrame(name, ReferenceFrame.getWorldFrame())
+ {
+ @Override
+ protected void updateTwistRelativeToParent(Twist twist)
+ {
+ twist.setToZero();
+ pose.transform(linearVelocity, twist.getLinearPart());
+ }
+
+ @Override
+ protected void updateTransformToParent(RigidBodyTransform rigidBodyTransform)
+ {
+ rigidBodyTransform.set(pose);
+ }
+ };
+ }
+
+ @Override
+ public String getSensorName()
+ {
+ return name;
+ }
+
+ @Override
+ public MovingReferenceFrame getMeasurementFrame()
+ {
+ return sensorFrame;
+ }
+
+ @Override
+ public RigidBodyBasics getMeasurementLink()
+ {
+ return null;
+ }
+
+ @Override
+ public QuaternionReadOnly getOrientationMeasurement()
+ {
+ return pose.getOrientation();
+ }
+
+ @Override
+ public Vector3DReadOnly getAngularVelocityMeasurement()
+ {
+ return angularVelocity;
+ }
+
+ @Override
+ public Vector3DReadOnly getLinearAccelerationMeasurement()
+ {
+ return linearAcceleration;
+ }
+
+ @Override
+ public void getOrientationNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getAngularVelocityNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getAngularVelocityBiasProcessNoiseCovariance(DMatrixRMaj biasProcessNoiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getLinearAccelerationNoiseCovariance(DMatrixRMaj noiseCovarianceToPack)
+ {
+
+ }
+
+ @Override
+ public void getLinearAccelerationBiasProcessNoiseCovariance(DMatrixRMaj biasProcessNoiseCovarianceToPack)
+ {
+
+ }
+ }
+}
From f7bd954ba71f16bec595f2268dc22e1ff0c85535 Mon Sep 17 00:00:00 2001
From: Robert
Date: Tue, 2 Sep 2025 22:54:35 -0500
Subject: [PATCH 09/18] fixed a couple of the last little stupid bugs
---
.../ExtendedKalmanFilter.java | 1 +
.../odomEKF/MeasurementModel.java | 5 +-
.../odomEKF/OdometryKalmanFilter.java | 10 +-
.../odomEKF/OdometryTools.java | 17 --
.../odomEKF/SensedVariables.java | 5 -
.../odomEKF/MeasurementModelTest.java | 164 +++++++++++++++---
.../odomEKF/OdomTestTools.java | 19 ++
.../odomEKF/OdometryToolsTest.java | 26 +++
.../odomEKF/ProcessModelTest.java | 3 +-
9 files changed, 192 insertions(+), 58 deletions(-)
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index 4b65b5f4cad3..2b460f0a9e3a 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -6,6 +6,7 @@
import org.ejml.dense.row.CommonOps_DDRM;
import org.ejml.dense.row.MatrixFeatures_DDRM;
import org.ejml.dense.row.factory.LinearSolverFactory_DDRM;
+import us.ihmc.log.LogTools;
/**
* This class provides an implementation of an Extended Kalman Filter (EKF). The EKF is a recursive filter that uses a linearization of the process and
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index 45473b51ac43..183aaae88e16 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -41,7 +41,6 @@ class MeasurementModel
private final DMatrixRMaj left = new DMatrixRMaj(4, 4);
private final DMatrixRMaj right = new DMatrixRMaj(4, 4);
private final DMatrixRMaj lr = new DMatrixRMaj(4, 4);
- private final DMatrixRMaj lr3x3 = new DMatrixRMaj(3, 3);
private final Quaternion tempRotation = new Quaternion();
public MeasurementModel(String prefix,
@@ -73,7 +72,7 @@ public void update(DMatrixRMaj footCovariance)
// First row. Update the predicted foot position of the foot relative to the base in the base frame
predictedMeasurements.relativePosition.sub(footState.translation, baseState.translation);
- baseState.orientation.transform(predictedMeasurements.relativePosition); // FIXME is this right? transforms do more operations than I think is expected
+ baseState.orientation.inverseTransform(predictedMeasurements.relativePosition);
// Update the predicted foot orientation of the foot relative to the base in the base frame
QuaternionTools.multiplyConjugateLeft(baseState.orientation, footState.orientation, footOrientationInBaseFrame);
@@ -98,7 +97,7 @@ public void update(DMatrixRMaj footCovariance)
predictedMeasurements.contactVelocity.set(footState.linearVelocity);
// Fifth row. Set the predicted gravity vector
- footState.orientation.transform(gravityVector, predictedMeasurements.accelMeasure);
+ footState.orientation.inverseTransform(gravityVector, predictedMeasurements.accelMeasure);
if (OdometryKalmanFilter.includeBias)
predictedMeasurements.accelMeasure.add(footState.accelBias);
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index 242eacd4181b..85bd3c291c1a 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -37,7 +37,7 @@
*/
public class OdometryKalmanFilter extends ExtendedKalmanFilter
{
- static final boolean includeBias = false;
+ static final boolean includeBias = true;
static final boolean usePredictedStateInJacobian = true;
// Constants and providers
@@ -134,10 +134,10 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
orientationCovariance.set(1e-2);
biasCovariance.set(1e-4);
- measuredTranslationCovariance.set(1e-5);
- measuredOrientationCovariance.set(1e-2);
- velocityErrorCovariance.set(1e-3);
- contactVelocityCovariance.set(1e-5);
+ measuredTranslationCovariance.set(1e-8);
+ measuredOrientationCovariance.set(1e-8);
+ velocityErrorCovariance.set(1e-5);
+ contactVelocityCovariance.set(1e-6);
contactAccelerationCovariance.set(1.0);
// translationCovariance.set(1e-4);
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
index 12c46880f1b7..93c8f3fa3f14 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryTools.java
@@ -113,23 +113,6 @@ static void setDiagonals(int start, int elements, double value, DMatrixRMaj matr
matrix.set(i, i, value);
}
- public static void toQuaternionFromRotationVector(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
- {
- double magnitude = rotation.norm();
- if (magnitude < 1e-7)
- {
- quaternionToPack.setToZero();
- return;
- }
-
- double s = Math.sin(magnitude * 0.5);
- double qs = Math.cos(magnitude * 0.5);
- double qx = s * rotation.getX() / magnitude;
- double qy = s * rotation.getY() / magnitude;
- double qz = s * rotation.getZ() / magnitude;
- quaternionToPack.set(qx, qy, qz, qs);
- }
-
public static void toQuaternionFromRotationVectorSmallAngle(Vector3DReadOnly rotation, QuaternionBasics quaternionToPack)
{
double magnitude = rotation.norm();
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
index 7d38a65007e5..a94c0c60a478 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
@@ -79,11 +79,6 @@ public void update()
positionMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
orientationMeasurement.setFromReferenceFrame(imu.getMeasurementFrame());
- FramePoint3D imuInWorld = new FramePoint3D(imu.getMeasurementFrame());
- FramePoint3D baseInWorld = new FramePoint3D(baseIMU.getMeasurementFrame());
- imuInWorld.changeFrame(worldFrame);
- baseInWorld.changeFrame(worldFrame);
-
imu.getMeasurementFrame().getTwistRelativeToOther(baseIMU.getMeasurementFrame(), twist);
twist.changeFrame(worldFrame);
linearVelocity.set(twist.getLinearPart());
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
index 3107796d2959..49bf96e8b6fc 100644
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModelTest.java
@@ -12,7 +12,6 @@
import us.ihmc.euclid.transform.RigidBodyTransform;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
-import us.ihmc.euclid.tuple4D.Quaternion;
import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import us.ihmc.matrixlib.MatrixTools;
import us.ihmc.mecano.frames.MovingReferenceFrame;
@@ -20,6 +19,8 @@
import us.ihmc.mecano.spatial.Twist;
import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.IMUBiasProvider;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.ProcessModelTest.TestBiasProvider;
+import us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.ProcessModelTest.TestIMU;
import us.ihmc.yoVariables.registry.YoRegistry;
import java.util.Random;
@@ -35,8 +36,133 @@ public class MeasurementModelTest
@Test
public void testUpdate()
{
- fail("Need to write this.");
+ int iters = 100;
+ Random random = new Random(1738L);
+
+ YoRegistry test = new YoRegistry(getClass().getSimpleName());
+ ProcessModelTest.TestIMU baseIMU = new ProcessModelTest.TestIMU("base");
+ ProcessModelTest.TestIMU footIMU = new ProcessModelTest.TestIMU("foot");
+
+ StateVariables baseState = new StateVariables("base", baseIMU.getMeasurementFrame(), test);
+ StateVariables footState = new StateVariables("foot", footIMU.getMeasurementFrame(), test);
+ SensedVariables baseSensing = new SensedVariables("baseSensed", baseIMU, baseIMU, new TestBiasProvider(), test);
+ SensedVariables footSensing = new SensedVariables("footSensed", baseIMU, footIMU, new TestBiasProvider(), test);
+ MeasurementVariables footMeasure = new MeasurementVariables("footMeasure", test);
+
+ MeasurementModel measurementModel = new MeasurementModel("foot0", baseState, footState, footSensing, footMeasure, gravity, () -> Double.MAX_VALUE, test);
+ DMatrixRMaj jacobian = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink * 2);
+
+ for (int iter = 0; iter < iters; iter++)
+ {
+ jacobian.zero();
+
+ OdomTestTools.setRandomSensed(random, footSensing);
+ footIMU.linearAcceleration.set(footSensing.accelMeasurement);
+ footIMU.angularVelocity.set(footSensing.gyroMeasurement);
+ footIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ footIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ footIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomSensed(random, baseSensing);
+ baseIMU.linearAcceleration.set(baseSensing.accelMeasurement);
+ baseIMU.angularVelocity.set(baseSensing.gyroMeasurement);
+ baseIMU.pose.set(EuclidCoreRandomTools.nextPoint3D(random, 10.0), EuclidCoreRandomTools.nextOrientation3D(random));
+ baseIMU.linearVelocity.set(EuclidCoreRandomTools.nextVector3D(random, 10.0));
+ baseIMU.sensorFrame.update();
+
+ OdomTestTools.setRandomState(random, baseSensing, baseState);
+ OdomTestTools.setRandomState(random, footSensing, footState);
+
+ measurementModel.update(new DMatrixRMaj(3, 3));
+
+ DMatrixRMaj baseTranslation = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footTranslation = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footRelativePosition = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseLinearVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footLinearVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj gravityVector = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseRotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj footRotationMatrix = new DMatrixRMaj(3, 3);
+ DMatrixRMaj baseAcceleration = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseAccelerationBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footAccelerationBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseGyro = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseGyroBias = new DMatrixRMaj(3, 1);
+ DMatrixRMaj baseGyroUnbiased = new DMatrixRMaj(3, 1);
+ DMatrixRMaj footVelocity = new DMatrixRMaj(3, 1);
+
+ DMatrixRMaj footOffset = new DMatrixRMaj(3, 1);
+ DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
+
+ baseState.translation.get(baseTranslation);
+ footState.translation.get(footTranslation);
+ footSensing.positionMeasurement.get(footRelativePosition);
+ baseState.linearVelocity.get(baseLinearVelocity);
+ footState.linearVelocity.get(footLinearVelocity);
+ OdometryTools.toRotationMatrix(baseState.orientation, baseRotationMatrix);
+ OdometryTools.toRotationMatrix(footState.orientation, footRotationMatrix);
+ gravity.get(gravityVector);
+ baseSensing.accelMeasurement.get(baseAcceleration);
+ baseState.accelBias.get(baseAccelerationBias);
+ footState.accelBias.get(footAccelerationBias);
+ baseSensing.gyroMeasurement.get(baseGyro);
+ baseState.gyroBias.get(baseGyroBias);
+
+ DMatrixRMaj expectedRelativeError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedLinearVelocityError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedRotationError = new DMatrixRMaj(4, 1);
+ DMatrixRMaj expectedPredictedContactVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj expectedPredictedContactAccel = new DMatrixRMaj(3, 1);
+
+ // First term
+ CommonOps_DDRM.subtract(footTranslation, baseTranslation, footOffset);
+ CommonOps_DDRM.multTransA(baseRotationMatrix, footOffset, expectedRelativeError);
+
+ // Second term
+ DMatrixRMaj rotation = multiplyLeftInverse(footSensing.orientationMeasurement, OdomTestTools.fromVector(multiplyLeftInverse(baseState.orientation, footState.orientation)));
+ expectedPredictedRotationError.set(OdometryTools.logMap(rotation));
+
+ // Third term
+ CommonOps_DDRM.subtract(baseLinearVelocity, footLinearVelocity, footVelocity);
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.subtract(baseGyro, baseGyroBias, baseGyroUnbiased);
+ else
+ baseGyroUnbiased.set(baseGyro);
+ OdometryTools.toSkewSymmetricMatrix(baseGyroUnbiased.get(0), baseGyroUnbiased.get(1), baseGyroUnbiased.get(2), skewMatrix);
+ footSensing.linearVelocity.get(expectedPredictedLinearVelocityError);
+ CommonOps_DDRM.multAdd(-1.0, skewMatrix, footRelativePosition, expectedPredictedLinearVelocityError);
+ CommonOps_DDRM.multAddTransA(-1.0, baseRotationMatrix, footVelocity, expectedPredictedLinearVelocityError);
+
+ // Fourth term
+ expectedPredictedContactVelocity.set(footLinearVelocity);
+
+
+ // Fifth term
+ CommonOps_DDRM.multTransA(footRotationMatrix, gravityVector, expectedPredictedContactAccel);
+ if (OdometryKalmanFilter.includeBias)
+ CommonOps_DDRM.addEquals(expectedPredictedContactAccel, footAccelerationBias);
+
+ DMatrixRMaj predictedRelativeError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedRotationError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedLinearVelocityError = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedContactVelocity = new DMatrixRMaj(3, 1);
+ DMatrixRMaj predictedContactAccel = new DMatrixRMaj(3, 1);
+
+ footMeasure.relativePosition.get(predictedRelativeError);
+ footMeasure.relativeOrientationError.get(predictedRotationError);
+ footMeasure.relativeLinearVelocityError.get(predictedLinearVelocityError);
+ footMeasure.contactVelocity.get(predictedContactVelocity);
+ footMeasure.accelMeasure.get(predictedContactAccel);
+
+ EjmlUnitTests.assertEquals(expectedRelativeError, predictedRelativeError, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedRotationError, predictedRotationError, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedLinearVelocityError, predictedLinearVelocityError, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedContactVelocity, predictedContactVelocity, 1e-6);
+ EjmlUnitTests.assertEquals(expectedPredictedContactAccel, predictedContactAccel, 1e-6);
+ }
}
+
+
@Test
public void testComputeMeasurementJacobian()
{
@@ -53,13 +179,15 @@ public void testComputeMeasurementJacobian()
SensedVariables footSensing = new SensedVariables("footSensed", baseIMU, footIMU, new TestBiasProvider(), test);
MeasurementVariables measurement = new MeasurementVariables("footMeasure", test);
- MeasurementModel processModel = new MeasurementModel("foot0", baseState, footState, footSensing, measurement, gravity, () -> Double.MAX_VALUE, test);
+ MeasurementModel measurementModel = new MeasurementModel("foot0", baseState, footState, footSensing, measurement, gravity, () -> Double.MAX_VALUE, test);
DMatrixRMaj jacobian = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, OdometryIndexHelper.errorSizePerLink * 2);
for (int iter = 0; iter < iters; iter++)
{
jacobian.zero();
+ measurementModel.isInContact.set(true);
+
OdomTestTools.setRandomSensed(random, footSensing);
footIMU.linearAcceleration.set(footSensing.accelMeasurement);
footIMU.angularVelocity.set(footSensing.gyroMeasurement);
@@ -77,8 +205,8 @@ public void testComputeMeasurementJacobian()
OdomTestTools.setRandomState(random, baseSensing, baseState);
OdomTestTools.setRandomState(random, footSensing, footState);
- processModel.computeBaseMeasurementJacobian( 0, jacobian);
- processModel.computeFootMeasurementJacobian( 0, OdometryIndexHelper.errorSizePerLink, jacobian);
+ measurementModel.computeBaseMeasurementJacobian( 0, jacobian);
+ measurementModel.computeFootMeasurementJacobian( 0, OdometryIndexHelper.errorSizePerLink, jacobian);
DMatrixRMaj jacobianExpected = new DMatrixRMaj(OdometryIndexHelper.errorSizePerLink, 2 * OdometryIndexHelper.errorSizePerLink);
DMatrixRMaj skewMatrix = new DMatrixRMaj(3, 3);
@@ -115,7 +243,7 @@ public void testComputeMeasurementJacobian()
MatrixTools.setMatrixBlock(jacobianExpected, 3, 6, block, 1, 1, 3, 3, -1.0);
// Row 2, foot
- block = OdometryTools.lOperator(multiply(footSensing.orientationMeasurement, fromVector(multiplyLeftInverse(baseState.orientation, footState.orientation))));
+ block = OdometryTools.lOperator(multiply(footSensing.orientationMeasurement, OdomTestTools.fromVector(multiplyLeftInverse(baseState.orientation, footState.orientation))));
MatrixTools.setMatrixBlock(jacobianExpected, 3, 21, block, 1, 1, 3, 3, 1.0);
// Row 3, base
@@ -132,7 +260,7 @@ public void testComputeMeasurementJacobian()
CommonOps_DDRM.insert(skewMatrix, jacobianExpected, 6, 6);
if (OdometryKalmanFilter.includeBias)
{
- OdometryTools.toSkewSymmetricMatrix(footState.translation, skewMatrix);
+ OdometryTools.toSkewSymmetricMatrix(footSensing.positionMeasurement, skewMatrix);
MatrixTools.setMatrixBlock(jacobianExpected, 6, 12, skewMatrix, 0, 0, 3, 3, -1.0);
}
@@ -167,7 +295,7 @@ public void testComputeMeasurementJacobian()
static DMatrixRMaj multiply(QuaternionReadOnly a, QuaternionReadOnly b)
{
DMatrixRMaj vector = new DMatrixRMaj(4, 1);
- CommonOps_DDRM.mult(OdometryTools.lOperator(a), toVector(b), vector);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(a), OdomTestTools.toVector(b), vector);
return vector;
}
@@ -175,29 +303,11 @@ static DMatrixRMaj multiply(QuaternionReadOnly a, QuaternionReadOnly b)
static DMatrixRMaj multiplyLeftInverse(QuaternionReadOnly a, QuaternionReadOnly b)
{
DMatrixRMaj vector = new DMatrixRMaj(4, 1);
- CommonOps_DDRM.mult(OdometryTools.lOperator(a.getS(), -a.getX(), -a.getY(), -a.getZ()), toVector(b), vector);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(a.getS(), -a.getX(), -a.getY(), -a.getZ()), OdomTestTools.toVector(b), vector);
return vector;
}
- static Quaternion fromVector(DMatrixRMaj quaternion)
- {
- return new Quaternion(quaternion.get(1), quaternion.get(2), quaternion.get(3), quaternion.get(0));
- }
-
- static DMatrixRMaj toVector(QuaternionReadOnly quaternionReadOnly)
- {
- DMatrixRMaj vector = new DMatrixRMaj(4, 1);
- vector.set(0, quaternionReadOnly.getS());
- vector.set(1, quaternionReadOnly.getX());
- vector.set(2, quaternionReadOnly.getY());
- vector.set(3, quaternionReadOnly.getZ());
-
- return vector;
- }
-
-
-
static class TestBiasProvider implements IMUBiasProvider
{
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
index 7c4dbf2ead66..41e7dadfa740 100644
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdomTestTools.java
@@ -3,6 +3,8 @@
import org.ejml.data.DMatrixRMaj;
import us.ihmc.euclid.tools.EuclidCoreRandomTools;
import us.ihmc.euclid.tools.EuclidCoreTools;
+import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import java.util.Random;
@@ -45,4 +47,21 @@ public static DMatrixRMaj setRandomState(Random random, SensedVariables sensedVa
return stateVector;
}
+
+
+ static Quaternion fromVector(DMatrixRMaj quaternion)
+ {
+ return new Quaternion(quaternion.get(1), quaternion.get(2), quaternion.get(3), quaternion.get(0));
+ }
+
+ static DMatrixRMaj toVector(QuaternionReadOnly quaternionReadOnly)
+ {
+ DMatrixRMaj vector = new DMatrixRMaj(4, 1);
+ vector.set(0, quaternionReadOnly.getS());
+ vector.set(1, quaternionReadOnly.getX());
+ vector.set(2, quaternionReadOnly.getY());
+ vector.set(3, quaternionReadOnly.getZ());
+
+ return vector;
+ }
}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
index 741ec03ed7df..ae6d107be4ee 100644
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryToolsTest.java
@@ -9,7 +9,9 @@
import us.ihmc.euclid.tools.EuclidCoreRandomTools;
import us.ihmc.euclid.tools.EuclidCoreTestTools;
import us.ihmc.euclid.tuple3D.Vector3D;
+import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
import us.ihmc.euclid.tuple4D.Quaternion;
+import us.ihmc.euclid.tuple4D.interfaces.QuaternionReadOnly;
import us.ihmc.matrixlib.MatrixTools;
import java.util.Random;
@@ -122,4 +124,28 @@ public void testToRotationMatrixInverse()
EjmlUnitTests.assertEquals(rotationExpected, rotationMatrixInverse, 1e-8);
}
}
+
+ @Test
+ public void testExponentialMap()
+ {
+ Random random = new Random(1738L);
+
+ for (int i = 0; i < 1000; i++)
+ {
+ Quaternion a = EuclidCoreRandomTools.nextQuaternion(random);
+ Vector3DReadOnly rotationVector = EuclidCoreRandomTools.nextVector3D(random, 0.02);
+ DMatrixRMaj rotatedAVector = new DMatrixRMaj(4, 1);
+ DMatrixRMaj rotationQuat = OdometryTools.exponentialMap(rotationVector);
+ CommonOps_DDRM.mult(OdometryTools.lOperator(a), OdometryTools.exponentialMap(rotationVector), rotatedAVector);
+ Quaternion rotatedA = OdomTestTools.fromVector(rotatedAVector);
+
+ Quaternion rotationQuaternion = new Quaternion(rotationVector);
+
+ EuclidCoreTestTools.assertEquals(rotationQuaternion, OdomTestTools.fromVector(rotationQuat), 1e-5);
+ Quaternion rotatedAExpected = new Quaternion();
+ rotatedAExpected.multiply(a,rotationQuaternion);
+
+ EuclidCoreTestTools.assertEquals(rotatedAExpected, rotatedA, 1e-6);
+ }
+ }
}
diff --git a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
index 229b7cab346f..2e38e7920f91 100644
--- a/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
+++ b/ihmc-state-estimation/src/test/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/ProcessModelTest.java
@@ -125,7 +125,7 @@ public void testUpdate()
predictedState.translation.get(predictedTranslation);
predictedState.linearVelocity.get(predictedLinearVelocity);
- predictedState.orientation.get(predictedRotation);
+ predictedRotation.set(OdomTestTools.toVector(predictedState.orientation));
predictedState.accelBias.get(predictedAccelBias);
predictedState.gyroBias.get(predictedGyroBias);
@@ -136,6 +136,7 @@ public void testUpdate()
EjmlUnitTests.assertEquals(expectedPredictedGyroBias, predictedGyroBias, 1e-6);
}
}
+
@Test
public void testComputeProcessJacobian()
{
From 58ddc5c569bc978cb36eb740d078ad2352b9fd22 Mon Sep 17 00:00:00 2001
From: Robert
Date: Tue, 2 Sep 2025 23:16:39 -0500
Subject: [PATCH 10/18] fixed some more variables
---
.../odomEKF/MeasurementModel.java | 11 +++++++--
.../odomEKF/OdometryKalmanFilter.java | 24 ++++---------------
2 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index 183aaae88e16..dd2b4c99f348 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -11,6 +11,7 @@
import us.ihmc.yoVariables.providers.DoubleProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
+import us.ihmc.yoVariables.variable.YoDouble;
import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
@@ -28,6 +29,7 @@ class MeasurementModel
public final YoQuaternion footOrientationInBaseFrame;
public final YoBoolean isInContact;
+ public final YoDouble footVariance;
private final DoubleProvider contactThreshold;
// Temp variables
@@ -61,6 +63,7 @@ public MeasurementModel(String prefix,
footOrientationInBaseFrame = new YoQuaternion(prefix + "OrientationInBaseFrame", registry);
isInContact = new YoBoolean(prefix + "IsInContact", registry);
+ footVariance = new YoDouble(prefix + "FootVariance", registry);
}
/**
@@ -91,7 +94,9 @@ public void update(DMatrixRMaj footCovariance)
predictedMeasurements.relativeLinearVelocityError.add(footSensing.linearVelocity);
if (!isInContact.getBooleanValue())
+ {
return;
+ }
// Fourth row. Set the predicted linear velocity
predictedMeasurements.contactVelocity.set(footState.linearVelocity);
@@ -108,13 +113,15 @@ public void update(DMatrixRMaj footCovariance)
private boolean computeIsFootInContact(DMatrixRMaj footCovariance)
{
- footState.translation.get(vector);
+ footState.linearVelocity.get(vector);
CommonOps_DDRM.mult(footCovariance, vector, tempDVector);
CommonOps_DDRM.multTransA(vector, tempDVector, tempScalar);
+ footVariance.set(Math.sqrt(tempScalar.get(0, 0)));
+
// tests the mahalonobis distance of the foot velocity being below a certain threshold.
- return tempScalar.get(0, 0) < contactThreshold.getValue();
+ return footVariance.getValue() < contactThreshold.getValue();
}
public boolean getIsFootInContact()
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index 85bd3c291c1a..9ebc99aeea5f 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -49,7 +49,6 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final List extends IMUSensorReadOnly> feetIMUs;
private final IMUBiasProvider imuBiasProvider;
- private final double estimatorDT;
private final FrameVector3DReadOnly gravityVector;
private final YoDouble translationCovariance = new YoDouble("translationCovariance", registry);
@@ -122,41 +121,28 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
{
super(stateSizePerLink * (1 + feetIMUs.size()), errorSizePerLink * (1 + feetIMUs.size()) , measurementSizePerLink * feetIMUs.size());
- int links = 1 + feetIMUs.size();
// Initialize covariance
processCovariance = new DMatrixRMaj(getErrorSize(), getErrorSize());
measurementCovariance = new DMatrixRMaj(getMeasurementSize(), getMeasurementSize());
MatrixTools.setDiagonal(processCovariance, 1e-2);
MatrixTools.setDiagonal(measurementCovariance, 1e-3);
- translationCovariance.set(1e-3);
+ translationCovariance.set(1e-5);
velocityCovariance.set(1e-2);
orientationCovariance.set(1e-2);
biasCovariance.set(1e-4);
- measuredTranslationCovariance.set(1e-8);
- measuredOrientationCovariance.set(1e-8);
+ measuredTranslationCovariance.set(1e-6);
+ measuredOrientationCovariance.set(1e-5);
velocityErrorCovariance.set(1e-5);
- contactVelocityCovariance.set(1e-6);
+ contactVelocityCovariance.set(1e-10);
contactAccelerationCovariance.set(1.0);
-// translationCovariance.set(1e-4);
-// velocityCovariance.set(1e-4);
-// orientationCovariance.set(1e-4);
-// biasCovariance.set(1e-4);
-//
-// measuredTranslationCovariance.set(1e-4);
-// measuredOrientationCovariance.set(1e-4);
-// velocityErrorCovariance.set(1e-4);
-// contactVelocityCovariance.set(1e-4);
-// contactAccelerationCovariance.set(1e-4);
-
contactThreshold.set(Double.MAX_VALUE);
this.baseIMU = baseIMU;
this.feetIMUs = feetIMUs;
this.imuBiasProvider = baseImuBiasProvider;
- this.estimatorDT = estimatorDT;
predictedState = new DMatrixRMaj(getStateSize(), 1);
predictedMeasurement = new DMatrixRMaj(getMeasurementSize(), 1);
@@ -451,7 +437,7 @@ public DMatrixRMaj measurementModel(DMatrixRMaj predictedState)
// Note that this bypasses the use of predicted state vector, as the predicted state variables are computed in the {@link #processModel(DMatrixRMaj)} call
for (int i = 0; i < feetIMUs.size(); i++)
{
- int row = errorSizePerLink * (i + 1) + errorLinearVelocityIndex;
+ int row = errorSizePerLink * (i + 1) + measurementContactVelocityIndex;
MatrixTools.setMatrixBlock(footCovariance, 0, 0, getCovariance(), row, row, 3, 3, 1.0);
measurementModels.get(i).update(footCovariance);
From 32f2c0900b84f90e2f095588244057ff9864ef9b Mon Sep 17 00:00:00 2001
From: Robert
Date: Wed, 3 Sep 2025 17:28:44 -0500
Subject: [PATCH 11/18] started trying to implement the filter weights from the
internet
---
.../ExtendedKalmanFilter.java | 78 ++++++++++-------
.../odomEKF/MeasurementModel.java | 15 +---
.../odomEKF/MeasurementVariables.java | 8 +-
.../odomEKF/OdometryKalmanFilter.java | 83 +++++++++++--------
.../OdometryKalmanFilterParameters.java | 53 ++++++++++++
.../odomEKF/SensedVariables.java | 5 +-
6 files changed, 159 insertions(+), 83 deletions(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterParameters.java
diff --git a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
index 2b460f0a9e3a..9a9a1cbe01d7 100644
--- a/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
+++ b/ihmc-parameter-estimation/src/main/java/us/ihmc/parameterEstimation/ExtendedKalmanFilter.java
@@ -6,7 +6,7 @@
import org.ejml.dense.row.CommonOps_DDRM;
import org.ejml.dense.row.MatrixFeatures_DDRM;
import org.ejml.dense.row.factory.LinearSolverFactory_DDRM;
-import us.ihmc.log.LogTools;
+import us.ihmc.matrixlib.MatrixTools;
/**
* This class provides an implementation of an Extended Kalman Filter (EKF). The EKF is a recursive filter that uses a linearization of the process and
@@ -91,7 +91,23 @@ public abstract class ExtendedKalmanFilter
/**
* Covariance matrix 'S' of the linearized measurement model, used in the update step of the filter.
*/
- private final DMatrixRMaj residualCovariance;
+ protected final DMatrixRMaj residualCovariance;
+ /**
+ * Masked version of the residual, mask^T * y
+ */
+ protected final DMatrixRMaj maskedMeasurementResidual;
+ /**
+ * Masked version of the measurement covariance, mask^T * R * mask, used to reject outlier measurements from the filter
+ */
+ protected final DMatrixRMaj maskedMeasurementCovariance;
+ /**
+ * Masked version of the covariance, mask^T * S * mask, used to reject outlier measurements from the filter
+ */
+ protected final DMatrixRMaj maskedResidualCovariance;
+ /**
+ * Masked measurement jacobian, mask^T * H
+ */
+ protected final DMatrixRMaj maskedMeasurementJacobian;
/**
* Container variable used to hold the inverse of the residual covariance matrix.
*/
@@ -143,9 +159,6 @@ public abstract class ExtendedKalmanFilter
/** Terms used for calculating the Joseph form of the covariance update. */
private final DMatrixRMaj josephTransposeTerm;
- private final DMatrixRMaj josephTransposeTermContainer;
- private final DMatrixRMaj josephIdentity;
- private final DMatrixRMaj josephGainTerm;
private final DMatrixRMaj josephGainTermContainer;
private final int stateSize;
@@ -165,9 +178,11 @@ public ExtendedKalmanFilter(int stateSize, int errorSize, int measurementSize)
processCovariance = new DMatrixRMaj(errorSize, errorSize);
measurementCovariance = new DMatrixRMaj(measurementSize, measurementSize);
+ maskedMeasurementCovariance = new DMatrixRMaj(measurementSize, measurementSize);
processJacobian = new DMatrixRMaj(stateSize, errorSize);
measurementJacobian = new DMatrixRMaj(measurementSize, errorSize);
+ maskedMeasurementJacobian = new DMatrixRMaj(measurementSize, errorSize);
state = new DMatrixRMaj(stateSize, 1);
predictedState = new DMatrixRMaj(stateSize, 1);
@@ -176,8 +191,10 @@ public ExtendedKalmanFilter(int stateSize, int errorSize, int measurementSize)
predictedCovarianceContainer = new DMatrixRMaj(errorSize, errorSize);
measurementResidual = new DMatrixRMaj(measurementSize, 1);
+ maskedMeasurementResidual = new DMatrixRMaj(measurementSize, 1);
residualCovarianceContainer = new DMatrixRMaj(errorSize, errorSize);
residualCovariance = new DMatrixRMaj(measurementSize, measurementSize);
+ maskedResidualCovariance = new DMatrixRMaj(measurementSize, measurementSize);
inverseResidualCovariance = new DMatrixRMaj(measurementSize, measurementSize);
solver = new LinearSolverSafe<>(LinearSolverFactory_DDRM.symmPosDef(measurementSize));
kalmanGain = new DMatrixRMaj(stateSize, measurementSize);
@@ -191,9 +208,6 @@ public ExtendedKalmanFilter(int stateSize, int errorSize, int measurementSize)
normalizedInnovationContainer = new DMatrixRMaj(measurementSize, 1);
josephTransposeTerm = new DMatrixRMaj(errorSize, errorSize);
- josephTransposeTermContainer = new DMatrixRMaj(errorSize, measurementSize);
- josephIdentity = CommonOps_DDRM.identity(errorSize);
- josephGainTerm = new DMatrixRMaj(errorSize, errorSize);
josephGainTermContainer = new DMatrixRMaj(errorSize, errorSize);
}
@@ -352,7 +366,8 @@ protected void updateStep()
calculateKalmanGain();
// x_(k|k) = x_(k|k-1) + K_k * y_k
- CommonOps_DDRM.mult(kalmanGain, measurementResidual, stateCorrection);
+ CommonOps_DDRM.mult(kalmanGain, maskedMeasurementResidual, stateCorrection);
+ // We want to do this, in case the state exists on a manifold (e.g. quaternion)
applyStateCorrection(predictedState, stateCorrection, updatedState);
calculateUpdatedCovariance();
@@ -363,22 +378,33 @@ protected void updateStep()
private void calculateResidualCovarianceAndInverse()
{
// S_k = H_k * P_(k|k-1) * H_k^T + R_k
- residualCovariance.zero();
CommonOps_DDRM.mult(measurementJacobian, predictedCovariance, residualCovarianceContainer);
CommonOps_DDRM.multTransB(residualCovarianceContainer, measurementJacobian, residualCovariance);
CommonOps_DDRM.addEquals(residualCovariance, measurementCovariance);
- solver.setA(residualCovariance);
+ maskResidualCovarianceForOutlierRejection();
+
+ solver.setA(maskedResidualCovariance);
// fixme this doesn't work.
- CommonOps_DDRM.invert(residualCovariance, inverseResidualCovariance);
+ CommonOps_DDRM.invert(maskedResidualCovariance, inverseResidualCovariance);
// solver.invert(inverseResidualCovariance);
}
+ /**
+ * If the user wants to reject outlier measurements, they can override this method to modify the masked variables. By default, no masking is done.
+ */
+ protected void maskResidualCovarianceForOutlierRejection()
+ {
+ maskedResidualCovariance.set(residualCovariance);
+ maskedMeasurementCovariance.set(measurementCovariance);
+ maskedMeasurementResidual.set(measurementResidual);
+ maskedMeasurementJacobian.set(measurementJacobian);
+ }
+
protected void calculateKalmanGain()
{
// K_k = P_(k|k-1) * H_k^T * S_k^-1
- kalmanGain.zero();
- CommonOps_DDRM.multTransB(predictedCovariance, measurementJacobian, kalmanGainContainer);
+ CommonOps_DDRM.multTransB(predictedCovariance, maskedMeasurementJacobian, kalmanGainContainer);
CommonOps_DDRM.mult(kalmanGainContainer, inverseResidualCovariance, kalmanGain);
}
@@ -389,28 +415,22 @@ protected void calculateUpdatedCovariance()
{
// Joseph form of covariance update is apparently more numerically stable
// P_(k|k) = (I - K_k * H_k) * P_(k|k-1) * (I - K_k * H_k)^T + K_k * R_k * K_k^T
- josephGainTerm.set(kalmanGain);
- CommonOps_DDRM.mult(josephGainTerm, measurementCovariance, josephGainTermContainer);
- CommonOps_DDRM.multTransB(josephGainTermContainer, kalmanGain, josephGainTerm);
+ CommonOps_DDRM.mult(kalmanGain, maskedMeasurementCovariance, josephGainTermContainer);
+ CommonOps_DDRM.multTransB(josephGainTermContainer, kalmanGain, updatedCovariance);
- josephTransposeTermContainer.set(kalmanGain);
- CommonOps_DDRM.mult(josephTransposeTermContainer, measurementJacobian, josephTransposeTerm);
- CommonOps_DDRM.scale(-1.0, josephTransposeTerm);
- CommonOps_DDRM.addEquals(josephTransposeTerm, josephIdentity);
+ CommonOps_DDRM.mult(-1.0, kalmanGain, maskedMeasurementJacobian, josephTransposeTerm);
+ MatrixTools.addDiagonal(josephTransposeTerm, 1.0);
// Now put these terms into the covariance update variables
CommonOps_DDRM.multTransB(predictedCovariance, josephTransposeTerm, updatedCovarianceContainer);
- CommonOps_DDRM.mult(josephTransposeTerm, updatedCovarianceContainer, updatedCovariance);
- CommonOps_DDRM.addEquals(updatedCovariance, josephGainTerm);
+ CommonOps_DDRM.multAdd(josephTransposeTerm, updatedCovarianceContainer, updatedCovariance);
}
else // DEFAULT
{
// P_(k|k) = (I - K_k * H_k) * P_(k|k-1)
- updatedCovariance.set(kalmanGain);
- CommonOps_DDRM.mult(updatedCovariance, measurementJacobian, updatedCovarianceContainer);
+ CommonOps_DDRM.mult(-1.0, kalmanGain, measurementJacobian, updatedCovarianceContainer);
+ MatrixTools.addDiagonal(updatedCovarianceContainer, 1.0);
CommonOps_DDRM.mult(updatedCovarianceContainer, predictedCovariance, updatedCovariance);
- CommonOps_DDRM.scale(-1.0, updatedCovariance);
- CommonOps_DDRM.addEquals(updatedCovariance, predictedCovariance);
}
}
@@ -426,8 +446,8 @@ protected void calculateUpdatedCovariance()
*/
private double calculateNormalizedInnovation()
{
- CommonOps_DDRM.mult(inverseResidualCovariance, measurementResidual, normalizedInnovationContainer);
- CommonOps_DDRM.multTransA(measurementResidual, normalizedInnovationContainer, normalizedInnovation);
+ CommonOps_DDRM.mult(inverseResidualCovariance, maskedMeasurementResidual, normalizedInnovationContainer);
+ CommonOps_DDRM.multTransA(maskedMeasurementResidual, normalizedInnovationContainer, normalizedInnovation);
return normalizedInnovation.getData()[0];
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
index dd2b4c99f348..bbbbdb11f499 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementModel.java
@@ -28,7 +28,6 @@ class MeasurementModel
// State variables
public final YoQuaternion footOrientationInBaseFrame;
- public final YoBoolean isInContact;
public final YoDouble footVariance;
private final DoubleProvider contactThreshold;
@@ -62,7 +61,6 @@ public MeasurementModel(String prefix,
this.contactThreshold = contactThreshold;
footOrientationInBaseFrame = new YoQuaternion(prefix + "OrientationInBaseFrame", registry);
- isInContact = new YoBoolean(prefix + "IsInContact", registry);
footVariance = new YoDouble(prefix + "FootVariance", registry);
}
@@ -71,7 +69,7 @@ public MeasurementModel(String prefix,
*/
public void update(DMatrixRMaj footCovariance)
{
- isInContact.set(computeIsFootInContact(footCovariance));
+ footSensing.isInContact.set(computeIsFootInContact(footCovariance));
// First row. Update the predicted foot position of the foot relative to the base in the base frame
predictedMeasurements.relativePosition.sub(footState.translation, baseState.translation);
@@ -93,11 +91,6 @@ public void update(DMatrixRMaj footCovariance)
predictedMeasurements.relativeLinearVelocityError.sub(velocityError);
predictedMeasurements.relativeLinearVelocityError.add(footSensing.linearVelocity);
- if (!isInContact.getBooleanValue())
- {
- return;
- }
-
// Fourth row. Set the predicted linear velocity
predictedMeasurements.contactVelocity.set(footState.linearVelocity);
@@ -126,7 +119,7 @@ private boolean computeIsFootInContact(DMatrixRMaj footCovariance)
public boolean getIsFootInContact()
{
- return isInContact.getBooleanValue();
+ return footSensing.isInContact.getBooleanValue();
}
/**
@@ -137,7 +130,6 @@ public void computeBaseMeasurementJacobian(int rowOffset, DMatrixRMaj jacobianTo
OdometryTools.toRotationMatrix(baseState.orientation, rotationMatrixTranspose);
CommonOps_DDRM.transpose(rotationMatrixTranspose);
-
positionError.sub(footState.translation, baseState.translation);
velocityError.sub(footState.linearVelocity, baseState.linearVelocity);
baseState.orientation.inverseTransform(positionError);
@@ -203,9 +195,6 @@ public void computeFootMeasurementJacobian(int rowOffset, int colOffset, DMatrix
row = rowOffset + measurementRelativeVelocityIndex;
CommonOps_DDRM.insert(rotationMatrixTranspose, jacobianToPack, row, colOffset + errorLinearVelocityIndex);
- if (!isInContact.getBooleanValue())
- return;
-
// Fourth row. Partial of contact velocity w.r.t. foot state
// The contact velocity is directly the foot velocity when it's in contact.
row = rowOffset + measurementContactVelocityIndex;
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
index 8c579577c615..d01c4de05a50 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/MeasurementVariables.java
@@ -3,6 +3,7 @@
import org.ejml.data.DMatrixRMaj;
import us.ihmc.yoVariables.euclid.YoVector3D;
import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoBoolean;
import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.*;
import static us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF.OdometryIndexHelper.measurementAccelIndex;
@@ -16,6 +17,7 @@ class MeasurementVariables
public final YoVector3D contactVelocity;
public final YoVector3D accelMeasure;
+
public MeasurementVariables(String prefix, YoRegistry registry)
{
relativePosition = new YoVector3D(prefix + "RelativePosition", registry);
@@ -25,15 +27,11 @@ public MeasurementVariables(String prefix, YoRegistry registry)
accelMeasure = new YoVector3D(prefix + "AccelMeasure", registry);
}
- public void get(boolean isInContact, int start, DMatrixRMaj measurementToPack)
+ public void get(int start, DMatrixRMaj measurementToPack)
{
relativePosition.get(start + measurementRelativeTranslationIndex, measurementToPack);
relativeOrientationError.get(start + measurementRelativeOrientationErrorIndex, measurementToPack);
relativeLinearVelocityError.get(start + measurementRelativeVelocityIndex, measurementToPack);
-
- if (!isInContact)
- return;
-
contactVelocity.get(start + measurementContactVelocityIndex, measurementToPack);
accelMeasure.get(start + measurementAccelIndex, measurementToPack);
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
index 9ebc99aeea5f..7f6c91825574 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilter.java
@@ -51,16 +51,7 @@ public class OdometryKalmanFilter extends ExtendedKalmanFilter
private final FrameVector3DReadOnly gravityVector;
- private final YoDouble translationCovariance = new YoDouble("translationCovariance", registry);
- private final YoDouble velocityCovariance = new YoDouble("velocityCovariance", registry);
- private final YoDouble orientationCovariance = new YoDouble("orientationCovariance", registry);
- private final YoDouble biasCovariance = new YoDouble("biasCovariance", registry);
-
- private final YoDouble measuredTranslationCovariance = new YoDouble("measuredTranslationCovariance", registry);
- private final YoDouble measuredOrientationCovariance = new YoDouble("measuredOrientationCovariance", registry);
- private final YoDouble velocityErrorCovariance = new YoDouble("velocityErrorCovariance", registry);
- private final YoDouble contactVelocityCovariance = new YoDouble("contactVelocityCovariance", registry);
- private final YoDouble contactAccelerationCovariance = new YoDouble("contactAccelerationMeasureCovariance", registry);
+ private final OdometryKalmanFilterParameters parameters = new OdometryKalmanFilterParameters(registry);
private final YoDouble contactThreshold = new YoDouble("contactVarianceThreshold", registry);
@@ -127,18 +118,7 @@ public OdometryKalmanFilter(IMUSensorReadOnly baseIMU,
MatrixTools.setDiagonal(processCovariance, 1e-2);
MatrixTools.setDiagonal(measurementCovariance, 1e-3);
- translationCovariance.set(1e-5);
- velocityCovariance.set(1e-2);
- orientationCovariance.set(1e-2);
- biasCovariance.set(1e-4);
-
- measuredTranslationCovariance.set(1e-6);
- measuredOrientationCovariance.set(1e-5);
- velocityErrorCovariance.set(1e-5);
- contactVelocityCovariance.set(1e-10);
- contactAccelerationCovariance.set(1.0);
-
- contactThreshold.set(Double.MAX_VALUE);
+ contactThreshold.set(0.5);
this.baseIMU = baseIMU;
this.feetIMUs = feetIMUs;
@@ -265,24 +245,31 @@ public void compute()
private void updateCovariance()
{
- for (int i = 0; i < feetIMUs.size() + 1; i++)
+ OdometryTools.setDiagonals(errorTranslationIndex, 3, parameters.baseProcessTranslationNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(errorLinearVelocityIndex, 2, parameters.baseProcessVelocityXYNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(errorLinearVelocityIndex + 2, 1, parameters.baseProcessVelocityZNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(errorOrientationIndex, 3, parameters.baseProcessOrientationNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(errorAccelBiasIndex, 3, parameters.baseProcessAccelBiasNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(errorGyroBiasIndex, 3, parameters.baseProcessGyroBiasNoise.getValue(), processCovariance);
+
+ for (int i = 0; i < feetIMUs.size(); i++)
{
- int index = i * errorSizePerLink;
- OdometryTools.setDiagonals(index + errorTranslationIndex, 3, translationCovariance.getValue(), processCovariance);
- OdometryTools.setDiagonals(index + errorLinearVelocityIndex, 3, velocityCovariance.getValue(), processCovariance);
- OdometryTools.setDiagonals(index + errorOrientationIndex, 3, orientationCovariance.getValue(), processCovariance);
- OdometryTools.setDiagonals(index + errorAccelBiasIndex, 3, biasCovariance.getValue(), processCovariance);
- OdometryTools.setDiagonals(index + errorGyroBiasIndex, 3, biasCovariance.getValue(), processCovariance);
+ int index = (i + 1) * errorSizePerLink;
+ OdometryTools.setDiagonals(index + errorTranslationIndex, 3, parameters.footProcessTranslationNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorLinearVelocityIndex, 3, parameters.footProcessVelocityNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorOrientationIndex, 3, parameters.footProcessOrientationNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorAccelBiasIndex, 3, parameters.footProcessAccelBiasNoise.getValue(), processCovariance);
+ OdometryTools.setDiagonals(index + errorGyroBiasIndex, 3, parameters.footProcessGyroBiasNoise.getValue(), processCovariance);
}
for (int i = 0; i < feetIMUs.size(); i++)
{
int index = i * measurementSizePerLink;
- OdometryTools.setDiagonals(index + measurementRelativeTranslationIndex, 3, measuredTranslationCovariance.getValue(), measurementCovariance);
- OdometryTools.setDiagonals(index + measurementRelativeOrientationErrorIndex, 3, measuredOrientationCovariance.getValue(), measurementCovariance);
- OdometryTools.setDiagonals(index + measurementRelativeVelocityIndex, 3, velocityErrorCovariance.getValue(), measurementCovariance);
- OdometryTools.setDiagonals(index + measurementContactVelocityIndex, 3, contactVelocityCovariance.getValue(), measurementCovariance);
- OdometryTools.setDiagonals(index + measurementAccelIndex, 3, contactAccelerationCovariance.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementRelativeTranslationIndex, 3, parameters.measurementIKPositionNoise.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementRelativeOrientationErrorIndex, 3, parameters.measurementIKOrientationNoise.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementRelativeVelocityIndex, 3, parameters.measurementIKVelocityNoise.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementContactVelocityIndex, 3, parameters.measurementContactVelocityNoise.getValue(), measurementCovariance);
+ OdometryTools.setDiagonals(index + measurementAccelIndex, 3, parameters.measurementContactAccelNoise.getValue(), measurementCovariance);
}
setProcessCovariance(processCovariance);
@@ -441,7 +428,7 @@ public DMatrixRMaj measurementModel(DMatrixRMaj predictedState)
MatrixTools.setMatrixBlock(footCovariance, 0, 0, getCovariance(), row, row, 3, 3, 1.0);
measurementModels.get(i).update(footCovariance);
- feetMeasurements.get(i).get(measurementModels.get(i).getIsFootInContact(), i * measurementSizePerLink, predictedMeasurement);
+ feetMeasurements.get(i).get(i * measurementSizePerLink, predictedMeasurement);
}
// return the predicted measurement
@@ -482,4 +469,30 @@ protected DMatrixRMaj linearizeMeasurementModel(DMatrixRMaj predictedState)
return CMatrix;
}
+
+ @Override
+ protected void maskResidualCovarianceForOutlierRejection()
+ {
+ // FIXME garbage
+ DMatrixRMaj mask = CommonOps_DDRM.identity(getMeasurementSize(), getMeasurementSize());
+ DMatrixRMaj maskTransposeS = new DMatrixRMaj(0, 0);
+
+ for (int i = feetIMUs.size() - 1; i >= 0; i--)
+ {
+ if (!measurementModels.get(i).getIsFootInContact())
+ {
+ int index = i * measurementSizePerLink;
+ // remove the rows of the measurement accel, since it's larger
+ CommonOps_DDRM.removeColumns(mask, index + measurementAccelIndex, index + measurementAccelIndex + 2);
+ CommonOps_DDRM.removeColumns(mask, index + measurementContactVelocityIndex, index + measurementContactVelocityIndex + 2);
+ }
+ }
+
+ CommonOps_DDRM.multTransA(mask, measurementResidual, maskedMeasurementResidual);
+ CommonOps_DDRM.multTransA(mask, measurementJacobian, maskedMeasurementJacobian);
+ CommonOps_DDRM.multTransA(mask, residualCovariance, maskTransposeS);
+ CommonOps_DDRM.mult(maskTransposeS, mask, maskedResidualCovariance);
+ CommonOps_DDRM.multTransA(mask, measurementCovariance, maskTransposeS);
+ CommonOps_DDRM.mult(maskTransposeS, mask, maskedMeasurementCovariance);
+ }
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterParameters.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterParameters.java
new file mode 100644
index 000000000000..2e959e3a35d0
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/OdometryKalmanFilterParameters.java
@@ -0,0 +1,53 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
+
+import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoDouble;
+
+public class OdometryKalmanFilterParameters
+{
+ private final YoRegistry registry = new YoRegistry(getClass().getSimpleName());
+
+ public final YoDouble baseProcessTranslationNoise = new YoDouble("baseProcessTranslationNoise", registry);
+ public final YoDouble baseProcessVelocityXYNoise = new YoDouble("baseProcessVelocityXYNoise", registry);
+ public final YoDouble baseProcessVelocityZNoise = new YoDouble("baseProcessVelocityZNoise", registry);
+ public final YoDouble baseProcessOrientationNoise = new YoDouble("baseProcessOrientationNoise", registry);
+ public final YoDouble baseProcessGyroBiasNoise = new YoDouble("baseProcessGyroBiasNoise", registry);
+ public final YoDouble baseProcessAccelBiasNoise = new YoDouble("baseProcessAccelBiasNoise", registry);
+
+ public final YoDouble footProcessTranslationNoise = new YoDouble("footProcessTranslationNoise", registry);
+ public final YoDouble footProcessVelocityNoise = new YoDouble("footProcessVelocityNoise", registry);
+ public final YoDouble footProcessOrientationNoise = new YoDouble("footProcessOrientationNoise", registry);
+ public final YoDouble footProcessAccelBiasNoise = new YoDouble("footProcessAccelBiasNoise", registry);
+ public final YoDouble footProcessGyroBiasNoise = new YoDouble("footProcessGyroBiasNoise", registry);
+
+
+ public final YoDouble measurementIKPositionNoise = new YoDouble("measurementIKPositionNoise", registry);
+ public final YoDouble measurementIKVelocityNoise = new YoDouble("measurementIKVelocityNoise", registry);
+ public final YoDouble measurementIKOrientationNoise = new YoDouble("measurementIKOrientationNoise", registry);
+ public final YoDouble measurementContactVelocityNoise = new YoDouble("measurementContactVelocityNoise", registry);
+ public final YoDouble measurementContactAccelNoise = new YoDouble("measurementContactAccelNoise", registry);
+
+ public OdometryKalmanFilterParameters(YoRegistry parentRegistry)
+ {
+ baseProcessTranslationNoise.set(0.0005);
+ baseProcessVelocityXYNoise.set(0.005);
+ baseProcessVelocityZNoise.set(0.005);
+ baseProcessOrientationNoise.set(1e-7);
+ baseProcessGyroBiasNoise.set(1e-5);
+ baseProcessAccelBiasNoise.set(1e-4);
+
+ footProcessTranslationNoise.set(1e-4);
+ footProcessVelocityNoise.set(5.0);
+ footProcessOrientationNoise.set(1e-5); // made up.
+ footProcessGyroBiasNoise.set(1e-5); // made up
+ footProcessAccelBiasNoise.set(1e-4);
+
+ measurementIKPositionNoise.set(0.001);
+ measurementIKOrientationNoise.set(1e-8);
+ measurementIKVelocityNoise.set(0.05);
+ measurementContactVelocityNoise.set(0.01);
+ measurementContactAccelNoise.set(1.0);
+
+ parentRegistry.addChild(registry);
+ }
+}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
index a94c0c60a478..ce08c1996dae 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/odomEKF/SensedVariables.java
@@ -1,6 +1,5 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation.odomEKF;
-import us.ihmc.euclid.referenceFrame.FramePoint3D;
import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
@@ -12,6 +11,7 @@
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameQuaternion;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoBoolean;
class SensedVariables
{
@@ -32,6 +32,8 @@ class SensedVariables
public final YoFrameQuaternion orientationMeasurement;
public final YoFrameVector3D linearVelocity;
+ public final YoBoolean isInContact;
+
// Temp variables
private final FrameVector3D linearAcceleration = new FrameVector3D();
private final FrameVector3D angularVelocity = new FrameVector3D();
@@ -52,6 +54,7 @@ public SensedVariables(String prefix, IMUSensorReadOnly baseIMU, IMUSensorReadOn
orientationMeasurement = new YoFrameQuaternion(prefix + "OrientationMeasurement", baseIMU.getMeasurementFrame(), registry);
linearVelocity = new YoFrameVector3D(prefix + "LinearVelocity", worldFrame, registry);
+ isInContact = new YoBoolean(prefix + "IsInContact", registry);
}
public void update()
From 87a797019b5ee9fa03af2cc6d78dcf9b0bd9009e Mon Sep 17 00:00:00 2001
From: Robert
Date: Wed, 3 Sep 2025 22:17:58 -0500
Subject: [PATCH 12/18] extracted the foot estimator
---
...sKinematicsBasedLinearStateCalculator.java | 293 +---------------
.../SingleFootEstimator.java | 325 ++++++++++++++++++
2 files changed, 329 insertions(+), 289 deletions(-)
create mode 100644 ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index 8641f12b2153..28b22dd874c6 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -4,43 +4,23 @@
import java.util.List;
import java.util.Map;
-import us.ihmc.euclid.referenceFrame.FrameConvexPolygon2D;
-import us.ihmc.euclid.referenceFrame.FrameLineSegment2D;
-import us.ihmc.euclid.referenceFrame.FramePoint2D;
import us.ihmc.euclid.referenceFrame.FramePoint3D;
-import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
-import us.ihmc.euclid.referenceFrame.interfaces.FixedFramePoint3DBasics;
-import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
-import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
-import us.ihmc.euclid.referenceFrame.interfaces.FrameVertex2DSupplier;
-import us.ihmc.graphicsDescription.appearance.YoAppearance;
-import us.ihmc.graphicsDescription.yoGraphics.YoGraphicPosition;
import us.ihmc.graphicsDescription.yoGraphics.YoGraphicsListRegistry;
-import us.ihmc.graphicsDescription.yoGraphics.plotting.YoArtifactPosition;
import us.ihmc.humanoidRobotics.model.CenterOfPressureDataHolder;
-import us.ihmc.log.LogTools;
import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
import us.ihmc.mecano.spatial.Twist;
-import us.ihmc.mecano.spatial.interfaces.TwistReadOnly;
import us.ihmc.robotics.SCS2YoGraphicHolder;
import us.ihmc.robotics.contactable.ContactablePlaneBody;
-import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFramePoint2D;
-import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFrameVector3D;
import us.ihmc.yoVariables.euclid.filters.BacklashCompensatingVelocityYoFrameVector3D;
import us.ihmc.robotics.sensors.FootSwitchInterface;
-import us.ihmc.scs2.definition.visual.ColorDefinitions;
import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinition;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory.DefaultPoint2DGraphic;
import us.ihmc.scs2.definition.yoGraphic.YoGraphicGroupDefinition;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicPoint2DDefinition;
import us.ihmc.sensorProcessing.stateEstimation.StateEstimatorParameters;
import us.ihmc.sensorProcessing.stateEstimation.evaluation.FullInverseDynamicsStructure;
-import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint2D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
import us.ihmc.yoVariables.filters.AlphaFilteredYoVariable;
@@ -245,13 +225,11 @@ public void estimatePelvisLinearState(List trustedFeet, List AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(footToRootJointPositionBreakFrequency.getValue(),
- estimatorDT);
- footToRootJointPosition = new AlphaFilteredYoFrameVector3D(namePrefix + "FootToRootJointPosition", "", registry, alphaFoot, worldFrame);
- rootJointPositionPerFoot = new YoFramePoint3D(namePrefix + "BasedRootJointPosition", worldFrame, registry);
- footPositionInWorld = new YoFramePoint3D(namePrefix + "FootPositionInWorld", worldFrame, registry);
- footPolygon = new FrameConvexPolygon2D(FrameVertex2DSupplier.asFrameVertex2DSupplier(contactableFoot.getContactPoints2D()));
- footCenterCoPLineSegment = new FrameLineSegment2D(soleFrame);
- copRawInFootFrame = new YoFramePoint2D(namePrefix + "CoPRawInFootFrame", soleFrame, registry);
-
- DoubleProvider alphaCop = () -> AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(copFilterBreakFrequency.getValue(), estimatorDT);
- copFilteredInFootFrame = new AlphaFilteredYoFramePoint2D(namePrefix + "CoPFilteredInFootFrame", "", registry, alphaCop, copRawInFootFrame);
- copFilteredInFootFrame.update(0.0, 0.0);
- copPositionInWorld = new YoFramePoint3D(namePrefix + "CoPPositionsInWorld", worldFrame, registry);
- footVelocityInWorld = new YoFrameVector3D(namePrefix + "VelocityInWorld", worldFrame, registry);
- }
-
- public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
- {
- if (yoGraphicsListRegistry == null)
- return;
-
- String sidePrefix = foot.getName();
- YoGraphicPosition copInWorld = new YoGraphicPosition(sidePrefix + "StateEstimatorCoP", copPositionInWorld, 0.005, YoAppearance.DeepPink());
- YoArtifactPosition artifact = copInWorld.createArtifact();
- artifact.setVisible(false);
- yoGraphicsListRegistry.registerArtifact("StateEstimator", artifact);
- }
-
- @Override
- public YoGraphicDefinition getSCS2YoGraphics()
- {
- YoGraphicPoint2DDefinition copVisual = YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP",
- copPositionInWorld,
- 0.01,
- ColorDefinitions.DeepPink(),
- DefaultPoint2DGraphic.CIRCLE);
- return copVisual;
- }
-
- public void initialize()
- {
- footToRootJointPosition.reset();
- copFilteredInFootFrame.reset();
- copFilteredInFootFrame.update(0.0, 0.0);
- footVelocityInWorld.setToZero();
- }
-
- private final FramePoint3D tempFramePoint = new FramePoint3D();
-
- /**
- * Estimates the pelvis position and linear velocity using the leg kinematics
- *
- * @param trustedFoot is the foot used to estimates the pelvis state
- * @param numberOfTrustedSides is only one or both legs used to estimate the pelvis state
- */
- private void updatePelvisWithKinematics(int numberOfTrustedFeet,
- double alphaVelocityUpdate,
- FixedFramePoint3DBasics rootJointPosition,
- FixedFrameVector3DBasics rootJointLinearVelocity)
- {
- double scaleFactor = 1.0 / numberOfTrustedFeet;
-
- rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
- rootJointPosition.scaleAdd(scaleFactor, rootJointPositionPerFoot, rootJointPosition);
-
- rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaVelocityUpdate, footVelocityInWorld, rootJointLinearVelocity);
- }
-
- /**
- * updates the position of a swinging foot
- *
- * @param swingingFoot a foot in swing
- * @param pelvisPosition the current pelvis position
- */
- private void updateUntrustedFootPosition(FramePoint3DReadOnly pelvisPosition)
- {
- footPositionInWorld.sub(pelvisPosition, footToRootJointPosition);
-
- copPositionInWorld.set(footPositionInWorld);
-
- copRawInFootFrame.setToZero();
- copFilteredInFootFrame.setToZero();
- }
-
- private final FrameVector3D tempFrameVector = new FrameVector3D();
-
- private void updateTrustedFootPosition(boolean trustCoPAsNonSlippingContactPoint, FramePoint3DReadOnly rootJointPosition)
- {
- if (trustCoPAsNonSlippingContactPoint)
- {
- tempFrameVector.setIncludingFrame(rootJointPosition);
- tempFrameVector.sub(footToRootJointPosition); // New foot position
- tempFrameVector.sub(footPositionInWorld); // Delta from previous to new foot position
- copPositionInWorld.add(tempFrameVector); // New CoP position
- }
-
- footPositionInWorld.set(rootJointPosition);
- footPositionInWorld.sub(footToRootJointPosition);
- }
-
- private final FramePoint2D tempCoP2d = new FramePoint2D();
- private final FrameVector3D tempCoPOffset = new FrameVector3D();
-
- /**
- * Compute the foot CoP. The CoP is the point on the support foot trusted to be not slipping.
- *
- * @param trustedSide
- * @param footSwitch
- */
- private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
- {
- if (trustCoPAsNonSlippingContactPoint)
- {
- if (useControllerDesiredCoP)
- centerOfPressureDataHolderFromController.getCenterOfPressure(tempCoP2d, foot);
- else
- footSwitch.getCenterOfPressure(tempCoP2d);
-
- if (tempCoP2d.containsNaN())
- {
- tempCoP2d.setToZero(soleFrame);
- }
- else
- {
- boolean isCoPInsideFoot = footPolygon.isPointInside(tempCoP2d);
- if (!isCoPInsideFoot)
- {
- if (footSwitch.getFootLoadPercentage() > 0.2)
- {
- footCenterCoPLineSegment.set(soleFrame, 0.0, 0.0, tempCoP2d.getX(), tempCoP2d.getY());
- int intersections = footPolygon.intersectionWith(footCenterCoPLineSegment, intersectionPoints[0], intersectionPoints[1]);
-
- if (intersections == 0)
- {
- LogTools.info("Found no solution for the CoP projection.");
- tempCoP2d.setToZero(soleFrame);
- }
- else
- {
- tempCoP2d.set(intersectionPoints[0]);
-
- if (intersections == 2)
- LogTools.info("Found two solutions for the CoP projection.");
- }
- }
- else // If foot barely loaded and actual CoP outside, then don't update the raw CoP right below
- {
- tempCoP2d.setIncludingFrame(copRawInFootFrame);
- }
- }
- }
-
- copRawInFootFrame.set(tempCoP2d);
-
- tempCoPOffset.setIncludingFrame(soleFrame, copFilteredInFootFrame.getX(), copFilteredInFootFrame.getY(), 0.0);
- copFilteredInFootFrame.update();
- tempCoPOffset.setIncludingFrame(soleFrame,
- copFilteredInFootFrame.getX() - tempCoPOffset.getX(),
- copFilteredInFootFrame.getY() - tempCoPOffset.getY(),
- 0.0);
-
- tempCoPOffset.changeFrame(worldFrame);
- copPositionInWorld.add(tempCoPOffset);
- }
- else
- {
- tempCoP2d.setToZero(soleFrame);
- }
- }
-
- /**
- * Assuming the CoP is not moving, the foot position can be updated. That way we can see if the foot
- * is on the edge.
- *
- * @param plantedFoot
- */
- private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
- {
- if (!trustCoPAsNonSlippingContactPoint)
- return;
-
- tempCoPOffset.setIncludingFrame(copFilteredInFootFrame, 0.0);
- tempCoPOffset.changeFrame(worldFrame);
- footPositionInWorld.sub(copPositionInWorld, tempCoPOffset);
- }
-
- /**
- * Updates the different kinematics related stuff that is used to estimate the pelvis state
- */
- public void updateKinematics()
- {
- tempFramePoint.setToZero(rootJointFrame);
- tempFramePoint.changeFrame(soleFrame);
-
- tempFrameVector.setIncludingFrame(tempFramePoint);
- tempFrameVector.changeFrame(worldFrame);
-
- footToRootJointPosition.update(tempFrameVector);
- }
-
- private final Twist tempRootBodyTwist = new Twist();
- private final Twist footTwistInWorld = new Twist();
-
- private void updateFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist)
- {
- computeFootLinearVelocityInWorld(rootBodyTwist, footVelocityInWorld);
- }
-
- private void computeFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist, FixedFrameVector3DBasics footLinearVelocityToPack)
- {
- tempRootBodyTwist.setIncludingFrame(rootBodyTwist);
- tempRootBodyTwist.setBaseFrame(worldFrame);
- tempRootBodyTwist.changeFrame(foot.getBodyFixedFrame());
-
- foot.getBodyFixedFrame().getTwistRelativeToOther(rootJointFrame, footTwistInWorld);
- footTwistInWorld.add(tempRootBodyTwist);
- footTwistInWorld.setBodyFrame(soleFrame);
- footTwistInWorld.changeFrame(worldFrame);
-
- footTwistInWorld.getLinearVelocityAt(copPositionInWorld, footLinearVelocityToPack);
- }
+ return footEstimatorMap.get(foot).getFootVelocityInWorld();
}
@Override
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
new file mode 100644
index 000000000000..29d6dc63497f
--- /dev/null
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -0,0 +1,325 @@
+package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation;
+
+import us.ihmc.euclid.referenceFrame.FrameConvexPolygon2D;
+import us.ihmc.euclid.referenceFrame.FrameLineSegment2D;
+import us.ihmc.euclid.referenceFrame.FramePoint2D;
+import us.ihmc.euclid.referenceFrame.FramePoint3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
+import us.ihmc.euclid.referenceFrame.ReferenceFrame;
+import us.ihmc.euclid.referenceFrame.interfaces.FixedFramePoint3DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVertex2DSupplier;
+import us.ihmc.graphicsDescription.appearance.YoAppearance;
+import us.ihmc.graphicsDescription.yoGraphics.YoGraphicPosition;
+import us.ihmc.graphicsDescription.yoGraphics.YoGraphicsListRegistry;
+import us.ihmc.graphicsDescription.yoGraphics.plotting.YoArtifactPosition;
+import us.ihmc.humanoidRobotics.model.CenterOfPressureDataHolder;
+import us.ihmc.log.LogTools;
+import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
+import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
+import us.ihmc.mecano.spatial.Twist;
+import us.ihmc.mecano.spatial.interfaces.TwistReadOnly;
+import us.ihmc.robotics.SCS2YoGraphicHolder;
+import us.ihmc.robotics.contactable.ContactablePlaneBody;
+import us.ihmc.robotics.sensors.FootSwitchInterface;
+import us.ihmc.scs2.definition.visual.ColorDefinitions;
+import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinition;
+import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory;
+import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory.DefaultPoint2DGraphic;
+import us.ihmc.scs2.definition.yoGraphic.YoGraphicPoint2DDefinition;
+import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFramePoint2D;
+import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFrameVector3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint2D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
+import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.filters.AlphaFilteredYoVariable;
+import us.ihmc.yoVariables.providers.DoubleProvider;
+import us.ihmc.yoVariables.registry.YoRegistry;
+
+class SingleFootEstimator implements SCS2YoGraphicHolder
+{
+ private final RigidBodyBasics foot;
+
+ private final ReferenceFrame rootJointFrame;
+ private final ReferenceFrame soleFrame;
+ private final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+
+ private final YoFrameVector3D footVelocityInWorld;
+ private final AlphaFilteredYoFrameVector3D footToRootJointPosition;
+ private final YoFramePoint3D footPositionInWorld;
+ /** Debug variable */
+ private final YoFramePoint3D rootJointPositionPerFoot;
+ private final YoFramePoint3D copPositionInWorld;
+ private final AlphaFilteredYoFramePoint2D copFilteredInFootFrame;
+ private final YoFramePoint2D copRawInFootFrame;
+ private final FrameConvexPolygon2D footPolygon;
+ private final FrameLineSegment2D footCenterCoPLineSegment;
+ private final FootSwitchInterface footSwitch;
+ private final CenterOfPressureDataHolder centerOfPressureDataHolderFromController;
+
+ private final FramePoint2DBasics[] intersectionPoints = new FramePoint2DBasics[] {new FramePoint2D(), new FramePoint2D()};
+
+ public SingleFootEstimator(FloatingJointBasics rootJoint,
+ ContactablePlaneBody contactableFoot,
+ FootSwitchInterface footSwitch,
+ DoubleProvider footToRootJointPositionBreakFrequency,
+ DoubleProvider copFilterBreakFrequency,
+ CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
+ double estimatorDT,
+ YoRegistry registry)
+ {
+ this.rootJointFrame = rootJoint.getFrameAfterJoint();
+ this.footSwitch = footSwitch;
+ this.centerOfPressureDataHolderFromController = centerOfPressureDataHolderFromController;
+ foot = contactableFoot.getRigidBody();
+ soleFrame = contactableFoot.getContactFrame();
+
+ String namePrefix = foot.getName();
+
+ DoubleProvider alphaFoot = () -> AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(footToRootJointPositionBreakFrequency.getValue(),
+ estimatorDT);
+ footToRootJointPosition = new AlphaFilteredYoFrameVector3D(namePrefix + "FootToRootJointPosition", "", registry, alphaFoot, worldFrame);
+ rootJointPositionPerFoot = new YoFramePoint3D(namePrefix + "BasedRootJointPosition", worldFrame, registry);
+ footPositionInWorld = new YoFramePoint3D(namePrefix + "FootPositionInWorld", worldFrame, registry);
+ footPolygon = new FrameConvexPolygon2D(FrameVertex2DSupplier.asFrameVertex2DSupplier(contactableFoot.getContactPoints2D()));
+ footCenterCoPLineSegment = new FrameLineSegment2D(soleFrame);
+ copRawInFootFrame = new YoFramePoint2D(namePrefix + "CoPRawInFootFrame", soleFrame, registry);
+
+ DoubleProvider alphaCop = () -> AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(copFilterBreakFrequency.getValue(), estimatorDT);
+ copFilteredInFootFrame = new AlphaFilteredYoFramePoint2D(namePrefix + "CoPFilteredInFootFrame", "", registry, alphaCop, copRawInFootFrame);
+ copFilteredInFootFrame.update(0.0, 0.0);
+ copPositionInWorld = new YoFramePoint3D(namePrefix + "CoPPositionsInWorld", worldFrame, registry);
+ footVelocityInWorld = new YoFrameVector3D(namePrefix + "VelocityInWorld", worldFrame, registry);
+ }
+
+ public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
+ {
+ if (yoGraphicsListRegistry == null)
+ return;
+
+ String sidePrefix = foot.getName();
+ YoGraphicPosition copInWorld = new YoGraphicPosition(sidePrefix + "StateEstimatorCoP", copPositionInWorld, 0.005, YoAppearance.DeepPink());
+ YoArtifactPosition artifact = copInWorld.createArtifact();
+ artifact.setVisible(false);
+ yoGraphicsListRegistry.registerArtifact("StateEstimator", artifact);
+ }
+
+ @Override
+ public YoGraphicDefinition getSCS2YoGraphics()
+ {
+ YoGraphicPoint2DDefinition copVisual = YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP",
+ copPositionInWorld,
+ 0.01,
+ ColorDefinitions.DeepPink(),
+ DefaultPoint2DGraphic.CIRCLE);
+ return copVisual;
+ }
+
+ public void initialize()
+ {
+ footToRootJointPosition.reset();
+ copFilteredInFootFrame.reset();
+ copFilteredInFootFrame.update(0.0, 0.0);
+ footVelocityInWorld.setToZero();
+ }
+
+ private final FramePoint3D tempFramePoint = new FramePoint3D();
+
+ /**
+ * Estimates the pelvis position and linear velocity using the leg kinematics
+ *
+ * @param trustedFoot is the foot used to estimates the pelvis state
+ * @param numberOfTrustedSides is only one or both legs used to estimate the pelvis state
+ */
+ public void updatePelvisWithKinematics(int numberOfTrustedFeet,
+ double alphaVelocityUpdate,
+ FixedFramePoint3DBasics rootJointPosition,
+ FixedFrameVector3DBasics rootJointLinearVelocity)
+ {
+ double scaleFactor = 1.0 / numberOfTrustedFeet;
+
+ rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
+ rootJointPosition.scaleAdd(scaleFactor, rootJointPositionPerFoot, rootJointPosition);
+
+ rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaVelocityUpdate, footVelocityInWorld, rootJointLinearVelocity);
+ }
+
+ /**
+ * updates the position of a swinging foot
+ *
+ * @param swingingFoot a foot in swing
+ * @param pelvisPosition the current pelvis position
+ */
+ public void updateUntrustedFootPosition(FramePoint3DReadOnly pelvisPosition)
+ {
+ footPositionInWorld.sub(pelvisPosition, footToRootJointPosition);
+
+ copPositionInWorld.set(footPositionInWorld);
+
+ copRawInFootFrame.setToZero();
+ copFilteredInFootFrame.setToZero();
+ }
+
+ private final FrameVector3D tempFrameVector = new FrameVector3D();
+
+ public void updateTrustedFootPosition(boolean trustCoPAsNonSlippingContactPoint, FramePoint3DReadOnly rootJointPosition)
+ {
+ if (trustCoPAsNonSlippingContactPoint)
+ {
+ tempFrameVector.setIncludingFrame(rootJointPosition);
+ tempFrameVector.sub(footToRootJointPosition); // New foot position
+ tempFrameVector.sub(footPositionInWorld); // Delta from previous to new foot position
+ copPositionInWorld.add(tempFrameVector); // New CoP position
+ }
+
+ footPositionInWorld.set(rootJointPosition);
+ footPositionInWorld.sub(footToRootJointPosition);
+ }
+
+ public void setCoPZPositionInWorld(boolean useControllerDesiredCoP, double zPosition)
+ {
+ updateCoPPosition(true, useControllerDesiredCoP);
+ copPositionInWorld.setZ(zPosition);
+ correctFootPositionsUsingCoP(true);
+ }
+
+ public void setFootZPositionInWorld(double zPosition)
+ {
+ footPositionInWorld.setZ(zPosition);
+ }
+
+ private final FramePoint2D tempCoP2d = new FramePoint2D();
+ private final FrameVector3D tempCoPOffset = new FrameVector3D();
+
+ /**
+ * Compute the foot CoP. The CoP is the point on the support foot trusted to be not slipping.
+ *
+ * @param trustedSide
+ * @param footSwitch
+ */
+ public void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
+ {
+ if (trustCoPAsNonSlippingContactPoint)
+ {
+ if (useControllerDesiredCoP)
+ centerOfPressureDataHolderFromController.getCenterOfPressure(tempCoP2d, foot);
+ else
+ footSwitch.getCenterOfPressure(tempCoP2d);
+
+ if (tempCoP2d.containsNaN())
+ {
+ tempCoP2d.setToZero(soleFrame);
+ }
+ else
+ {
+ boolean isCoPInsideFoot = footPolygon.isPointInside(tempCoP2d);
+ if (!isCoPInsideFoot)
+ {
+ if (footSwitch.getFootLoadPercentage() > 0.2)
+ {
+ footCenterCoPLineSegment.set(soleFrame, 0.0, 0.0, tempCoP2d.getX(), tempCoP2d.getY());
+ int intersections = footPolygon.intersectionWith(footCenterCoPLineSegment, intersectionPoints[0], intersectionPoints[1]);
+
+ if (intersections == 0)
+ {
+ LogTools.info("Found no solution for the CoP projection.");
+ tempCoP2d.setToZero(soleFrame);
+ }
+ else
+ {
+ tempCoP2d.set(intersectionPoints[0]);
+
+ if (intersections == 2)
+ LogTools.info("Found two solutions for the CoP projection.");
+ }
+ }
+ else // If foot barely loaded and actual CoP outside, then don't update the raw CoP right below
+ {
+ tempCoP2d.setIncludingFrame(copRawInFootFrame);
+ }
+ }
+ }
+
+ copRawInFootFrame.set(tempCoP2d);
+
+ tempCoPOffset.setIncludingFrame(soleFrame, copFilteredInFootFrame.getX(), copFilteredInFootFrame.getY(), 0.0);
+ copFilteredInFootFrame.update();
+ tempCoPOffset.setIncludingFrame(soleFrame,
+ copFilteredInFootFrame.getX() - tempCoPOffset.getX(),
+ copFilteredInFootFrame.getY() - tempCoPOffset.getY(),
+ 0.0);
+
+ tempCoPOffset.changeFrame(worldFrame);
+ copPositionInWorld.add(tempCoPOffset);
+ }
+ else
+ {
+ tempCoP2d.setToZero(soleFrame);
+ }
+ }
+
+ /**
+ * Assuming the CoP is not moving, the foot position can be updated. That way we can see if the foot
+ * is on the edge.
+ *
+ * @param plantedFoot
+ */
+ public void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
+ {
+ if (!trustCoPAsNonSlippingContactPoint)
+ return;
+
+ tempCoPOffset.setIncludingFrame(copFilteredInFootFrame, 0.0);
+ tempCoPOffset.changeFrame(worldFrame);
+ footPositionInWorld.sub(copPositionInWorld, tempCoPOffset);
+ }
+
+ /**
+ * Updates the different kinematics related stuff that is used to estimate the pelvis state
+ */
+ public void updateKinematics()
+ {
+ tempFramePoint.setToZero(rootJointFrame);
+ tempFramePoint.changeFrame(soleFrame);
+
+ tempFrameVector.setIncludingFrame(tempFramePoint);
+ tempFrameVector.changeFrame(worldFrame);
+
+ footToRootJointPosition.update(tempFrameVector);
+ }
+
+ private final Twist tempRootBodyTwist = new Twist();
+ private final Twist footTwistInWorld = new Twist();
+
+ public void updateFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist)
+ {
+ computeFootLinearVelocityInWorld(rootBodyTwist, footVelocityInWorld);
+ }
+
+ private void computeFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist, FixedFrameVector3DBasics footLinearVelocityToPack)
+ {
+ tempRootBodyTwist.setIncludingFrame(rootBodyTwist);
+ tempRootBodyTwist.setBaseFrame(worldFrame);
+ tempRootBodyTwist.changeFrame(foot.getBodyFixedFrame());
+
+ foot.getBodyFixedFrame().getTwistRelativeToOther(rootJointFrame, footTwistInWorld);
+ footTwistInWorld.add(tempRootBodyTwist);
+ footTwistInWorld.setBodyFrame(soleFrame);
+ footTwistInWorld.changeFrame(worldFrame);
+
+ footTwistInWorld.getLinearVelocityAt(copPositionInWorld, footLinearVelocityToPack);
+ }
+
+ public FrameVector3DReadOnly getFootToRootJointPosition()
+ {
+ return footToRootJointPosition;
+ }
+
+ public FrameVector3DReadOnly getFootVelocityInWorld()
+ {
+ return footVelocityInWorld;
+ }
+}
From 0a7f98c5e38c1d86b5588f685af92a3874983259 Mon Sep 17 00:00:00 2001
From: Robert
Date: Wed, 3 Sep 2025 23:53:55 -0500
Subject: [PATCH 13/18] added the imu as a filter for the feet
---
...sKinematicsBasedLinearStateCalculator.java | 75 +++--
.../PelvisLinearStateUpdater.java | 12 +
.../SingleFootEstimator.java | 313 +++++++++++++-----
3 files changed, 278 insertions(+), 122 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index 28b22dd874c6..845db3663e16 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -5,9 +5,12 @@
import java.util.Map;
import us.ihmc.euclid.referenceFrame.FramePoint3D;
+import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
+import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.graphicsDescription.yoGraphics.YoGraphicsListRegistry;
import us.ihmc.humanoidRobotics.model.CenterOfPressureDataHolder;
import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
@@ -15,6 +18,7 @@
import us.ihmc.mecano.spatial.Twist;
import us.ihmc.robotics.SCS2YoGraphicHolder;
import us.ihmc.robotics.contactable.ContactablePlaneBody;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.yoVariables.euclid.filters.BacklashCompensatingVelocityYoFrameVector3D;
import us.ihmc.robotics.sensors.FootSwitchInterface;
import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinition;
@@ -76,8 +80,12 @@ public class PelvisKinematicsBasedLinearStateCalculator implements SCS2YoGraphic
public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure inverseDynamicsStructure,
Map feetContactablePlaneBodies,
Map footSwitches,
+ Map footIMUs,
+ IMUBiasProvider imuBiasProvider,
CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
double estimatorDT,
+ double gravitationalAcceleration,
StateEstimatorParameters stateEstimatorParameters,
YoGraphicsListRegistry yoGraphicsListRegistry,
YoRegistry parentRegistry)
@@ -98,6 +106,16 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
copFilterBreakFrequency = new DoubleParameter("CopFilterBreakFrequency", registry, stateEstimatorParameters.getCoPFilterFreqInHertz());
correctTrustedFeetPositions = new BooleanParameter("correctTrustedFeetPositions", registry, stateEstimatorParameters.correctTrustedFeetPositions());
+ YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
+ YoDouble imuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
+ YoDouble imuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
+ imuAgainstKinematicsForPositionBreakFrequency.set(200.0);
+ imuAgainstKinematicsForVelocityBreakFrequency.set(2.0);
+ footAlphaLeakIMUOnly.set(0.999);
+
+ FrameVector3DReadOnly gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
+
+
footEstimators = new SingleFootEstimator[feetRigidBodies.length];
for (int i = 0; i < feetRigidBodies.length; i++)
{
@@ -107,9 +125,16 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
footEstimators[i] = new SingleFootEstimator(rootJoint,
contactableFoot,
footSwitch,
+ footIMUs.get(footRigidBody),
+ imuBiasProvider,
footToRootJointPositionBreakFrequency,
copFilterBreakFrequency,
centerOfPressureDataHolderFromController,
+ cancelGravityFromAccelerationMeasurement,
+ gravityVector,
+ imuAgainstKinematicsForPositionBreakFrequency,
+ imuAgainstKinematicsForVelocityBreakFrequency,
+ footAlphaLeakIMUOnly,
estimatorDT,
registry);
footEstimatorMap.put(footRigidBody, footEstimators[i]);
@@ -160,36 +185,32 @@ public void initialize(FramePoint3DReadOnly pelvisPosition)
for (SingleFootEstimator footEstimator : footEstimators)
{
- footEstimator.updateUntrustedFootPosition(pelvisPosition);
+ footEstimator.correctFootPositionFromRootJoint(false, false, pelvisPosition);
}
kinematicsIsUpToDate.set(false);
}
+ private final Twist tempRootBodyTwist = new Twist();
+
/**
* Updates the different kinematics related stuff that is used to estimate the pelvis state
*/
public void updateKinematics()
{
rootJointPosition.setToZero();
- updateKinematicsNewTwist();
-
- for (SingleFootEstimator footEstimator : footEstimators)
- footEstimator.updateKinematics();
- kinematicsIsUpToDate.set(true);
- }
-
- private final Twist tempRootBodyTwist = new Twist();
-
- private void updateKinematicsNewTwist()
- {
tempRootBodyTwist.setIncludingFrame(rootJoint.getJointTwist());
tempRootBodyTwist.getLinearPart().setMatchingFrame(rootJointLinearVelocity);
for (SingleFootEstimator footEstimator : footEstimators)
- footEstimator.updateFootLinearVelocityInWorld(tempRootBodyTwist);
+ {
+ footEstimator.updateKinematics(tempRootBodyTwist);
+ }
+
+ kinematicsIsUpToDate.set(true);
}
+
/**
* Updates this module when no foot can be trusted.
*
@@ -202,7 +223,7 @@ public void updateNoTrustedFeet(FramePoint3DReadOnly pelvisPosition, FrameVector
{
for (SingleFootEstimator footEstimator : footEstimators)
{
- footEstimator.updateUntrustedFootPosition(pelvisPosition);
+ footEstimator.correctFootPositionFromRootJoint(false, false, pelvisPosition);
}
rootJointPosition.set(pelvisPosition);
@@ -218,31 +239,25 @@ public void estimatePelvisLinearState(List trustedFeet, List trustedFeet, List footIMUs = new HashMap<>();
+ for (IMUSensorReadOnly imu : imuProcessedOutputs)
+ {
+ if (feetContactablePlaneBodies.containsKey(imu.getMeasurementLink()))
+ footIMUs.put(imu.getMeasurementLink(), imu);
+ }
+
kinematicsBasedLinearStateCalculator = new PelvisKinematicsBasedLinearStateCalculator(inverseDynamicsStructure,
feetContactablePlaneBodies,
footSwitches,
+ footIMUs,
+ imuBiasProvider,
centerOfPressureDataHolderFromController,
+ cancelGravityFromAccelerationMeasurement,
estimatorDT,
+ gravitationalAcceleration,
stateEstimatorParameters,
yoGraphicsListRegistry,
registry);
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index 29d6dc63497f..8b6c0b449991 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -29,70 +29,113 @@
import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinition;
import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory;
import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinitionFactory.DefaultPoint2DGraphic;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicPoint2DDefinition;
+import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFramePoint2D;
import us.ihmc.yoVariables.euclid.filters.AlphaFilteredYoFrameVector3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint2D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
-import us.ihmc.yoVariables.filters.AlphaFilteredYoVariable;
+import us.ihmc.yoVariables.filters.AlphaFilterTools;
+import us.ihmc.yoVariables.providers.BooleanProvider;
import us.ihmc.yoVariables.providers.DoubleProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
class SingleFootEstimator implements SCS2YoGraphicHolder
{
+ private static boolean USE_IMU_DATA = true;
+
private final RigidBodyBasics foot;
private final ReferenceFrame rootJointFrame;
private final ReferenceFrame soleFrame;
private final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
- private final YoFrameVector3D footVelocityInWorld;
+ private final YoFrameVector3D footIKLinearVelocityInWorld;
+ private final YoFrameVector3D footIKAngularVelocityInWorld;
+ private final YoFrameVector3D footIMULinearVelocityInWorld;
+ private final YoFrameVector3D footIMUAngularVelocityInWorld;
+ private final YoFrameVector3D footFusedLinearVelocityInWorld;
+ private final YoFrameVector3D footFusedAngularVelocityInWorld;
+
+ private final YoFrameVector3D copVelocityInWorld;
private final AlphaFilteredYoFrameVector3D footToRootJointPosition;
private final YoFramePoint3D footPositionInWorld;
/** Debug variable */
private final YoFramePoint3D rootJointPositionPerFoot;
+
private final YoFramePoint3D copPositionInWorld;
+ private final YoFramePoint2D previousCoPInFootFrame;
+
private final AlphaFilteredYoFramePoint2D copFilteredInFootFrame;
private final YoFramePoint2D copRawInFootFrame;
private final FrameConvexPolygon2D footPolygon;
private final FrameLineSegment2D footCenterCoPLineSegment;
private final FootSwitchInterface footSwitch;
+ private final IMUSensorReadOnly footIMU;
+ private final IMUBiasProvider imuBiasProvider;
private final CenterOfPressureDataHolder centerOfPressureDataHolderFromController;
+ private final DoubleProvider footAlphaLeakIMUOnly;
+ private final DoubleProvider imuAgainstKinematicsForVelocityBreakFrequency;
+ private final DoubleProvider imuAgainstKinematicsForPositionBreakFrequency;
+ private final BooleanProvider cancelGravityFromAccelerationMeasurement;
+ private final FrameVector3DReadOnly gravityVector;
+ private final double estimatorDT;
private final FramePoint2DBasics[] intersectionPoints = new FramePoint2DBasics[] {new FramePoint2D(), new FramePoint2D()};
public SingleFootEstimator(FloatingJointBasics rootJoint,
ContactablePlaneBody contactableFoot,
FootSwitchInterface footSwitch,
+ IMUSensorReadOnly footIMU,
+ IMUBiasProvider imuBiasProvider,
DoubleProvider footToRootJointPositionBreakFrequency,
DoubleProvider copFilterBreakFrequency,
CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
+ BooleanProvider cancelGravityFromAccelerationMeasurement,
+ FrameVector3DReadOnly gravityVector,
+ DoubleProvider imuAgainstKinematicsForPositionBreakFrequency,
+ DoubleProvider imuAgainstKinematicsForVelocityBreakFrequency,
+ DoubleProvider footAlphaLeakIMUOnly,
double estimatorDT,
YoRegistry registry)
{
this.rootJointFrame = rootJoint.getFrameAfterJoint();
this.footSwitch = footSwitch;
+ this.footIMU = footIMU;
+ this.imuBiasProvider = imuBiasProvider;
this.centerOfPressureDataHolderFromController = centerOfPressureDataHolderFromController;
+ this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
+ this.gravityVector = gravityVector;
+ this.imuAgainstKinematicsForPositionBreakFrequency = imuAgainstKinematicsForPositionBreakFrequency;
+ this.imuAgainstKinematicsForVelocityBreakFrequency = imuAgainstKinematicsForVelocityBreakFrequency;
+ this.footAlphaLeakIMUOnly = footAlphaLeakIMUOnly;
+ this.estimatorDT = estimatorDT;
foot = contactableFoot.getRigidBody();
soleFrame = contactableFoot.getContactFrame();
String namePrefix = foot.getName();
- DoubleProvider alphaFoot = () -> AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(footToRootJointPositionBreakFrequency.getValue(),
- estimatorDT);
+ DoubleProvider alphaFoot = () -> AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(footToRootJointPositionBreakFrequency.getValue(), estimatorDT);
footToRootJointPosition = new AlphaFilteredYoFrameVector3D(namePrefix + "FootToRootJointPosition", "", registry, alphaFoot, worldFrame);
rootJointPositionPerFoot = new YoFramePoint3D(namePrefix + "BasedRootJointPosition", worldFrame, registry);
footPositionInWorld = new YoFramePoint3D(namePrefix + "FootPositionInWorld", worldFrame, registry);
footPolygon = new FrameConvexPolygon2D(FrameVertex2DSupplier.asFrameVertex2DSupplier(contactableFoot.getContactPoints2D()));
footCenterCoPLineSegment = new FrameLineSegment2D(soleFrame);
copRawInFootFrame = new YoFramePoint2D(namePrefix + "CoPRawInFootFrame", soleFrame, registry);
+ previousCoPInFootFrame = new YoFramePoint2D(namePrefix + "PreviousCoPInFootFrame", soleFrame, registry);
- DoubleProvider alphaCop = () -> AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(copFilterBreakFrequency.getValue(), estimatorDT);
+ footIKLinearVelocityInWorld = new YoFrameVector3D(namePrefix + "IKLinearVelocityInWorld", worldFrame, registry);
+ footIKAngularVelocityInWorld = new YoFrameVector3D(namePrefix + "IKAngularVelocityInWorld", worldFrame, registry);
+ footIMULinearVelocityInWorld = new YoFrameVector3D(namePrefix + "IMULinearVelocityInWorld", worldFrame, registry);
+ footIMUAngularVelocityInWorld = new YoFrameVector3D(namePrefix + "IMUAngularVelocityInWorld", worldFrame, registry);
+ footFusedLinearVelocityInWorld = new YoFrameVector3D(namePrefix + "FusedLinearVelocityInWorld", worldFrame, registry);
+ footFusedAngularVelocityInWorld = new YoFrameVector3D(namePrefix + "FusedAngularVelocityInWorld", worldFrame, registry);
+
+ DoubleProvider alphaCop = () -> AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(copFilterBreakFrequency.getValue(), estimatorDT);
copFilteredInFootFrame = new AlphaFilteredYoFramePoint2D(namePrefix + "CoPFilteredInFootFrame", "", registry, alphaCop, copRawInFootFrame);
copFilteredInFootFrame.update(0.0, 0.0);
- copPositionInWorld = new YoFramePoint3D(namePrefix + "CoPPositionsInWorld", worldFrame, registry);
- footVelocityInWorld = new YoFrameVector3D(namePrefix + "VelocityInWorld", worldFrame, registry);
+ copPositionInWorld = new YoFramePoint3D(namePrefix + "CoPPositionInWorld", worldFrame, registry);
+ copVelocityInWorld = new YoFrameVector3D(namePrefix + "CoPVelocityInWorld", worldFrame, registry);
}
public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
@@ -110,12 +153,10 @@ public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
@Override
public YoGraphicDefinition getSCS2YoGraphics()
{
- YoGraphicPoint2DDefinition copVisual = YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP",
- copPositionInWorld,
- 0.01,
- ColorDefinitions.DeepPink(),
- DefaultPoint2DGraphic.CIRCLE);
- return copVisual;
+ return YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP", copPositionInWorld,
+ 0.01,
+ ColorDefinitions.DeepPink(),
+ DefaultPoint2DGraphic.CIRCLE);
}
public void initialize()
@@ -123,18 +164,67 @@ public void initialize()
footToRootJointPosition.reset();
copFilteredInFootFrame.reset();
copFilteredInFootFrame.update(0.0, 0.0);
- footVelocityInWorld.setToZero();
+ copVelocityInWorld.setToZero();
}
private final FramePoint3D tempFramePoint = new FramePoint3D();
+ private final Twist tempRootBodyTwist = new Twist();
+ private final Twist footTwistInWorld = new Twist();
+ private final Twist imuTwist = new Twist();
+
+
+ private final FrameVector3D tempFrameVector = new FrameVector3D();
+ private final FramePoint3D tempPoint = new FramePoint3D();
+
+ /**
+ * Updates the different kinematics related stuff that is used to estimate the pelvis state. This should be called before calling the other objectives.
+ */
+ public void updateKinematics(TwistReadOnly rootBodyTwist)
+ {
+ computeFootTwistInWorld(rootBodyTwist);
+ computeCoPLinearVelocityInWorld(copVelocityInWorld);
+
+ tempFramePoint.setToZero(rootJointFrame);
+ tempFramePoint.changeFrame(soleFrame);
+
+ tempFrameVector.setIncludingFrame(tempFramePoint);
+ tempFrameVector.changeFrame(worldFrame);
+
+ footToRootJointPosition.update(tempFrameVector);
+ }
+
+ /**
+ * One of the options for computing the
+ * @param useControllerDesiredCoP
+ * @param zPosition
+ */
+ public void updateCoPAndFootSettingZ(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP, double zPosition)
+ {
+ updateCoPPosition(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
+ copPositionInWorld.setZ(zPosition);
+ correctFootPositionsUsingCoP(trustCoPAsNonSlippingContactPoint);
+ }
+
+ public void updateCoPAndFootPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
+ {
+ updateCoPPosition(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
+ correctFootPositionsUsingCoP(trustCoPAsNonSlippingContactPoint);
+ }
+
+ public void setFootZPositionInWorld(double zPosition)
+ {
+ footPositionInWorld.setZ(zPosition);
+ }
+
+
/**
* Estimates the pelvis position and linear velocity using the leg kinematics
*
* @param trustedFoot is the foot used to estimates the pelvis state
* @param numberOfTrustedSides is only one or both legs used to estimate the pelvis state
*/
- public void updatePelvisWithKinematics(int numberOfTrustedFeet,
+ public void correctPelvisFromKinematics(int numberOfTrustedFeet,
double alphaVelocityUpdate,
FixedFramePoint3DBasics rootJointPosition,
FixedFrameVector3DBasics rootJointLinearVelocity)
@@ -144,63 +234,64 @@ public void updatePelvisWithKinematics(int numberOfTrustedFeet,
rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
rootJointPosition.scaleAdd(scaleFactor, rootJointPositionPerFoot, rootJointPosition);
- rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaVelocityUpdate, footVelocityInWorld, rootJointLinearVelocity);
+ rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaVelocityUpdate, copVelocityInWorld, rootJointLinearVelocity);
}
/**
- * updates the position of a swinging foot
+ * Corrects the position of the foot and center of pressure in the world using the root joint position and the forward kinematics from the root joint to the
+ * foot. If the foot is not trusted, it sets the center of pressure from the foot. If it is trusted, but is non slipping, we shift the center of pressure
+ * based on this correction.
+ *
+ *
+ * This should be called once the fusion of the different feet to compute the root joint state has occurred.
+ *
*
- * @param swingingFoot a foot in swing
- * @param pelvisPosition the current pelvis position
+ * @param isTrusted whether the foot is trusted, and therefore the CoP is trusted.
+ * @param trustCoPAsNonSlippingContactPoint whether to shift the CoP of a trusted foot based on the correction
+ * @param rootJointPosition accurate root joint position to apply.
*/
- public void updateUntrustedFootPosition(FramePoint3DReadOnly pelvisPosition)
+ public void correctFootPositionFromRootJoint(boolean isTrusted, boolean trustCoPAsNonSlippingContactPoint, FramePoint3DReadOnly rootJointPosition)
{
- footPositionInWorld.sub(pelvisPosition, footToRootJointPosition);
+ tempPoint.set(footPositionInWorld);
+ footPositionInWorld.sub(rootJointPosition, footToRootJointPosition);
- copPositionInWorld.set(footPositionInWorld);
-
- copRawInFootFrame.setToZero();
- copFilteredInFootFrame.setToZero();
- }
-
- private final FrameVector3D tempFrameVector = new FrameVector3D();
-
- public void updateTrustedFootPosition(boolean trustCoPAsNonSlippingContactPoint, FramePoint3DReadOnly rootJointPosition)
- {
- if (trustCoPAsNonSlippingContactPoint)
+ if (!isTrusted)
{
- tempFrameVector.setIncludingFrame(rootJointPosition);
- tempFrameVector.sub(footToRootJointPosition); // New foot position
- tempFrameVector.sub(footPositionInWorld); // Delta from previous to new foot position
+ copPositionInWorld.set(footPositionInWorld);
+ copRawInFootFrame.setToZero();
+ copFilteredInFootFrame.setToZero();
+ }
+ else if (trustCoPAsNonSlippingContactPoint)
+ {
+ tempFrameVector.sub(footPositionInWorld, tempPoint); // Delta from previous to new foot position
copPositionInWorld.add(tempFrameVector); // New CoP position
}
+ }
+
+ public FrameVector3DReadOnly getFootToRootJointPosition()
+ {
+ return footToRootJointPosition;
+ }
- footPositionInWorld.set(rootJointPosition);
- footPositionInWorld.sub(footToRootJointPosition);
+ public FrameVector3DReadOnly getCopVelocityInWorld()
+ {
+ return copVelocityInWorld;
}
- public void setCoPZPositionInWorld(boolean useControllerDesiredCoP, double zPosition)
+ public FrameVector3DReadOnly getFootLinearVelocityInWorld()
{
- updateCoPPosition(true, useControllerDesiredCoP);
- copPositionInWorld.setZ(zPosition);
- correctFootPositionsUsingCoP(true);
+ return footFusedLinearVelocityInWorld;
}
- public void setFootZPositionInWorld(double zPosition)
+ public FrameVector3DReadOnly getFootAngularVelocityInWorld()
{
- footPositionInWorld.setZ(zPosition);
+ return footFusedAngularVelocityInWorld;
}
private final FramePoint2D tempCoP2d = new FramePoint2D();
private final FrameVector3D tempCoPOffset = new FrameVector3D();
- /**
- * Compute the foot CoP. The CoP is the point on the support foot trusted to be not slipping.
- *
- * @param trustedSide
- * @param footSwitch
- */
- public void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
+ private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
{
if (trustCoPAsNonSlippingContactPoint)
{
@@ -243,63 +334,47 @@ public void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean
}
}
- copRawInFootFrame.set(tempCoP2d);
- tempCoPOffset.setIncludingFrame(soleFrame, copFilteredInFootFrame.getX(), copFilteredInFootFrame.getY(), 0.0);
+ // Update the CoP values.
+ previousCoPInFootFrame.set(copFilteredInFootFrame);
+ copRawInFootFrame.set(tempCoP2d);
copFilteredInFootFrame.update();
+
+ // Update the change in the CoP from the previous tick.
tempCoPOffset.setIncludingFrame(soleFrame,
- copFilteredInFootFrame.getX() - tempCoPOffset.getX(),
- copFilteredInFootFrame.getY() - tempCoPOffset.getY(),
+ copFilteredInFootFrame.getX() - previousCoPInFootFrame.getX(),
+ copFilteredInFootFrame.getY() - previousCoPInFootFrame.getY(),
0.0);
-
tempCoPOffset.changeFrame(worldFrame);
+ // Apply this same change to the CoP in world.
copPositionInWorld.add(tempCoPOffset);
}
else
{
tempCoP2d.setToZero(soleFrame);
+ copRawInFootFrame.setToZero();
+ copFilteredInFootFrame.setToZero();
+ copPositionInWorld.setFromReferenceFrame(soleFrame);
}
+
}
- /**
- * Assuming the CoP is not moving, the foot position can be updated. That way we can see if the foot
- * is on the edge.
- *
- * @param plantedFoot
- */
- public void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
+ private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
{
if (!trustCoPAsNonSlippingContactPoint)
return;
+ // Compute where the CoP position is in the world, according to the kinematics
tempCoPOffset.setIncludingFrame(copFilteredInFootFrame, 0.0);
tempCoPOffset.changeFrame(worldFrame);
+ // Update where the foot must be, according to this CoP location
footPositionInWorld.sub(copPositionInWorld, tempCoPOffset);
}
- /**
- * Updates the different kinematics related stuff that is used to estimate the pelvis state
- */
- public void updateKinematics()
- {
- tempFramePoint.setToZero(rootJointFrame);
- tempFramePoint.changeFrame(soleFrame);
-
- tempFrameVector.setIncludingFrame(tempFramePoint);
- tempFrameVector.changeFrame(worldFrame);
+ private final FrameVector3D linearAcceleration = new FrameVector3D();
+ private final FrameVector3D angularVelocity = new FrameVector3D();
- footToRootJointPosition.update(tempFrameVector);
- }
-
- private final Twist tempRootBodyTwist = new Twist();
- private final Twist footTwistInWorld = new Twist();
-
- public void updateFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist)
- {
- computeFootLinearVelocityInWorld(rootBodyTwist, footVelocityInWorld);
- }
-
- private void computeFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist, FixedFrameVector3DBasics footLinearVelocityToPack)
+ private void computeFootTwistInWorld(TwistReadOnly rootBodyTwist)
{
tempRootBodyTwist.setIncludingFrame(rootBodyTwist);
tempRootBodyTwist.setBaseFrame(worldFrame);
@@ -310,16 +385,70 @@ private void computeFootLinearVelocityInWorld(TwistReadOnly rootBodyTwist, Fixed
footTwistInWorld.setBodyFrame(soleFrame);
footTwistInWorld.changeFrame(worldFrame);
- footTwistInWorld.getLinearVelocityAt(copPositionInWorld, footLinearVelocityToPack);
- }
+ if (USE_IMU_DATA)
+ {
+ // Record the kinematic data
+ footIKLinearVelocityInWorld.set(footTwistInWorld.getLinearPart());
+ footIKAngularVelocityInWorld.set(footTwistInWorld.getAngularPart());
+
+ // Get the previous IMU twist estimate from the fused data set
+ imuTwist.setToZero(worldFrame);
+ imuTwist.getLinearPart().set(footFusedLinearVelocityInWorld);
+ imuTwist.getAngularPart().set(footFusedAngularVelocityInWorld);
+ imuTwist.changeFrame(footIMU.getMeasurementFrame());
+
+ // Compute the acceleration reading from the IMU
+ linearAcceleration.setIncludingFrame(footIMU.getMeasurementFrame(), footIMU.getLinearAccelerationMeasurement());
+ FrameVector3DReadOnly accelerationBias = imuBiasProvider.getLinearAccelerationBiasInIMUFrame(footIMU);
+ if (accelerationBias != null)
+ linearAcceleration.sub(accelerationBias);
+
+ // Update acceleration (minus gravity)
+ if (cancelGravityFromAccelerationMeasurement.getValue())
+ {
+ linearAcceleration.changeFrame(worldFrame);
+ linearAcceleration.add(gravityVector);
+ linearAcceleration.changeFrame(footIMU.getMeasurementFrame());
+ }
- public FrameVector3DReadOnly getFootToRootJointPosition()
- {
- return footToRootJointPosition;
+ // Compute the gyro reading from the IMU
+ angularVelocity.setIncludingFrame(footIMU.getMeasurementFrame(), footIMU.getAngularVelocityMeasurement());
+ FrameVector3DReadOnly gyroBias = imuBiasProvider.getAngularVelocityBiasInIMUFrame(footIMU);
+ if (gyroBias != null)
+ angularVelocity.sub(gyroBias);
+
+ // Integrate the linear acceleration into the velocity measurement
+ imuTwist.getLinearPart().scaleAdd(estimatorDT * footAlphaLeakIMUOnly.getValue(), linearAcceleration, imuTwist.getLinearPart());
+ // Set the angular part from the gyro
+ imuTwist.getAngularPart().set(angularVelocity);
+
+ imuTwist.changeFrame(worldFrame);
+
+ footIMUAngularVelocityInWorld.set(imuTwist.getAngularPart());
+ footIMULinearVelocityInWorld.set(imuTwist.getLinearPart());
+
+ // Fuse the IMU and kinematics data for velocity
+ double alpha = AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(imuAgainstKinematicsForVelocityBreakFrequency.getValue(), estimatorDT);
+ footFusedAngularVelocityInWorld.interpolate(footIKAngularVelocityInWorld, footIMUAngularVelocityInWorld, alpha);
+ footFusedLinearVelocityInWorld.interpolate(footIKLinearVelocityInWorld, footIMULinearVelocityInWorld, alpha);
+ }
+ else
+ {
+ footIKLinearVelocityInWorld.setToNaN();
+ footIKAngularVelocityInWorld.setToNaN();
+ footIMULinearVelocityInWorld.setToNaN();
+ footIMUAngularVelocityInWorld.setToNaN();
+
+ footFusedLinearVelocityInWorld.set(footTwistInWorld.getLinearPart());
+ footFusedAngularVelocityInWorld.set(footTwistInWorld.getAngularPart());
+ }
}
- public FrameVector3DReadOnly getFootVelocityInWorld()
+ private void computeCoPLinearVelocityInWorld(FixedFrameVector3DBasics footLinearVelocityToPack)
{
- return footVelocityInWorld;
+ footTwistInWorld.getLinearPart().set(footFusedLinearVelocityInWorld);
+ footTwistInWorld.getAngularPart().set(footFusedAngularVelocityInWorld);
+
+ footTwistInWorld.getLinearVelocityAt(copPositionInWorld, footLinearVelocityToPack);
}
}
From feaa06ddae3ff0e89521d208edade662c3d3bbff Mon Sep 17 00:00:00 2001
From: Robert
Date: Thu, 4 Sep 2025 01:36:26 -0500
Subject: [PATCH 14/18] got the IMU based locomotion working better
---
...sKinematicsBasedLinearStateCalculator.java | 60 ++++++++++++++---
.../PelvisLinearStateUpdater.java | 6 +-
.../SingleFootEstimator.java | 64 +++++++++++++++----
3 files changed, 106 insertions(+), 24 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index 845db3663e16..fedc28bc87d2 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -1,5 +1,6 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -109,7 +110,7 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
YoDouble imuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
YoDouble imuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
- imuAgainstKinematicsForPositionBreakFrequency.set(200.0);
+ imuAgainstKinematicsForPositionBreakFrequency.set(0.0);
imuAgainstKinematicsForVelocityBreakFrequency.set(2.0);
footAlphaLeakIMUOnly.set(0.999);
@@ -234,14 +235,28 @@ public void updateNoTrustedFeet(FramePoint3DReadOnly pelvisPosition, FrameVector
rootJointLinearVelocity.setToZero();
}
- public void estimatePelvisLinearState(List trustedFeet, List unTrustedFeet, FramePoint3DReadOnly pelvisPosition)
+ private final List trustedFeetLocal = new ArrayList<>();
+ private final List unTrustedFeetLocal = new ArrayList<>();
+
+ public void estimatePelvisLinearState(List trustedFeet,
+ List unTrustedFeet,
+ FramePoint3DReadOnly pelvisPosition,
+ FrameVector3DReadOnly pelvisLinearVelocity)
{
if (!kinematicsIsUpToDate.getBooleanValue())
throw new RuntimeException("Leg kinematics needs to be updated before trying to estimate the pelvis position/linear velocity.");
+ trustedFeetLocal.clear();
+ unTrustedFeetLocal.clear();
+
for (int i = 0; i < trustedFeet.size(); i++)
+ trustedFeetLocal.add(trustedFeet.get(i));
+ for (int i = 0; i < unTrustedFeet.size(); i++)
+ unTrustedFeetLocal.add(unTrustedFeet.get(i));
+
+ for (int i = 0; i < trustedFeetLocal.size(); i++)
{
- SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeet.get(i));
+ SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeetLocal.get(i));
if (assumeTrustedFootAtZeroHeight.getValue())
{
if (trustCoPAsNonSlippingContactPoint.getValue())
@@ -257,23 +272,52 @@ public void estimatePelvisLinearState(List trustedFeet, List 0)
{
updateTrustedFeetLists();
- kinematicsBasedLinearStateCalculator.estimatePelvisLinearState(listOfTrustedFeet, listOfUnTrustedFeet, rootJointPosition);
+ kinematicsBasedLinearStateCalculator.estimatePelvisLinearState(listOfTrustedFeet, listOfUnTrustedFeet, rootJointPosition, rootJointVelocity);
if (imuBasedLinearStateCalculator.isEstimationEnabled())
{
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index 8b6c0b449991..be9aa14b638e 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -9,6 +9,7 @@
import us.ihmc.euclid.referenceFrame.interfaces.FixedFramePoint3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVertex2DSupplier;
@@ -39,6 +40,7 @@
import us.ihmc.yoVariables.providers.BooleanProvider;
import us.ihmc.yoVariables.providers.DoubleProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
+import us.ihmc.yoVariables.variable.YoBoolean;
class SingleFootEstimator implements SCS2YoGraphicHolder
{
@@ -57,13 +59,17 @@ class SingleFootEstimator implements SCS2YoGraphicHolder
private final YoFrameVector3D footFusedLinearVelocityInWorld;
private final YoFrameVector3D footFusedAngularVelocityInWorld;
+ private final YoBoolean footIsMoving;
+
private final YoFrameVector3D copVelocityInWorld;
private final AlphaFilteredYoFrameVector3D footToRootJointPosition;
private final YoFramePoint3D footPositionInWorld;
/** Debug variable */
private final YoFramePoint3D rootJointPositionPerFoot;
- private final YoFramePoint3D copPositionInWorld;
+ private final YoFramePoint3D copIKPositionInWorld;
+ private final YoFramePoint3D copIMUPositionInWorld;
+ private final YoFramePoint3D copFusedPositionInWorld;
private final YoFramePoint2D previousCoPInFootFrame;
private final AlphaFilteredYoFramePoint2D copFilteredInFootFrame;
@@ -124,6 +130,8 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
copRawInFootFrame = new YoFramePoint2D(namePrefix + "CoPRawInFootFrame", soleFrame, registry);
previousCoPInFootFrame = new YoFramePoint2D(namePrefix + "PreviousCoPInFootFrame", soleFrame, registry);
+ footIsMoving = new YoBoolean(namePrefix + "IsMoving", registry);
+
footIKLinearVelocityInWorld = new YoFrameVector3D(namePrefix + "IKLinearVelocityInWorld", worldFrame, registry);
footIKAngularVelocityInWorld = new YoFrameVector3D(namePrefix + "IKAngularVelocityInWorld", worldFrame, registry);
footIMULinearVelocityInWorld = new YoFrameVector3D(namePrefix + "IMULinearVelocityInWorld", worldFrame, registry);
@@ -134,7 +142,9 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
DoubleProvider alphaCop = () -> AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(copFilterBreakFrequency.getValue(), estimatorDT);
copFilteredInFootFrame = new AlphaFilteredYoFramePoint2D(namePrefix + "CoPFilteredInFootFrame", "", registry, alphaCop, copRawInFootFrame);
copFilteredInFootFrame.update(0.0, 0.0);
- copPositionInWorld = new YoFramePoint3D(namePrefix + "CoPPositionInWorld", worldFrame, registry);
+ copIKPositionInWorld = new YoFramePoint3D(namePrefix + "CoPIKPositionInWorld", worldFrame, registry);
+ copIMUPositionInWorld = new YoFramePoint3D(namePrefix + "CoPIMUPositionInWorld", worldFrame, registry);
+ copFusedPositionInWorld = new YoFramePoint3D(namePrefix + "CoPFusedPositionInWorld", worldFrame, registry);
copVelocityInWorld = new YoFrameVector3D(namePrefix + "CoPVelocityInWorld", worldFrame, registry);
}
@@ -144,7 +154,7 @@ public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
return;
String sidePrefix = foot.getName();
- YoGraphicPosition copInWorld = new YoGraphicPosition(sidePrefix + "StateEstimatorCoP", copPositionInWorld, 0.005, YoAppearance.DeepPink());
+ YoGraphicPosition copInWorld = new YoGraphicPosition(sidePrefix + "StateEstimatorCoP", copFusedPositionInWorld, 0.005, YoAppearance.DeepPink());
YoArtifactPosition artifact = copInWorld.createArtifact();
artifact.setVisible(false);
yoGraphicsListRegistry.registerArtifact("StateEstimator", artifact);
@@ -153,7 +163,7 @@ public void createVisualization(YoGraphicsListRegistry yoGraphicsListRegistry)
@Override
public YoGraphicDefinition getSCS2YoGraphics()
{
- return YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP", copPositionInWorld,
+ return YoGraphicDefinitionFactory.newYoGraphicPoint2D(foot.getName() + "StateEstimatorCoP", copFusedPositionInWorld,
0.01,
ColorDefinitions.DeepPink(),
DefaultPoint2DGraphic.CIRCLE);
@@ -192,6 +202,12 @@ public void updateKinematics(TwistReadOnly rootBodyTwist)
tempFrameVector.changeFrame(worldFrame);
footToRootJointPosition.update(tempFrameVector);
+
+ // FIXME remove magic parameters
+ if (USE_IMU_DATA)
+ footIsMoving.set(getFootLinearVelocityInWorld().norm() > 0.5 || getFootAngularVelocityInWorld().norm() > 1.0);
+ else
+ footIsMoving.set(false);
}
/**
@@ -202,7 +218,7 @@ public void updateKinematics(TwistReadOnly rootBodyTwist)
public void updateCoPAndFootSettingZ(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP, double zPosition)
{
updateCoPPosition(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
- copPositionInWorld.setZ(zPosition);
+ copFusedPositionInWorld.setZ(zPosition);
correctFootPositionsUsingCoP(trustCoPAsNonSlippingContactPoint);
}
@@ -257,14 +273,14 @@ public void correctFootPositionFromRootJoint(boolean isTrusted, boolean trustCoP
if (!isTrusted)
{
- copPositionInWorld.set(footPositionInWorld);
+ copFusedPositionInWorld.set(footPositionInWorld);
copRawInFootFrame.setToZero();
copFilteredInFootFrame.setToZero();
}
else if (trustCoPAsNonSlippingContactPoint)
{
tempFrameVector.sub(footPositionInWorld, tempPoint); // Delta from previous to new foot position
- copPositionInWorld.add(tempFrameVector); // New CoP position
+ copFusedPositionInWorld.add(tempFrameVector); // New CoP position
}
}
@@ -288,6 +304,11 @@ public FrameVector3DReadOnly getFootAngularVelocityInWorld()
return footFusedAngularVelocityInWorld;
}
+ public boolean isFootMoving()
+ {
+ return footIsMoving.getBooleanValue();
+ }
+
private final FramePoint2D tempCoP2d = new FramePoint2D();
private final FrameVector3D tempCoPOffset = new FrameVector3D();
@@ -340,6 +361,9 @@ private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolea
copRawInFootFrame.set(tempCoP2d);
copFilteredInFootFrame.update();
+ // Update the CoP value from kinematics. This treats it as non-stationary.
+ copIKPositionInWorld.setMatchingFrame(soleFrame, copFilteredInFootFrame.getX(), copFilteredInFootFrame.getY(), 0.0);
+
// Update the change in the CoP from the previous tick.
tempCoPOffset.setIncludingFrame(soleFrame,
copFilteredInFootFrame.getX() - previousCoPInFootFrame.getX(),
@@ -347,16 +371,25 @@ private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolea
0.0);
tempCoPOffset.changeFrame(worldFrame);
// Apply this same change to the CoP in world.
- copPositionInWorld.add(tempCoPOffset);
+ copFusedPositionInWorld.add(tempCoPOffset);
}
else
{
tempCoP2d.setToZero(soleFrame);
- copRawInFootFrame.setToZero();
- copFilteredInFootFrame.setToZero();
- copPositionInWorld.setFromReferenceFrame(soleFrame);
+ // Update the CoP value to match the center of the foot.
+ copIKPositionInWorld.setFromReferenceFrame(soleFrame);
}
+ if (USE_IMU_DATA)
+ {
+ // Integrate the fused position using the velocity, which is from the kinematics and the IMU.
+ computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, tempFrameVector);
+ copIMUPositionInWorld.scaleAdd(footAlphaLeakIMUOnly.getValue() * estimatorDT, tempFrameVector, copFusedPositionInWorld);
+
+ // Fuse the kinematic data with the IMU data
+ double alpha = AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(imuAgainstKinematicsForPositionBreakFrequency.getValue(), estimatorDT);
+ copFusedPositionInWorld.interpolate(copIKPositionInWorld, copIMUPositionInWorld, alpha);
+ }
}
private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
@@ -368,7 +401,7 @@ private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPo
tempCoPOffset.setIncludingFrame(copFilteredInFootFrame, 0.0);
tempCoPOffset.changeFrame(worldFrame);
// Update where the foot must be, according to this CoP location
- footPositionInWorld.sub(copPositionInWorld, tempCoPOffset);
+ footPositionInWorld.sub(copFusedPositionInWorld, tempCoPOffset);
}
private final FrameVector3D linearAcceleration = new FrameVector3D();
@@ -445,10 +478,15 @@ private void computeFootTwistInWorld(TwistReadOnly rootBodyTwist)
}
private void computeCoPLinearVelocityInWorld(FixedFrameVector3DBasics footLinearVelocityToPack)
+ {
+ computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, footLinearVelocityToPack);
+ }
+
+ private void computeLinearVelocityAtPointInWorld(FramePoint3DReadOnly point, FixedFrameVector3DBasics footLinearVelocityToPack)
{
footTwistInWorld.getLinearPart().set(footFusedLinearVelocityInWorld);
footTwistInWorld.getAngularPart().set(footFusedAngularVelocityInWorld);
- footTwistInWorld.getLinearVelocityAt(copPositionInWorld, footLinearVelocityToPack);
+ footTwistInWorld.getLinearVelocityAt(point, footLinearVelocityToPack);
}
}
From aa2d75f50a13653513a0ab49a5c41a6b8f6d4019 Mon Sep 17 00:00:00 2001
From: Robert
Date: Thu, 4 Sep 2025 01:54:46 -0500
Subject: [PATCH 15/18] started passing in a use imu flag
---
.../PelvisKinematicsBasedLinearStateCalculator.java | 2 ++
.../SingleFootEstimator.java | 11 ++++++-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index fedc28bc87d2..c93f58ab61a0 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -107,6 +107,7 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
copFilterBreakFrequency = new DoubleParameter("CopFilterBreakFrequency", registry, stateEstimatorParameters.getCoPFilterFreqInHertz());
correctTrustedFeetPositions = new BooleanParameter("correctTrustedFeetPositions", registry, stateEstimatorParameters.correctTrustedFeetPositions());
+ YoBoolean useFootIMUData = new YoBoolean("useFootIMUData", registry);
YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
YoDouble imuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
YoDouble imuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
@@ -133,6 +134,7 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
centerOfPressureDataHolderFromController,
cancelGravityFromAccelerationMeasurement,
gravityVector,
+ useFootIMUData,
imuAgainstKinematicsForPositionBreakFrequency,
imuAgainstKinematicsForVelocityBreakFrequency,
footAlphaLeakIMUOnly,
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index be9aa14b638e..8dd07ba743ea 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -44,14 +44,13 @@
class SingleFootEstimator implements SCS2YoGraphicHolder
{
- private static boolean USE_IMU_DATA = true;
-
private final RigidBodyBasics foot;
private final ReferenceFrame rootJointFrame;
private final ReferenceFrame soleFrame;
private final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
+ private final BooleanProvider useIMUData;
private final YoFrameVector3D footIKLinearVelocityInWorld;
private final YoFrameVector3D footIKAngularVelocityInWorld;
private final YoFrameVector3D footIMULinearVelocityInWorld;
@@ -99,6 +98,7 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
BooleanProvider cancelGravityFromAccelerationMeasurement,
FrameVector3DReadOnly gravityVector,
+ BooleanProvider useIMUData,
DoubleProvider imuAgainstKinematicsForPositionBreakFrequency,
DoubleProvider imuAgainstKinematicsForVelocityBreakFrequency,
DoubleProvider footAlphaLeakIMUOnly,
@@ -112,6 +112,7 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
this.centerOfPressureDataHolderFromController = centerOfPressureDataHolderFromController;
this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
this.gravityVector = gravityVector;
+ this.useIMUData = useIMUData;
this.imuAgainstKinematicsForPositionBreakFrequency = imuAgainstKinematicsForPositionBreakFrequency;
this.imuAgainstKinematicsForVelocityBreakFrequency = imuAgainstKinematicsForVelocityBreakFrequency;
this.footAlphaLeakIMUOnly = footAlphaLeakIMUOnly;
@@ -204,7 +205,7 @@ public void updateKinematics(TwistReadOnly rootBodyTwist)
footToRootJointPosition.update(tempFrameVector);
// FIXME remove magic parameters
- if (USE_IMU_DATA)
+ if (useIMUData.getValue() && footIMU != null)
footIsMoving.set(getFootLinearVelocityInWorld().norm() > 0.5 || getFootAngularVelocityInWorld().norm() > 1.0);
else
footIsMoving.set(false);
@@ -380,7 +381,7 @@ private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolea
copIKPositionInWorld.setFromReferenceFrame(soleFrame);
}
- if (USE_IMU_DATA)
+ if (useIMUData.getValue() && footIMU != null)
{
// Integrate the fused position using the velocity, which is from the kinematics and the IMU.
computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, tempFrameVector);
@@ -418,7 +419,7 @@ private void computeFootTwistInWorld(TwistReadOnly rootBodyTwist)
footTwistInWorld.setBodyFrame(soleFrame);
footTwistInWorld.changeFrame(worldFrame);
- if (USE_IMU_DATA)
+ if (useIMUData.getValue() && footIMU != null)
{
// Record the kinematic data
footIKLinearVelocityInWorld.set(footTwistInWorld.getLinearPart());
From cb153e89673d642b718250a079e724cd1ce33f08 Mon Sep 17 00:00:00 2001
From: Robert
Date: Tue, 16 Sep 2025 16:40:00 -0500
Subject: [PATCH 16/18] refactor where the foot estimator states exist
---
...sKinematicsBasedLinearStateCalculator.java | 285 ++++--------------
.../PelvisLinearStateUpdater.java | 228 ++++++++++++--
.../SingleFootEstimator.java | 83 +++--
3 files changed, 301 insertions(+), 295 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index c93f58ab61a0..1c74f54ef57f 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -1,33 +1,25 @@
package us.ihmc.stateEstimation.humanoid.kinematicsBasedStateEstimation;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import us.ihmc.euclid.referenceFrame.FramePoint3D;
-import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
-import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
-import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.graphicsDescription.yoGraphics.YoGraphicsListRegistry;
import us.ihmc.humanoidRobotics.model.CenterOfPressureDataHolder;
-import us.ihmc.mecano.multiBodySystem.interfaces.FloatingJointBasics;
import us.ihmc.mecano.multiBodySystem.interfaces.RigidBodyBasics;
-import us.ihmc.mecano.spatial.Twist;
-import us.ihmc.robotics.SCS2YoGraphicHolder;
import us.ihmc.robotics.contactable.ContactablePlaneBody;
import us.ihmc.sensorProcessing.stateEstimation.IMUSensorReadOnly;
import us.ihmc.yoVariables.euclid.filters.BacklashCompensatingVelocityYoFrameVector3D;
import us.ihmc.robotics.sensors.FootSwitchInterface;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicDefinition;
-import us.ihmc.scs2.definition.yoGraphic.YoGraphicGroupDefinition;
import us.ihmc.sensorProcessing.stateEstimation.StateEstimatorParameters;
import us.ihmc.sensorProcessing.stateEstimation.evaluation.FullInverseDynamicsStructure;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFramePoint3D;
import us.ihmc.yoVariables.euclid.referenceFrame.YoFrameVector3D;
+import us.ihmc.yoVariables.filters.AlphaFilterTools;
import us.ihmc.yoVariables.filters.AlphaFilteredYoVariable;
import us.ihmc.yoVariables.parameters.BooleanParameter;
import us.ihmc.yoVariables.parameters.DoubleParameter;
@@ -43,16 +35,11 @@
*
* @author Sylvain
*/
-public class PelvisKinematicsBasedLinearStateCalculator implements SCS2YoGraphicHolder
+public class PelvisKinematicsBasedLinearStateCalculator
{
- private static final boolean VISUALIZE = true;
-
private final YoRegistry registry = new YoRegistry(getClass().getSimpleName());
- private final FloatingJointBasics rootJoint;
- private final RigidBodyBasics[] feetRigidBodies;
- private final SingleFootEstimator[] footEstimators;
- private final Map footEstimatorMap = new HashMap<>();
+ private final Map footEstimatorMap;
private final ReferenceFrame worldFrame = ReferenceFrame.getWorldFrame();
@@ -60,93 +47,39 @@ public class PelvisKinematicsBasedLinearStateCalculator implements SCS2YoGraphic
private final YoFrameVector3D rootJointLinearVelocity = new YoFrameVector3D("estimatedRootJointLinearVelocityKinematics", worldFrame, registry);
private final DoubleProvider alphaRootJointLinearVelocity;
- /** Debug variable */
- private final YoDouble alphaLinearVelocityDebug = new YoDouble("alphaRootJointLinearVelocityBacklashKinematics", registry);
- /** Debug variable */
- private final YoDouble slopTimeLinearVelocityDebug = new YoDouble("slopTimeRootJointLinearVelocityBacklashKinematics", registry);
/** Debug variable */
private final BacklashCompensatingVelocityYoFrameVector3D rootJointLinearVelocityFDDebug;
- private final DoubleProvider footToRootJointPositionBreakFrequency;
private final BooleanProvider correctTrustedFeetPositions;
- private final DoubleProvider copFilterBreakFrequency;
-
private final YoBoolean kinematicsIsUpToDate = new YoBoolean("kinematicsIsUpToDate", registry);
- private final BooleanProvider useControllerDesiredCoP;
private final BooleanProvider trustCoPAsNonSlippingContactPoint;
private final BooleanParameter assumeTrustedFootAtZeroHeight = new BooleanParameter("assumeTrustedFootAtZeroHeight", registry, false);
+ private final BooleanProvider useControllerDesiredCoP;
- public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure inverseDynamicsStructure,
- Map feetContactablePlaneBodies,
- Map footSwitches,
- Map footIMUs,
- IMUBiasProvider imuBiasProvider,
- CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
- BooleanProvider cancelGravityFromAccelerationMeasurement,
+ public PelvisKinematicsBasedLinearStateCalculator(Map footEstimatorMap,
double estimatorDT,
- double gravitationalAcceleration,
StateEstimatorParameters stateEstimatorParameters,
- YoGraphicsListRegistry yoGraphicsListRegistry,
YoRegistry parentRegistry)
{
- rootJoint = inverseDynamicsStructure.getRootJoint();
- feetRigidBodies = feetContactablePlaneBodies.keySet().toArray(new RigidBodyBasics[0]);
-
- footToRootJointPositionBreakFrequency = new DoubleParameter("FootToRootJointPositionBreakFrequency",
- registry,
- stateEstimatorParameters.getKinematicsPelvisPositionFilterFreqInHertz());
+ this.footEstimatorMap = footEstimatorMap;
alphaRootJointLinearVelocity = new DoubleParameter("alphaRootJointLinearVelocityKinematics",
registry,
stateEstimatorParameters.getPelvisLinearVelocityAlphaNewTwist());
trustCoPAsNonSlippingContactPoint = new BooleanParameter("trustCoPAsNonSlippingContactPoint",
registry,
stateEstimatorParameters.trustCoPAsNonSlippingContactPoint());
- useControllerDesiredCoP = new BooleanParameter("useControllerDesiredCoP", registry, stateEstimatorParameters.useControllerDesiredCenterOfPressure());
- copFilterBreakFrequency = new DoubleParameter("CopFilterBreakFrequency", registry, stateEstimatorParameters.getCoPFilterFreqInHertz());
- correctTrustedFeetPositions = new BooleanParameter("correctTrustedFeetPositions", registry, stateEstimatorParameters.correctTrustedFeetPositions());
-
- YoBoolean useFootIMUData = new YoBoolean("useFootIMUData", registry);
- YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
- YoDouble imuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
- YoDouble imuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
- imuAgainstKinematicsForPositionBreakFrequency.set(0.0);
- imuAgainstKinematicsForVelocityBreakFrequency.set(2.0);
- footAlphaLeakIMUOnly.set(0.999);
-
- FrameVector3DReadOnly gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
-
- footEstimators = new SingleFootEstimator[feetRigidBodies.length];
- for (int i = 0; i < feetRigidBodies.length; i++)
- {
- RigidBodyBasics footRigidBody = feetRigidBodies[i];
- ContactablePlaneBody contactableFoot = feetContactablePlaneBodies.get(footRigidBody);
- FootSwitchInterface footSwitch = footSwitches.get(footRigidBody);
- footEstimators[i] = new SingleFootEstimator(rootJoint,
- contactableFoot,
- footSwitch,
- footIMUs.get(footRigidBody),
- imuBiasProvider,
- footToRootJointPositionBreakFrequency,
- copFilterBreakFrequency,
- centerOfPressureDataHolderFromController,
- cancelGravityFromAccelerationMeasurement,
- gravityVector,
- useFootIMUData,
- imuAgainstKinematicsForPositionBreakFrequency,
- imuAgainstKinematicsForVelocityBreakFrequency,
- footAlphaLeakIMUOnly,
- estimatorDT,
- registry);
- footEstimatorMap.put(footRigidBody, footEstimators[i]);
- }
+ correctTrustedFeetPositions = new BooleanParameter("correctTrustedFeetPositions", registry, stateEstimatorParameters.correctTrustedFeetPositions());
+ useControllerDesiredCoP = new BooleanParameter("useControllerDesiredCoP", registry, stateEstimatorParameters.useControllerDesiredCenterOfPressure());
/*
* These are for debug purposes, not need to clutter the state estimator parameters class with them.
*/
- alphaLinearVelocityDebug.set(AlphaFilteredYoVariable.computeAlphaGivenBreakFrequencyProperly(16.0, estimatorDT));
+ YoDouble alphaLinearVelocityDebug = new YoDouble("alphaRootJointLinearVelocityBacklashKinematics", registry);
+ YoDouble slopTimeLinearVelocityDebug = new YoDouble("slopTimeRootJointLinearVelocityBacklashKinematics", registry);
+ alphaLinearVelocityDebug.set(AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(16.0, estimatorDT));
slopTimeLinearVelocityDebug.set(0.03);
rootJointLinearVelocityFDDebug = new BacklashCompensatingVelocityYoFrameVector3D("estimatedRootJointLinearVelocityBacklashKin",
"",
@@ -159,41 +92,26 @@ public PelvisKinematicsBasedLinearStateCalculator(FullInverseDynamicsStructure i
* -------------------------------------------------------------------------------------------------
*/
- if (VISUALIZE)
- {
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- footEstimator.createVisualization(yoGraphicsListRegistry);
- }
- }
parentRegistry.addChild(registry);
}
/**
- * Estimates the foot positions corresponding to the given pelvisPosition
+ * Estimates the foot positions corresponding to the given rootJointPosition
*
- * @param pelvisPosition
+ * @param rootJointPosition
*/
- public void initialize(FramePoint3DReadOnly pelvisPosition)
+ public void initialize(FramePoint3DReadOnly rootJointPosition)
{
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- footEstimator.initialize();
- }
- setPelvisLinearVelocityToZero();
+ rootJointLinearVelocityFDDebug.reset();
+ rootJointLinearVelocityFDDebug.setToZero();
+ rootJointLinearVelocity.setToZero();
- updateKinematics();
- setPelvisPosition(pelvisPosition);
+ this.rootJointPosition.set(rootJointPosition);
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- footEstimator.correctFootPositionFromRootJoint(false, false, pelvisPosition);
- }
kinematicsIsUpToDate.set(false);
}
- private final Twist tempRootBodyTwist = new Twist();
/**
* Updates the different kinematics related stuff that is used to estimate the pelvis state
@@ -201,91 +119,58 @@ public void initialize(FramePoint3DReadOnly pelvisPosition)
public void updateKinematics()
{
rootJointPosition.setToZero();
-
- tempRootBodyTwist.setIncludingFrame(rootJoint.getJointTwist());
- tempRootBodyTwist.getLinearPart().setMatchingFrame(rootJointLinearVelocity);
-
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- footEstimator.updateKinematics(tempRootBodyTwist);
- }
-
kinematicsIsUpToDate.set(true);
}
-
/**
* Updates this module when no foot can be trusted.
*
- * @param pelvisPosition the current estimated pelvis position. This module does not compute
+ * @param rootJointPosition the current estimated pelvis position. This module does not compute
* an estimate.
- * @param pelvisLinearVelocity the current estimated pelvis linear velocity. This module does not
+ * @param rootJointLinearVelocity the current estimated pelvis linear velocity. This module does not
* compute an estimate. If {@code null}, a zero velocity is assumed.
*/
- public void updateNoTrustedFeet(FramePoint3DReadOnly pelvisPosition, FrameVector3DReadOnly pelvisLinearVelocity)
+ public void setRootJointState(FramePoint3DReadOnly rootJointPosition, FrameVector3DReadOnly rootJointLinearVelocity)
{
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- footEstimator.correctFootPositionFromRootJoint(false, false, pelvisPosition);
- }
-
- rootJointPosition.set(pelvisPosition);
+ this.rootJointPosition.set(rootJointPosition);
- if (pelvisLinearVelocity != null)
- rootJointLinearVelocity.set(pelvisLinearVelocity);
+ if (rootJointLinearVelocity != null)
+ this.rootJointLinearVelocity.set(rootJointLinearVelocity);
else
- rootJointLinearVelocity.setToZero();
+ this.rootJointLinearVelocity.setToZero();
}
- private final List trustedFeetLocal = new ArrayList<>();
- private final List unTrustedFeetLocal = new ArrayList<>();
+ public boolean getKinematicsIsUpToDate()
+ {
+ return kinematicsIsUpToDate.getBooleanValue();
+ }
- public void estimatePelvisLinearState(List trustedFeet,
- List unTrustedFeet,
- FramePoint3DReadOnly pelvisPosition,
- FrameVector3DReadOnly pelvisLinearVelocity)
+ public boolean getAssumeTrustedFootAtZeroHeight()
{
- if (!kinematicsIsUpToDate.getBooleanValue())
- throw new RuntimeException("Leg kinematics needs to be updated before trying to estimate the pelvis position/linear velocity.");
+ return assumeTrustedFootAtZeroHeight.getValue();
+ }
- trustedFeetLocal.clear();
- unTrustedFeetLocal.clear();
+ public boolean getTrustCoPAsNonSlippingContactPoint()
+ {
+ return trustCoPAsNonSlippingContactPoint.getValue();
+ }
- for (int i = 0; i < trustedFeet.size(); i++)
- trustedFeetLocal.add(trustedFeet.get(i));
- for (int i = 0; i < unTrustedFeet.size(); i++)
- unTrustedFeetLocal.add(unTrustedFeet.get(i));
+ public boolean getUseControllerDesiredCoP()
+ {
+ return useControllerDesiredCoP.getValue();
+ }
- for (int i = 0; i < trustedFeetLocal.size(); i++)
- {
- SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeetLocal.get(i));
- if (assumeTrustedFootAtZeroHeight.getValue())
- {
- if (trustCoPAsNonSlippingContactPoint.getValue())
- {
- footEstimator.updateCoPAndFootSettingZ(trustCoPAsNonSlippingContactPoint.getValue(), useControllerDesiredCoP.getValue(), 0.0);
- }
- else
- {
- footEstimator.setFootZPositionInWorld(0.0);
- }
- }
- else
- {
- footEstimator.updateCoPAndFootPosition(trustCoPAsNonSlippingContactPoint.getValue(), useControllerDesiredCoP.getValue());
- }
- }
+ public boolean getCorrectTrustedFeetPositions()
+ {
+ return correctTrustedFeetPositions.getValue();
+ }
- int trustIdx = 0;
- while (trustIdx < trustedFeetLocal.size())
- {
- if (footEstimatorMap.get(trustedFeetLocal.get(trustIdx)).isFootMoving())
- unTrustedFeetLocal.add(trustedFeetLocal.remove(trustIdx));
- else
- trustIdx++;
- }
+ public void estimatePelvisLinearState(List trustedFeet, FramePoint3DReadOnly pelvisPosition, FrameVector3DReadOnly pelvisLinearVelocity)
+ {
+ if (!kinematicsIsUpToDate.getBooleanValue())
+ throw new RuntimeException("Leg kinematics needs to be updated before trying to estimate the pelvis position/linear velocity.");
- if (trustedFeetLocal.isEmpty())
+ if (trustedFeet.isEmpty())
{
rootJointPosition.set(pelvisPosition);
if (pelvisLinearVelocity != null)
@@ -295,56 +180,24 @@ public void estimatePelvisLinearState(List trustedFeet,
}
else
{
- for (int i = 0; i < trustedFeetLocal.size(); i++)
+ for (int i = 0; i < trustedFeet.size(); i++)
{
- SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeetLocal.get(i));
- footEstimator.correctPelvisFromKinematics(trustedFeetLocal.size(),
- alphaRootJointLinearVelocity.getValue(),
- rootJointPosition,
- rootJointLinearVelocity);
- }
- }
+ SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeet.get(i));
- rootJointLinearVelocityFDDebug.update();
+ double scaleFactor = 1.0 / trustedFeet.size();
- // TODO should this use the local copies?
- if (correctTrustedFeetPositions.getValue())
- {
- for (int i = 0; i < trustedFeetLocal.size(); i++)
- {
- SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeetLocal.get(i));
- footEstimator.correctFootPositionFromRootJoint(true, trustCoPAsNonSlippingContactPoint.getValue(), rootJointPosition);
+ rootJointPosition.scaleAdd(scaleFactor, footEstimator.getRootJointPositionFromKinematics(), rootJointPosition);
+ rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaRootJointLinearVelocity.getValue(),
+ footEstimator.getCopVelocityInWorld(),
+ rootJointLinearVelocity);
}
}
- for (int i = 0; i < unTrustedFeetLocal.size(); i++)
- {
- SingleFootEstimator footEstimator = footEstimatorMap.get(unTrustedFeetLocal.get(i));
- footEstimator.correctFootPositionFromRootJoint(false, false, pelvisPosition);
- }
+ rootJointLinearVelocityFDDebug.update();
kinematicsIsUpToDate.set(false);
}
- public void setPelvisPosition(FramePoint3DReadOnly pelvisPosition)
- {
- rootJointPosition.set(pelvisPosition);
- }
-
- public void setPelvisLinearVelocity(FrameVector3DReadOnly pelvisLinearVelocity)
- {
- rootJointLinearVelocityFDDebug.reset();
- rootJointLinearVelocityFDDebug.set(pelvisLinearVelocity);
- rootJointLinearVelocity.set(pelvisLinearVelocity);
- }
-
- public void setPelvisLinearVelocityToZero()
- {
- rootJointLinearVelocityFDDebug.reset();
- rootJointLinearVelocityFDDebug.setToZero();
- rootJointLinearVelocity.setToZero();
- }
-
public FramePoint3DReadOnly getPelvisPosition()
{
return rootJointPosition;
@@ -354,28 +207,4 @@ public FrameVector3DReadOnly getPelvisVelocity()
{
return rootJointLinearVelocity;
}
-
- public void getFootToRootJointPosition(FramePoint3D positionToPack, RigidBodyBasics foot)
- {
- positionToPack.setIncludingFrame(footEstimatorMap.get(foot).getFootToRootJointPosition());
- }
-
- public FrameVector3DReadOnly getFootVelocityInWorld(RigidBodyBasics foot)
- {
- return footEstimatorMap.get(foot).getCopVelocityInWorld();
- }
-
- @Override
- public YoGraphicDefinition getSCS2YoGraphics()
- {
- if (!VISUALIZE)
- return null;
-
- YoGraphicGroupDefinition group = new YoGraphicGroupDefinition(getClass().getSimpleName());
- for (SingleFootEstimator footEstimator : footEstimators)
- {
- group.addChild(footEstimator.getSCS2YoGraphics());
- }
- return group;
- }
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
index dd3237dc2b03..055f1a34a340 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
@@ -12,6 +12,9 @@
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
import us.ihmc.euclid.referenceFrame.interfaces.FixedFramePoint3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
+import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
+import us.ihmc.euclid.referenceFrame.tools.EuclidFrameFactories;
+import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple3D.interfaces.Tuple3DReadOnly;
import us.ihmc.graphicsDescription.yoGraphics.YoGraphicsListRegistry;
import us.ihmc.humanoidRobotics.model.CenterOfPressureDataHolder;
@@ -57,6 +60,7 @@
public class PelvisLinearStateUpdater implements SCS2YoGraphicHolder
{
private static final boolean MORE_YOVARIABLES = false;
+ private static final boolean VISUALIZE_FEET_ESTIMATORS = true;
private static final double minForceZInPercentThresholdToFilterFoot = 0.0;
private static final double maxForceZInPercentThresholdToFilterFoot = 0.45;
@@ -95,11 +99,15 @@ public class PelvisLinearStateUpdater implements SCS2YoGraphicHolder
private final DoubleProvider forceZInPercentThresholdToTrustFoot;
private final DoubleProvider forceZInPercentThresholdToNotTrustFoot;
+ private final SingleFootEstimator[] footEstimators;
+ private final Map footEstimatorMap = new HashMap<>();
+
private final Map footSwitches;
private final Map footForces = new LinkedHashMap<>();
private final Map footWrenches = new LinkedHashMap<>();
private final DoubleProvider delayTimeBeforeTrustingFoot;
private final Map haveFeetHitGroundFiltered = new LinkedHashMap<>();
+ private final Map areFeetNotMovingFiltered = new LinkedHashMap<>();
private final Map areFeetTrusted = new LinkedHashMap<>();
private final Map wereFeetTrustedLastTick = new LinkedHashMap<>();
private final List listOfTrustedFeet = new ArrayList<>();
@@ -174,17 +182,58 @@ public PelvisLinearStateUpdater(FullInverseDynamicsStructure inverseDynamicsStru
footIMUs.put(imu.getMeasurementLink(), imu);
}
- kinematicsBasedLinearStateCalculator = new PelvisKinematicsBasedLinearStateCalculator(inverseDynamicsStructure,
- feetContactablePlaneBodies,
- footSwitches,
- footIMUs,
- imuBiasProvider,
- centerOfPressureDataHolderFromController,
- cancelGravityFromAccelerationMeasurement,
+ FrameVector3DReadOnly gravityVector = EuclidFrameFactories.newLinkedFrameVector3DReadOnly(() -> worldFrame, new Vector3D(0, 0, -Math.abs(gravitationalAcceleration)));
+
+ DoubleProvider footToRootJointPositionBreakFrequency = new DoubleParameter("FootToRootJointPositionBreakFrequency",
+ registry,
+ stateEstimatorParameters.getKinematicsPelvisPositionFilterFreqInHertz());
+ DoubleProvider copFilterBreakFrequency = new DoubleParameter("CopFilterBreakFrequency", registry, stateEstimatorParameters.getCoPFilterFreqInHertz());
+ YoBoolean useFootIMUData = new YoBoolean("useFootIMUData", registry);
+ YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
+ YoDouble footImuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
+ YoDouble footImuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
+ footImuAgainstKinematicsForPositionBreakFrequency.set(12.0);
+ footImuAgainstKinematicsForVelocityBreakFrequency.set(2.0);
+ footAlphaLeakIMUOnly.set(0.999);
+
+ RigidBodyBasics[] feetRigidBodies = feetContactablePlaneBodies.keySet().toArray(new RigidBodyBasics[0]);
+
+ footEstimators = new SingleFootEstimator[feetRigidBodies.length];
+ for (int i = 0; i < feetRigidBodies.length; i++)
+ {
+ RigidBodyBasics footRigidBody = feetRigidBodies[i];
+ ContactablePlaneBody contactableFoot = feetContactablePlaneBodies.get(footRigidBody);
+ FootSwitchInterface footSwitch = footSwitches.get(footRigidBody);
+ footEstimators[i] = new SingleFootEstimator(rootJoint,
+ contactableFoot,
+ footSwitch,
+ footIMUs.get(footRigidBody),
+ imuBiasProvider,
+ footToRootJointPositionBreakFrequency,
+ copFilterBreakFrequency,
+ centerOfPressureDataHolderFromController,
+ cancelGravityFromAccelerationMeasurement,
+ gravityVector,
+ useFootIMUData,
+ footImuAgainstKinematicsForPositionBreakFrequency,
+ footImuAgainstKinematicsForVelocityBreakFrequency,
+ footAlphaLeakIMUOnly,
+ estimatorDT,
+ registry);
+ footEstimatorMap.put(footRigidBody, footEstimators[i]);
+ }
+
+ if (VISUALIZE_FEET_ESTIMATORS)
+ {
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ footEstimator.createVisualization(yoGraphicsListRegistry);
+ }
+ }
+
+ kinematicsBasedLinearStateCalculator = new PelvisKinematicsBasedLinearStateCalculator(footEstimatorMap,
estimatorDT,
- gravitationalAcceleration,
stateEstimatorParameters,
- yoGraphicsListRegistry,
registry);
imuBasedLinearStateCalculator = new PelvisIMUBasedLinearStateCalculator(rootJoint,
@@ -275,8 +324,11 @@ private void setupBunchOfVariables()
String footPrefix = foot.getName();
final GlitchFilteredYoBoolean hasFootHitTheGroundFiltered = new GlitchFilteredYoBoolean("has" + footPrefix + "FootHitGroundFiltered", registry, 0);
+ final GlitchFilteredYoBoolean isFootMovingFiltered = new GlitchFilteredYoBoolean("is" + footPrefix + "FootNotMovingFiltered", registry, 0);
hasFootHitTheGroundFiltered.set(true);
+ isFootMovingFiltered.set(false);
haveFeetHitGroundFiltered.put(foot, hasFootHitTheGroundFiltered);
+ areFeetNotMovingFiltered.put(foot, isFootMovingFiltered);
YoBoolean isFootTrusted = new YoBoolean("is" + footPrefix + "FootTrusted", registry);
YoBoolean wasFootTrusted = new YoBoolean("was" + footPrefix + "FootTrustedLastTick", registry);
@@ -334,6 +386,16 @@ private void initializeRobotState()
rootJointVelocity.setToZero();
kinematicsBasedLinearStateCalculator.initialize(rootJointPosition);
+ // Initialize the feet states
+ tempRootBodyTwist.setIncludingFrame(rootJoint.getJointTwist());
+
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ footEstimator.initialize();
+ footEstimator.updateStateFromKinematics(tempRootBodyTwist);
+ footEstimator.correctFootPositionFromRootJoint(false, false, rootJointPosition);
+ }
+
imuBasedLinearStateCalculator.initialize();
mainIMULinearVelocityEstimate.getPositionEstimation().setToZero();
mainIMULinearVelocityEstimate.getRateEstimation().setToZero();
@@ -352,7 +414,13 @@ public void updateForFrozenState()
// Keep setting the position so the localization updater works properly.
rootJoint.setJointPosition(rootJointPosition);
kinematicsBasedLinearStateCalculator.updateKinematics();
- kinematicsBasedLinearStateCalculator.updateNoTrustedFeet(rootJointPosition, null);
+ updateFeetFromKinematics();
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ footEstimator.correctFootPositionFromRootJoint(false, false, rootJointPosition);
+ }
+ kinematicsBasedLinearStateCalculator.setRootJointState(rootJointPosition, null);
+
// Reset the IMU updater
imuBasedLinearStateCalculator.initialize();
@@ -362,14 +430,51 @@ public void updateForFrozenState()
rootJointVelocity.setToZero();
}
+ private final Twist tempRootBodyTwist = new Twist();
+
+ private void updateFeetFromKinematics()
+ {
+ // FIXME do we want to use the fused pelvis velocity?
+ tempRootBodyTwist.setIncludingFrame(rootJoint.getJointTwist());
+ tempRootBodyTwist.getLinearPart().setMatchingFrame(kinematicsBasedLinearStateCalculator.getPelvisVelocity());
+
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ footEstimator.updateStateFromKinematics(tempRootBodyTwist);
+ }
+ }
+
+ private void correctFeetStateFromPelvis()
+ {
+ if (kinematicsBasedLinearStateCalculator.getCorrectTrustedFeetPositions())
+ {
+ for (int i = 0; i < listOfTrustedFeet.size(); i++)
+ {
+ SingleFootEstimator footEstimator = footEstimatorMap.get(listOfTrustedFeet.get(i));
+ footEstimator.correctFootPositionFromRootJoint(true,
+ kinematicsBasedLinearStateCalculator.getTrustCoPAsNonSlippingContactPoint(),
+ kinematicsBasedLinearStateCalculator.getPelvisPosition());
+ }
+ }
+
+ for (int i = 0; i < listOfUnTrustedFeet.size(); i++)
+ {
+ SingleFootEstimator footEstimator = footEstimatorMap.get(listOfUnTrustedFeet.get(i));
+ footEstimator.correctFootPositionFromRootJoint(false, false, kinematicsBasedLinearStateCalculator.getPelvisPosition());
+ }
+ }
+
+
+
public void updateRootJointPositionAndLinearVelocity()
{
kinematicsBasedLinearStateCalculator.updateKinematics();
+ updateFeetFromKinematics();
if (requestStopEstimationOfPelvisLinearState.getBooleanValue())
return;
- numberOfEndEffectorsTrusted.set(setTrustedFeetUsingFootSwitches());
+ numberOfEndEffectorsTrusted.set(setTrustedFeetUsingFootSwitchesAndKinematics());
numberOfEndEffectorsFilteredByLoad.set(0);
if (numberOfEndEffectorsTrusted.getIntegerValue() >= optimalNumberOfTrustedFeet.getValue())
@@ -405,15 +510,21 @@ public void updateRootJointPositionAndLinearVelocity()
tempTwist.changeFrame(rootJointFrame);
rootJointNewLinearVelocityEstimate.setMatchingFrame(tempTwist.getLinearPart());
rootJointPositionEstimate.update(null, rootJointNewLinearVelocityEstimate);
- kinematicsBasedLinearStateCalculator.updateNoTrustedFeet(rootJointPosition, rootJointVelocity);
+
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ footEstimator.correctFootPositionFromRootJoint(false, false, rootJointPosition);
+ }
+
+ kinematicsBasedLinearStateCalculator.setRootJointState(rootJointPosition, rootJointVelocity);
}
else
throw new RuntimeException("No foot trusted!");
}
else if (numberOfEndEffectorsTrusted.getIntegerValue() > 0)
{
- updateTrustedFeetLists();
- kinematicsBasedLinearStateCalculator.estimatePelvisLinearState(listOfTrustedFeet, listOfUnTrustedFeet, rootJointPosition, rootJointVelocity);
+ updateTrustedFeet();
+ kinematicsBasedLinearStateCalculator.estimatePelvisLinearState(listOfTrustedFeet, rootJointPosition, rootJointVelocity);
if (imuBasedLinearStateCalculator.isEstimationEnabled())
{
@@ -424,6 +535,9 @@ else if (numberOfEndEffectorsTrusted.getIntegerValue() > 0)
rootJointPosition.set(kinematicsBasedLinearStateCalculator.getPelvisPosition());
rootJointVelocity.set(kinematicsBasedLinearStateCalculator.getPelvisVelocity());
}
+
+ // FIXME should this used the fused measurement, rather than the kinematic only measurement?
+ correctFeetStateFromPelvis();
}
else
{
@@ -442,8 +556,9 @@ private void updateRootJoint()
imuBasedLinearStateCalculator.saveMeasurementFrameTwist(rootJoint.getJointTwist());
}
- private int setTrustedFeetUsingFootSwitches()
+ private int setTrustedFeetUsingFootSwitchesAndKinematics()
{
+ int numberOfEndEffectorsHitGround = 0;
int numberOfEndEffectorsTrusted = 0;
int windowSize = (int) (delayTimeBeforeTrustingFoot.getValue() / estimatorDT);
@@ -452,19 +567,49 @@ private int setTrustedFeetUsingFootSwitches()
{
RigidBodyBasics foot = feet.get(i);
wereFeetTrustedLastTick.get(foot).set(areFeetTrusted.get(foot).getValue());
- haveFeetHitGroundFiltered.get(foot).setWindowSize(windowSize);
+
+ GlitchFilteredYoBoolean hasFootHitGround = haveFeetHitGroundFiltered.get(foot);
+ GlitchFilteredYoBoolean isFootNotMoving = areFeetNotMovingFiltered.get(foot);
+ hasFootHitGround.setWindowSize(windowSize);
+ isFootNotMoving.setWindowSize(windowSize);
if (footSwitches.get(foot).hasFootHitGroundFiltered())
- haveFeetHitGroundFiltered.get(foot).update(true);
+ hasFootHitGround.update(true);
else
- haveFeetHitGroundFiltered.get(foot).set(false);
+ hasFootHitGround.set(false);
+ if (!footEstimatorMap.get(foot).isFootMoving())
+ isFootNotMoving.update(true);
+ else
+ isFootNotMoving.set(false);
- if (haveFeetHitGroundFiltered.get(foot).getBooleanValue())
+ if (hasFootHitGround.getBooleanValue())
+ numberOfEndEffectorsHitGround++;
+ if (hasFootHitGround.getBooleanValue() && isFootNotMoving.getBooleanValue())
numberOfEndEffectorsTrusted++;
}
- // Update only if at least one foot hit the ground
+ // TODO better incorporate the conditions of nothing moving
+
+ // Update only if at least one foot hit the ground and its not moving
if (numberOfEndEffectorsTrusted > 0)
+ {
+ if (trustOnlyLowestFoot.getValue())
+ {
+ numberOfEndEffectorsTrusted = filterAndTrustLowestFoot();
+ }
+ else
+ {
+ for (int i = 0; i < feet.size(); i++)
+ {
+ RigidBodyBasics foot = feet.get(i);
+ boolean isFootOnGround = haveFeetHitGroundFiltered.get(foot).getBooleanValue();
+ boolean isFootNotMoving = areFeetNotMovingFiltered.get(foot).getBooleanValue();
+ areFeetTrusted.get(foot).set(isFootOnGround && isFootNotMoving);
+ }
+ }
+ }
+ // Update if you have a foot on the ground, but it's moving
+ else if (numberOfEndEffectorsHitGround > 0)
{
if (trustOnlyLowestFoot.getValue())
{
@@ -478,6 +623,8 @@ private int setTrustedFeetUsingFootSwitches()
boolean isFootOnGround = haveFeetHitGroundFiltered.get(foot).getBooleanValue();
areFeetTrusted.get(foot).set(isFootOnGround);
}
+ // override the number of end effectors trusted with the number that hit the ground.
+ numberOfEndEffectorsTrusted = numberOfEndEffectorsHitGround;
}
}
// Else if there is a foot with a force past the threshold trust the force and not the CoP
@@ -506,6 +653,8 @@ private int setTrustedFeetUsingFootSwitches()
}
}
+
+
if (numberOfEndEffectorsTrusted == 0)
{
if (trustImuWhenNoFeetAreInContact.getValue())
@@ -690,7 +839,7 @@ private void computePositionFromMergingMeasurements()
}
}
- private void updateTrustedFeetLists()
+ private void updateTrustedFeet()
{
listOfTrustedFeet.clear();
listOfUnTrustedFeet.clear();
@@ -703,6 +852,32 @@ private void updateTrustedFeetLists()
else
listOfUnTrustedFeet.add(foot);
}
+
+ if (!kinematicsBasedLinearStateCalculator.getKinematicsIsUpToDate())
+ throw new RuntimeException("Leg kinematics needs to be updated before trying to estimate the pelvis position/linear velocity.");
+
+ for (int i = 0; i < listOfTrustedFeet.size(); i++)
+ {
+ SingleFootEstimator footEstimator = footEstimatorMap.get(listOfTrustedFeet.get(i));
+ if (kinematicsBasedLinearStateCalculator.getAssumeTrustedFootAtZeroHeight())
+ {
+ if (kinematicsBasedLinearStateCalculator.getTrustCoPAsNonSlippingContactPoint())
+ {
+ footEstimator.computeStateSettingCoPZ(kinematicsBasedLinearStateCalculator.getTrustCoPAsNonSlippingContactPoint(),
+ kinematicsBasedLinearStateCalculator.getUseControllerDesiredCoP(),
+ 0.0);
+ }
+ else
+ {
+ footEstimator.computeStateSettingFootZ(0.0);
+ }
+ }
+ else
+ {
+ footEstimator.computeState(kinematicsBasedLinearStateCalculator.getTrustCoPAsNonSlippingContactPoint(),
+ kinematicsBasedLinearStateCalculator.getUseControllerDesiredCoP());
+ }
+ }
}
public void getEstimatedPelvisPosition(FramePoint3D pelvisPositionToPack)
@@ -724,8 +899,17 @@ public List getCurrentListOfTrustedFeet()
public YoGraphicDefinition getSCS2YoGraphics()
{
YoGraphicGroupDefinition group = new YoGraphicGroupDefinition(getClass().getSimpleName());
- group.addChild(kinematicsBasedLinearStateCalculator.getSCS2YoGraphics());
group.addChild(imuBasedLinearStateCalculator.getSCS2YoGraphics());
+
+ if (!VISUALIZE_FEET_ESTIMATORS)
+ return group;
+
+ for (SingleFootEstimator footEstimator : footEstimators)
+ {
+ group.addChild(footEstimator.getSCS2YoGraphics());
+ }
+
+
return group;
}
}
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index 8dd07ba743ea..4500d1c461ef 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -6,10 +6,8 @@
import us.ihmc.euclid.referenceFrame.FramePoint3D;
import us.ihmc.euclid.referenceFrame.FrameVector3D;
import us.ihmc.euclid.referenceFrame.ReferenceFrame;
-import us.ihmc.euclid.referenceFrame.interfaces.FixedFramePoint3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FixedFrameVector3DBasics;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DBasics;
-import us.ihmc.euclid.referenceFrame.interfaces.FramePoint2DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FramePoint3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVector3DReadOnly;
import us.ihmc.euclid.referenceFrame.interfaces.FrameVertex2DSupplier;
@@ -42,7 +40,7 @@
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
-class SingleFootEstimator implements SCS2YoGraphicHolder
+public class SingleFootEstimator implements SCS2YoGraphicHolder
{
private final RigidBodyBasics foot;
@@ -175,6 +173,7 @@ public void initialize()
footToRootJointPosition.reset();
copFilteredInFootFrame.reset();
copFilteredInFootFrame.update(0.0, 0.0);
+ copFusedPositionInWorld.setMatchingFrame(copFilteredInFootFrame, 0.0);
copVelocityInWorld.setToZero();
}
@@ -191,11 +190,11 @@ public void initialize()
/**
* Updates the different kinematics related stuff that is used to estimate the pelvis state. This should be called before calling the other objectives.
*/
- public void updateKinematics(TwistReadOnly rootBodyTwist)
+ public void updateStateFromKinematics(TwistReadOnly rootBodyTwist)
{
- computeFootTwistInWorld(rootBodyTwist);
- computeCoPLinearVelocityInWorld(copVelocityInWorld);
+ computeFootTwistInWorldFusingBaseStateAndFootIMU(rootBodyTwist);
+ // Update the vector from the root joint to the foot, based on the kinematics
tempFramePoint.setToZero(rootJointFrame);
tempFramePoint.changeFrame(soleFrame);
@@ -216,42 +215,33 @@ public void updateKinematics(TwistReadOnly rootBodyTwist)
* @param useControllerDesiredCoP
* @param zPosition
*/
- public void updateCoPAndFootSettingZ(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP, double zPosition)
+ public void computeStateSettingCoPZ(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP, double zPosition)
{
- updateCoPPosition(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
+ updateCoPState(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
copFusedPositionInWorld.setZ(zPosition);
- correctFootPositionsUsingCoP(trustCoPAsNonSlippingContactPoint);
- }
+ if (trustCoPAsNonSlippingContactPoint)
+ correctFootPositionUsingCoP();
- public void updateCoPAndFootPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
- {
- updateCoPPosition(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
- correctFootPositionsUsingCoP(trustCoPAsNonSlippingContactPoint);
+ rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
}
- public void setFootZPositionInWorld(double zPosition)
+ public void computeState(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
{
- footPositionInWorld.setZ(zPosition);
- }
+ updateCoPState(trustCoPAsNonSlippingContactPoint, useControllerDesiredCoP);
+ if (trustCoPAsNonSlippingContactPoint)
+ correctFootPositionUsingCoP();
+ rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
+ }
- /**
- * Estimates the pelvis position and linear velocity using the leg kinematics
- *
- * @param trustedFoot is the foot used to estimates the pelvis state
- * @param numberOfTrustedSides is only one or both legs used to estimate the pelvis state
- */
- public void correctPelvisFromKinematics(int numberOfTrustedFeet,
- double alphaVelocityUpdate,
- FixedFramePoint3DBasics rootJointPosition,
- FixedFrameVector3DBasics rootJointLinearVelocity)
+ public void computeStateSettingFootZ(double zPosition)
{
- double scaleFactor = 1.0 / numberOfTrustedFeet;
+ footPositionInWorld.setZ(zPosition);
+ // Update the CoP position from this foot position
+ copIKPositionInWorld.set(footPositionInWorld);
+ copFusedPositionInWorld.set(footPositionInWorld);
rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
- rootJointPosition.scaleAdd(scaleFactor, rootJointPositionPerFoot, rootJointPosition);
-
- rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaVelocityUpdate, copVelocityInWorld, rootJointLinearVelocity);
}
/**
@@ -269,25 +259,30 @@ public void correctPelvisFromKinematics(int numberOfTrustedFeet,
*/
public void correctFootPositionFromRootJoint(boolean isTrusted, boolean trustCoPAsNonSlippingContactPoint, FramePoint3DReadOnly rootJointPosition)
{
+ // Store the old, uncorrected foot position.
tempPoint.set(footPositionInWorld);
+ // Update the foot position
footPositionInWorld.sub(rootJointPosition, footToRootJointPosition);
if (!isTrusted)
{
+ // If the foot isn't trusted, the CoP position isn't relevant, and we can set it to zero in the foot frame.
copFusedPositionInWorld.set(footPositionInWorld);
copRawInFootFrame.setToZero();
copFilteredInFootFrame.setToZero();
}
else if (trustCoPAsNonSlippingContactPoint)
{
+ // If the foot is trusted, but we allow the CoP to move, we need to shift the CoP position to account for this drift. That means computing the drift
+ // delta correction, and then adding it to the position.
tempFrameVector.sub(footPositionInWorld, tempPoint); // Delta from previous to new foot position
copFusedPositionInWorld.add(tempFrameVector); // New CoP position
}
}
- public FrameVector3DReadOnly getFootToRootJointPosition()
+ public FramePoint3DReadOnly getRootJointPositionFromKinematics()
{
- return footToRootJointPosition;
+ return rootJointPositionPerFoot;
}
public FrameVector3DReadOnly getCopVelocityInWorld()
@@ -313,7 +308,7 @@ public boolean isFootMoving()
private final FramePoint2D tempCoP2d = new FramePoint2D();
private final FrameVector3D tempCoPOffset = new FrameVector3D();
- private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
+ private void updateCoPState(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
{
if (trustCoPAsNonSlippingContactPoint)
{
@@ -379,25 +374,28 @@ private void updateCoPPosition(boolean trustCoPAsNonSlippingContactPoint, boolea
tempCoP2d.setToZero(soleFrame);
// Update the CoP value to match the center of the foot.
copIKPositionInWorld.setFromReferenceFrame(soleFrame);
+ copFusedPositionInWorld.setFromReferenceFrame(soleFrame);
}
+ // If we have an IMU and are using it, we should update the fused value.
if (useIMUData.getValue() && footIMU != null)
{
// Integrate the fused position using the velocity, which is from the kinematics and the IMU.
computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, tempFrameVector);
copIMUPositionInWorld.scaleAdd(footAlphaLeakIMUOnly.getValue() * estimatorDT, tempFrameVector, copFusedPositionInWorld);
- // Fuse the kinematic data with the IMU data
+ // Fuse the kinematic data with the IMU data. A higher break frequency equals a lower alpha value, trusting the kinematics more. If break frequency is
+ // zero, alpha equals one, and we trust the IMU completely.
double alpha = AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(imuAgainstKinematicsForPositionBreakFrequency.getValue(), estimatorDT);
copFusedPositionInWorld.interpolate(copIKPositionInWorld, copIMUPositionInWorld, alpha);
}
+
+ // Update the velocity of the foot at the CoP position
+ computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, copVelocityInWorld);
}
- private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPoint)
+ private void correctFootPositionUsingCoP()
{
- if (!trustCoPAsNonSlippingContactPoint)
- return;
-
// Compute where the CoP position is in the world, according to the kinematics
tempCoPOffset.setIncludingFrame(copFilteredInFootFrame, 0.0);
tempCoPOffset.changeFrame(worldFrame);
@@ -408,7 +406,7 @@ private void correctFootPositionsUsingCoP(boolean trustCoPAsNonSlippingContactPo
private final FrameVector3D linearAcceleration = new FrameVector3D();
private final FrameVector3D angularVelocity = new FrameVector3D();
- private void computeFootTwistInWorld(TwistReadOnly rootBodyTwist)
+ private void computeFootTwistInWorldFusingBaseStateAndFootIMU(TwistReadOnly rootBodyTwist)
{
tempRootBodyTwist.setIncludingFrame(rootBodyTwist);
tempRootBodyTwist.setBaseFrame(worldFrame);
@@ -478,11 +476,6 @@ private void computeFootTwistInWorld(TwistReadOnly rootBodyTwist)
}
}
- private void computeCoPLinearVelocityInWorld(FixedFrameVector3DBasics footLinearVelocityToPack)
- {
- computeLinearVelocityAtPointInWorld(copFusedPositionInWorld, footLinearVelocityToPack);
- }
-
private void computeLinearVelocityAtPointInWorld(FramePoint3DReadOnly point, FixedFrameVector3DBasics footLinearVelocityToPack)
{
footTwistInWorld.getLinearPart().set(footFusedLinearVelocityInWorld);
From a6d21f259f774e03eefac8bd7758958f61db1b1c Mon Sep 17 00:00:00 2001
From: Robert
Date: Tue, 16 Sep 2025 17:26:19 -0500
Subject: [PATCH 17/18] did some cleanup
---
.../PelvisLinearStateUpdater.java | 6 ++++++
.../SingleFootEstimator.java | 17 +++++++++++++++--
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
index 055f1a34a340..5c2c9821db96 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisLinearStateUpdater.java
@@ -189,12 +189,16 @@ public PelvisLinearStateUpdater(FullInverseDynamicsStructure inverseDynamicsStru
stateEstimatorParameters.getKinematicsPelvisPositionFilterFreqInHertz());
DoubleProvider copFilterBreakFrequency = new DoubleParameter("CopFilterBreakFrequency", registry, stateEstimatorParameters.getCoPFilterFreqInHertz());
YoBoolean useFootIMUData = new YoBoolean("useFootIMUData", registry);
+ YoDouble footLinearVelocityMovingThreshold = new YoDouble("footLinearVelocityMovingThreshold", registry);
+ YoDouble footAngularVelocityMovingThreshold = new YoDouble("footAngularVelocityMovingThreshold", registry);
YoDouble footAlphaLeakIMUOnly = new YoDouble("footIMUOnlyAlphaLeak", registry);
YoDouble footImuAgainstKinematicsForVelocityBreakFrequency = new YoDouble("footIMUAgainstKinematicsForVelocityBreakFrequency", registry);
YoDouble footImuAgainstKinematicsForPositionBreakFrequency = new YoDouble("footIMUAgainstKinematicsForPositionBreakFrequency", registry);
footImuAgainstKinematicsForPositionBreakFrequency.set(12.0);
footImuAgainstKinematicsForVelocityBreakFrequency.set(2.0);
footAlphaLeakIMUOnly.set(0.999);
+ footLinearVelocityMovingThreshold.set(0.3);
+ footAngularVelocityMovingThreshold.set(0.6);
RigidBodyBasics[] feetRigidBodies = feetContactablePlaneBodies.keySet().toArray(new RigidBodyBasics[0]);
@@ -213,6 +217,8 @@ public PelvisLinearStateUpdater(FullInverseDynamicsStructure inverseDynamicsStru
copFilterBreakFrequency,
centerOfPressureDataHolderFromController,
cancelGravityFromAccelerationMeasurement,
+ footLinearVelocityMovingThreshold,
+ footAngularVelocityMovingThreshold,
gravityVector,
useFootIMUData,
footImuAgainstKinematicsForPositionBreakFrequency,
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index 4500d1c461ef..4b70937e0978 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -39,6 +39,7 @@
import us.ihmc.yoVariables.providers.DoubleProvider;
import us.ihmc.yoVariables.registry.YoRegistry;
import us.ihmc.yoVariables.variable.YoBoolean;
+import us.ihmc.yoVariables.variable.YoDouble;
public class SingleFootEstimator implements SCS2YoGraphicHolder
{
@@ -56,6 +57,8 @@ public class SingleFootEstimator implements SCS2YoGraphicHolder
private final YoFrameVector3D footFusedLinearVelocityInWorld;
private final YoFrameVector3D footFusedAngularVelocityInWorld;
+ private final YoDouble footLinearVelocity;
+ private final YoDouble footAngularVelocity;
private final YoBoolean footIsMoving;
private final YoFrameVector3D copVelocityInWorld;
@@ -81,6 +84,8 @@ public class SingleFootEstimator implements SCS2YoGraphicHolder
private final DoubleProvider imuAgainstKinematicsForVelocityBreakFrequency;
private final DoubleProvider imuAgainstKinematicsForPositionBreakFrequency;
private final BooleanProvider cancelGravityFromAccelerationMeasurement;
+ private final DoubleProvider footLinearVelocityMovingThreshold;
+ private final DoubleProvider footAngularVelocityMovingThreshold;
private final FrameVector3DReadOnly gravityVector;
private final double estimatorDT;
@@ -95,6 +100,8 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
DoubleProvider copFilterBreakFrequency,
CenterOfPressureDataHolder centerOfPressureDataHolderFromController,
BooleanProvider cancelGravityFromAccelerationMeasurement,
+ DoubleProvider footLinearVelocityMovingThreshold,
+ DoubleProvider footAngularVelocityMovingThreshold,
FrameVector3DReadOnly gravityVector,
BooleanProvider useIMUData,
DoubleProvider imuAgainstKinematicsForPositionBreakFrequency,
@@ -109,6 +116,8 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
this.imuBiasProvider = imuBiasProvider;
this.centerOfPressureDataHolderFromController = centerOfPressureDataHolderFromController;
this.cancelGravityFromAccelerationMeasurement = cancelGravityFromAccelerationMeasurement;
+ this.footLinearVelocityMovingThreshold = footLinearVelocityMovingThreshold;
+ this.footAngularVelocityMovingThreshold = footAngularVelocityMovingThreshold;
this.gravityVector = gravityVector;
this.useIMUData = useIMUData;
this.imuAgainstKinematicsForPositionBreakFrequency = imuAgainstKinematicsForPositionBreakFrequency;
@@ -129,6 +138,8 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
copRawInFootFrame = new YoFramePoint2D(namePrefix + "CoPRawInFootFrame", soleFrame, registry);
previousCoPInFootFrame = new YoFramePoint2D(namePrefix + "PreviousCoPInFootFrame", soleFrame, registry);
+ footLinearVelocity = new YoDouble(namePrefix + "LinearVelocity", registry);
+ footAngularVelocity = new YoDouble(namePrefix + "AngularVelocity", registry);
footIsMoving = new YoBoolean(namePrefix + "IsMoving", registry);
footIKLinearVelocityInWorld = new YoFrameVector3D(namePrefix + "IKLinearVelocityInWorld", worldFrame, registry);
@@ -203,9 +214,11 @@ public void updateStateFromKinematics(TwistReadOnly rootBodyTwist)
footToRootJointPosition.update(tempFrameVector);
- // FIXME remove magic parameters
+ footLinearVelocity.set(getFootLinearVelocityInWorld().norm());
+ footAngularVelocity.set(getFootAngularVelocityInWorld().norm());
+
if (useIMUData.getValue() && footIMU != null)
- footIsMoving.set(getFootLinearVelocityInWorld().norm() > 0.5 || getFootAngularVelocityInWorld().norm() > 1.0);
+ footIsMoving.set(footLinearVelocity.getDoubleValue() > footLinearVelocityMovingThreshold.getValue() || footAngularVelocity.getDoubleValue() > footAngularVelocityMovingThreshold.getValue());
else
footIsMoving.set(false);
}
From bd9395c1ad3aecb0e571e624cc3d177c4f01771c Mon Sep 17 00:00:00 2001
From: Robert
Date: Tue, 16 Sep 2025 17:54:27 -0500
Subject: [PATCH 18/18] started improving the update to the foot state for the
kinematic pelvis linear velocity
---
...isKinematicsBasedLinearStateCalculator.java | 13 ++++++++++---
.../SingleFootEstimator.java | 18 ++++++++++++++++++
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
index 1c74f54ef57f..7b023c24ec7a 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/PelvisKinematicsBasedLinearStateCalculator.java
@@ -45,6 +45,7 @@ public class PelvisKinematicsBasedLinearStateCalculator
private final YoFramePoint3D rootJointPosition = new YoFramePoint3D("estimatedRootJointPositionKinematics", worldFrame, registry);
private final YoFrameVector3D rootJointLinearVelocity = new YoFrameVector3D("estimatedRootJointLinearVelocityKinematics", worldFrame, registry);
+ private final YoFrameVector3D averagedTrustedFootVelocity = new YoFrameVector3D("averagedTrustedFootVelocity", worldFrame, registry);
private final DoubleProvider alphaRootJointLinearVelocity;
/** Debug variable */
@@ -180,17 +181,23 @@ public void estimatePelvisLinearState(List trustedFeet, FramePo
}
else
{
+ averagedTrustedFootVelocity.setToZero();
for (int i = 0; i < trustedFeet.size(); i++)
{
SingleFootEstimator footEstimator = footEstimatorMap.get(trustedFeet.get(i));
double scaleFactor = 1.0 / trustedFeet.size();
+ // The root joint position is zeroed when we call #updateKinematics
rootJointPosition.scaleAdd(scaleFactor, footEstimator.getRootJointPositionFromKinematics(), rootJointPosition);
- rootJointLinearVelocity.scaleAdd(-scaleFactor * alphaRootJointLinearVelocity.getValue(),
- footEstimator.getCopVelocityInWorld(),
- rootJointLinearVelocity);
+ averagedTrustedFootVelocity.scaleAdd(scaleFactor, footEstimator.getCopVelocityInWorld(), averagedTrustedFootVelocity);
+
}
+
+ // We want the average velocity of the feet to trend to zero, since the trusted feet are assumed to not be moving.
+ rootJointLinearVelocity.scaleAdd(-alphaRootJointLinearVelocity.getValue(),
+ averagedTrustedFootVelocity,
+ rootJointLinearVelocity);
}
rootJointLinearVelocityFDDebug.update();
diff --git a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
index 4b70937e0978..5d149fcd4f6e 100644
--- a/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
+++ b/ihmc-state-estimation/src/main/java/us/ihmc/stateEstimation/humanoid/kinematicsBasedStateEstimation/SingleFootEstimator.java
@@ -63,9 +63,11 @@ public class SingleFootEstimator implements SCS2YoGraphicHolder
private final YoFrameVector3D copVelocityInWorld;
private final AlphaFilteredYoFrameVector3D footToRootJointPosition;
+ private final AlphaFilteredYoFrameVector3D footToRootJointVelocity;
private final YoFramePoint3D footPositionInWorld;
/** Debug variable */
private final YoFramePoint3D rootJointPositionPerFoot;
+ private final YoFrameVector3D rootJointVelocityPerFoot;
private final YoFramePoint3D copIKPositionInWorld;
private final YoFramePoint3D copIMUPositionInWorld;
@@ -131,7 +133,9 @@ public SingleFootEstimator(FloatingJointBasics rootJoint,
DoubleProvider alphaFoot = () -> AlphaFilterTools.computeAlphaGivenBreakFrequencyProperly(footToRootJointPositionBreakFrequency.getValue(), estimatorDT);
footToRootJointPosition = new AlphaFilteredYoFrameVector3D(namePrefix + "FootToRootJointPosition", "", registry, alphaFoot, worldFrame);
+ footToRootJointVelocity = new AlphaFilteredYoFrameVector3D(namePrefix + "FootToRootJointVelocity", "", registry, alphaFoot, worldFrame);
rootJointPositionPerFoot = new YoFramePoint3D(namePrefix + "BasedRootJointPosition", worldFrame, registry);
+ rootJointVelocityPerFoot = new YoFrameVector3D(namePrefix + "BasedRootJointVelocity", worldFrame, registry);
footPositionInWorld = new YoFramePoint3D(namePrefix + "FootPositionInWorld", worldFrame, registry);
footPolygon = new FrameConvexPolygon2D(FrameVertex2DSupplier.asFrameVertex2DSupplier(contactableFoot.getContactPoints2D()));
footCenterCoPLineSegment = new FrameLineSegment2D(soleFrame);
@@ -182,6 +186,7 @@ public YoGraphicDefinition getSCS2YoGraphics()
public void initialize()
{
footToRootJointPosition.reset();
+ footToRootJointVelocity.reset();
copFilteredInFootFrame.reset();
copFilteredInFootFrame.update(0.0, 0.0);
copFusedPositionInWorld.setMatchingFrame(copFilteredInFootFrame, 0.0);
@@ -236,6 +241,7 @@ public void computeStateSettingCoPZ(boolean trustCoPAsNonSlippingContactPoint, b
correctFootPositionUsingCoP();
rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
+ rootJointVelocityPerFoot.sub(footFusedLinearVelocityInWorld, footToRootJointVelocity);
}
public void computeState(boolean trustCoPAsNonSlippingContactPoint, boolean useControllerDesiredCoP)
@@ -245,6 +251,7 @@ public void computeState(boolean trustCoPAsNonSlippingContactPoint, boolean useC
correctFootPositionUsingCoP();
rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
+ rootJointVelocityPerFoot.sub(footFusedLinearVelocityInWorld, footToRootJointVelocity);
}
public void computeStateSettingFootZ(double zPosition)
@@ -255,6 +262,7 @@ public void computeStateSettingFootZ(double zPosition)
copFusedPositionInWorld.set(footPositionInWorld);
rootJointPositionPerFoot.add(footPositionInWorld, footToRootJointPosition);
+ rootJointVelocityPerFoot.sub(footFusedLinearVelocityInWorld, footToRootJointVelocity);
}
/**
@@ -298,6 +306,11 @@ public FramePoint3DReadOnly getRootJointPositionFromKinematics()
return rootJointPositionPerFoot;
}
+ public FrameVector3DReadOnly getRootJointVelocityFromKinematics()
+ {
+ return rootJointVelocityPerFoot;
+ }
+
public FrameVector3DReadOnly getCopVelocityInWorld()
{
return copVelocityInWorld;
@@ -426,6 +439,11 @@ private void computeFootTwistInWorldFusingBaseStateAndFootIMU(TwistReadOnly root
tempRootBodyTwist.changeFrame(foot.getBodyFixedFrame());
foot.getBodyFixedFrame().getTwistRelativeToOther(rootJointFrame, footTwistInWorld);
+
+ tempFrameVector.setIncludingFrame(footTwistInWorld.getLinearPart());
+ tempFrameVector.changeFrame(worldFrame);
+ footToRootJointVelocity.update(tempFrameVector);
+
footTwistInWorld.add(tempRootBodyTwist);
footTwistInWorld.setBodyFrame(soleFrame);
footTwistInWorld.changeFrame(worldFrame);