blob: 0ceae21c62a6e4636ed886e815957ab5a214c214 [file] [log] [blame]
#ifndef ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
#define ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
#include <atomic>
#include <cstdlib>
#include <mutex>
#include <private/dvr/types.h>
namespace android {
namespace dvr {
using Matrix3d = Eigen::Matrix<double, 3, 3>;
using Rotationd = quatd;
using Vector3d = vec3d;
using AngleAxisd = Eigen::AngleAxisd;
// Ported from GVR's pose_state.h.
// Stores a 3dof pose plus derivatives. This can be used for prediction.
struct PoseState {
// Time in nanoseconds for the current pose.
uint64_t timestamp_ns;
// Rotation from Sensor Space to Start Space.
Rotationd sensor_from_start_rotation;
// First derivative of the rotation.
// TODO(pfg): currently storing gyro data, switch to first derivative instead.
Vector3d sensor_from_start_rotation_velocity;
};
// Sensor fusion class that implements an Extended Kalman Filter (EKF) to
// estimate a 3D rotation from a gyroscope and and accelerometer.
// This system only has one state, the pose. It does not estimate any velocity
// or acceleration.
//
// To learn more about Kalman filtering one can read this article which is a
// good introduction: http://en.wikipedia.org/wiki/Kalman_filter
//
// Start Space is :
// z is up.
// y is forward based on the first sensor data.
// x = y \times z
// Sensor Space follows the android specification {@link
// http://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords}
// See http://go/vr-coords for definitions of Start Space and Sensor Space.
//
// This is a port from GVR's SensorFusion code (See
// https://cs/vr/gvr/sensors/sensor_fusion.h)
// which in turn is a port from java of OrientationEKF (See
// https://cs/java/com/google/vr/cardboard/vrtoolkit/vrtoolkit/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/OrientationEKF.java)
class SensorFusion {
public:
SensorFusion();
SensorFusion(const SensorFusion&) = delete;
void operator=(const SensorFusion&) = delete;
// Resets the state of the sensor fusion. It sets the velocity for
// prediction to zero. The reset will happen with the next
// accelerometer sample. Gyroscope sample will be discarded until a new
// accelerometer sample arrives.
void Reset();
// Gets the PoseState representing the latest pose and derivatives at a
// particular timestamp as estimated by SensorFusion.
PoseState GetLatestPoseState() const;
// Processes one gyroscope sample event. This updates the pose of the system
// and the prediction model. The gyroscope data is assumed to be in axis angle
// form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T.
//
// @param v_x velocity in x.
// @param v_y velocity in y.
// @param v_z velocity in z.
// @param timestamp_ns gyroscope event timestamp in nanosecond.
void ProcessGyroscopeSample(float v_x, float v_y, float v_z,
uint64_t timestamp_ns);
// Processes one accelerometer sample event. This updates the pose of the
// system. If the Accelerometer norm changes too much between sample it is not
// trusted as much.
//
// @param acc_x accelerometer data in x.
// @param acc_y accelerometer data in y.
// @param acc_z accelerometer data in z.
// @param timestamp_ns accelerometer event timestamp in nanosecond.
void ProcessAccelerometerSample(float acc_x, float acc_y, float acc_z,
uint64_t timestamp_ns);
private:
// Estimates the average timestep between gyroscope event.
void FilterGyroscopeTimestep(double gyroscope_timestep);
// Updates the state covariance with an incremental motion. It changes the
// space of the quadric.
void UpdateStateCovariance(const Matrix3d& motion_update);
// Computes the innovation vector of the Kalman based on the input pose.
// It uses the latest measurement vector (i.e. accelerometer data), which must
// be set prior to calling this function.
Vector3d ComputeInnovation(const Rotationd& pose);
// This computes the measurement_jacobian_ via numerical differentiation based
// on the current value of sensor_from_start_rotation_.
void ComputeMeasurementJacobian();
// Updates the accelerometer covariance matrix.
//
// This looks at the norm of recent accelerometer readings. If it has changed
// significantly, it means the phone receives additional acceleration than
// just gravity, and so the down vector information gravity signal is noisier.
//
// TODO(dcoz,pfg): this function is very simple, we probably need something
// more elaborated here once we have proper regression testing.
void UpdateMeasurementCovariance();
// Reset all internal states. This is not thread safe. Lock should be acquired
// outside of it. This function is called in ProcessAccelerometerSample.
void ResetState();
// Current transformation from Sensor Space to Start Space.
// x_sensor = sensor_from_start_rotation_ * x_start;
PoseState current_state_;
// Filtering of the gyroscope timestep started?
bool is_timestep_filter_initialized_;
// Filtered gyroscope timestep valid?
bool is_gyroscope_filter_valid_;
// Sensor fusion currently aligned with gravity? After initialization
// it will requires a couple of accelerometer data for the system to get
// aligned.
bool is_aligned_with_gravity_;
// Covariance of Kalman filter state (P in common formulation).
Matrix3d state_covariance_;
// Covariance of the process noise (Q in common formulation).
Matrix3d process_covariance_;
// Covariance of the accelerometer measurement (R in common formulation).
Matrix3d accelerometer_measurement_covariance_;
// Covariance of innovation (S in common formulation).
Matrix3d innovation_covariance_;
// Jacobian of the measurements (H in common formulation).
Matrix3d accelerometer_measurement_jacobian_;
// Gain of the Kalman filter (K in common formulation).
Matrix3d kalman_gain_;
// Parameter update a.k.a. innovation vector. (\nu in common formulation).
Vector3d innovation_;
// Measurement vector (z in common formulation).
Vector3d accelerometer_measurement_;
// Current prediction vector (g in common formulation).
Vector3d prediction_;
// Control input, currently this is only the gyroscope data (\mu in common
// formulation).
Vector3d control_input_;
// Update of the state vector. (x in common formulation).
Vector3d state_update_;
// Time of the last accelerometer processed event.
uint64_t current_accelerometer_timestamp_ns_;
// Estimates of the timestep between gyroscope event in seconds.
double filtered_gyroscope_timestep_s_;
// Number of timestep samples processed so far by the filter.
uint32_t num_gyroscope_timestep_samples_;
// Norm of the accelerometer for the previous measurement.
double previous_accelerometer_norm_;
// Moving average of the accelerometer norm changes. It is computed for every
// sensor datum.
double moving_average_accelerometer_norm_change_;
// Flag indicating if a state reset should be executed with the next
// accelerometer sample.
std::atomic<bool> execute_reset_with_next_accelerometer_sample_;
mutable std::mutex mutex_;
};
} // namespace dvr
} // namespace android
#endif // ANDROID_DVR_SENSORD_SENSOR_FUSION_H_