aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/OVR_SensorFusion.h
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src/OVR_SensorFusion.h')
-rw-r--r--LibOVR/Src/OVR_SensorFusion.h582
1 files changed, 582 insertions, 0 deletions
diff --git a/LibOVR/Src/OVR_SensorFusion.h b/LibOVR/Src/OVR_SensorFusion.h
new file mode 100644
index 0000000..2a17920
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorFusion.h
@@ -0,0 +1,582 @@
+/************************************************************************************
+
+PublicHeader: OVR.h
+Filename : OVR_SensorFusion.h
+Content : Methods that determine head orientation from sensor data over time
+Created : October 9, 2012
+Authors : Michael Antonov, Steve LaValle, Dov Katz, Max Katsev, Dan Gierl
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#ifndef OVR_SensorFusion_h
+#define OVR_SensorFusion_h
+
+#include "OVR_Device.h"
+#include "OVR_SensorFilter.h"
+#include "Kernel/OVR_Timer.h"
+#include "Kernel/OVR_Threads.h"
+#include "Kernel/OVR_Lockless.h"
+
+// CAPI forward declarations.
+typedef struct ovrPoseStatef_ ovrPoseStatef;
+typedef struct ovrSensorState_ ovrSensorState;
+
+namespace OVR {
+
+struct HmdRenderInfo;
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor State
+
+// These values are reported as compatible with C API.
+
+
+// PoseState describes the complete pose, or a rigid body configuration, at a
+// point in time, including first and second derivatives. It is used to specify
+// instantaneous location and movement of the headset.
+// SensorState is returned as a part of the sensor state.
+
+template<class T>
+class PoseState
+{
+public:
+ typedef typename CompatibleTypes<Transform<T> >::Type CompatibleType;
+
+ PoseState() : TimeInSeconds(0.0) { }
+ // float <-> double conversion constructor.
+ explicit PoseState(const PoseState<typename Math<T>::OtherFloatType> &src)
+ : Pose(src.Pose),
+ AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity),
+ AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration),
+ TimeInSeconds(src.TimeInSeconds)
+ { }
+
+ // C-interop support: PoseStatef <-> ovrPoseStatef
+ PoseState(const typename CompatibleTypes<PoseState<T> >::Type& src)
+ : Pose(src.Pose),
+ AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity),
+ AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration),
+ TimeInSeconds(src.TimeInSeconds)
+ { }
+
+ operator typename CompatibleTypes<PoseState<T> >::Type () const
+ {
+ typename CompatibleTypes<PoseState<T> >::Type result;
+ result.Pose = Pose;
+ result.AngularVelocity = AngularVelocity;
+ result.LinearVelocity = LinearVelocity;
+ result.AngularAcceleration = AngularAcceleration;
+ result.LinearAcceleration = LinearAcceleration;
+ result.TimeInSeconds = TimeInSeconds;
+ return result;
+ }
+
+
+ Transform<T> Pose;
+ Vector3<T> AngularVelocity;
+ Vector3<T> LinearVelocity;
+ Vector3<T> AngularAcceleration;
+ Vector3<T> LinearAcceleration;
+ // Absolute time of this state sample; always a double measured in seconds.
+ double TimeInSeconds;
+
+
+ // ***** Helpers for Pose integration
+
+ // Stores and integrates gyro angular velocity reading for a given time step.
+ void StoreAndIntegrateGyro(Vector3d angVel, double dt);
+ // Stores and integrates position/velocity from accelerometer reading for a given time step.
+ void StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt);
+
+ // Performs integration of state by adding next state delta to it
+ // to produce a combined state change
+ void AdvanceByDelta(const PoseState<T>& delta);
+};
+
+
+
+// External API returns pose as float, but uses doubles internally for quaternion precision.
+typedef PoseState<float> PoseStatef;
+typedef PoseState<double> PoseStated;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor State
+
+
+// Bit flags describing the current status of sensor tracking.
+enum StatusBits
+{
+ Status_OrientationTracked = 0x0001, // Orientation is currently tracked (connected and in use).
+ Status_PositionTracked = 0x0002, // Position is currently tracked (false if out of range).
+ Status_PositionConnected = 0x0020, // Position tracking HW is conceded.
+ // Status_HMDConnected = 0x0080 // HMD Display is available & connected.
+};
+
+
+// Full state of of the sensor reported by GetSensorState() at a given absolute time.
+class SensorState
+{
+public:
+ SensorState() : Temperature(0), StatusFlags(0) { }
+
+ // C-interop support
+ SensorState(const ovrSensorState& s);
+ operator ovrSensorState () const;
+
+ // Pose state at the time that SensorState was requested.
+ PoseStatef Predicted;
+ // Actual recorded pose configuration based on sensor sample at a
+ // moment closest to the requested time.
+ PoseStatef Recorded;
+
+ // Calibrated magnetometer reading, in Gauss, at sample time.
+ Vector3f Magnetometer;
+ // Sensor temperature reading, in degrees Celsius, at sample time.
+ float Temperature;
+ // Sensor status described by ovrStatusBits.
+ unsigned int StatusFlags;
+};
+
+
+
+//-------------------------------------------------------------------------------------
+
+class VisionHandler
+{
+public:
+ virtual void OnVisionSuccess(const Transform<double>& cameraFromImu, UInt32 exposureCounter) = 0;
+ virtual void OnVisionPreviousFrame(const Transform<double>& cameraFromImu) = 0;
+ virtual void OnVisionFailure() = 0;
+
+ // Get a configuration that represents the change over a short time interval
+ virtual Transform<double> GetVisionPrediction(UInt32 exposureCounter) = 0;
+};
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion
+
+// SensorFusion class accumulates Sensor notification messages to keep track of
+// orientation, which involves integrating the gyro and doing correction with gravity.
+// Magnetometer based yaw drift correction is also supported; it is usually enabled
+// automatically based on loaded magnetometer configuration.
+// Orientation is reported as a quaternion, from which users can obtain either the
+// rotation matrix or Euler angles.
+//
+// The class can operate in two ways:
+// - By user manually passing MessageBodyFrame messages to the OnMessage() function.
+// - By attaching SensorFusion to a SensorDevice, in which case it will
+// automatically handle notifications from that device.
+
+
+class SensorFusion : public NewOverrideBase, public VisionHandler
+{
+ friend class SensorFusionDebug;
+
+ enum
+ {
+ MagMaxReferences = 1000
+ };
+
+public:
+
+ // -------------------------------------------------------------------------------
+ // Critical components for tiny API
+
+ SensorFusion(SensorDevice* sensor = 0);
+ ~SensorFusion();
+
+ // Attaches this SensorFusion to the IMU sensor device, from which it will receive
+ // notification messages. If a sensor is attached, manual message notification
+ // is not necessary. Calling this function also resets SensorFusion state.
+ bool AttachToSensor(SensorDevice* sensor);
+
+ // Returns true if this Sensor fusion object is attached to the IMU.
+ bool IsAttachedToSensor() const;
+
+ // Sets up head-and-neck model and device-to-pupil dimensions from the user's profile and the HMD stats.
+ // This copes elegantly if profile is NULL.
+ void SetUserHeadDimensions(Profile const &profile, HmdRenderInfo const &hmdRenderInfo);
+
+ // Get the predicted pose (orientation, position) of the center pupil frame (CPF) at a specific point in time.
+ Transformf GetPoseAtTime(double absoluteTime) const;
+
+ // Get the full dynamical system state of the CPF, which includes velocities and accelerations,
+ // predicted at a specified absolute point in time.
+ SensorState GetSensorStateAtTime(double absoluteTime) const;
+
+ // Get the sensor status (same as GetSensorStateAtTime(...).Status)
+ unsigned int GetStatus() const;
+
+ // End tiny API components
+ // -------------------------------------------------------------------------------
+
+ // Resets the current orientation.
+ void Reset ();
+
+ // Configuration
+ void EnableMotionTracking(bool enable = true) { MotionTrackingEnabled = enable; }
+ bool IsMotionTrackingEnabled() const { return MotionTrackingEnabled; }
+
+ // Accelerometer/Gravity Correction Control
+ // Enables/disables gravity correction (on by default).
+ void SetGravityEnabled (bool enableGravity);
+ bool IsGravityEnabled () const;
+
+ // Vision Position and Orientation Configuration
+ // -----------------------------------------------
+ bool IsVisionPositionEnabled () const;
+ void SetVisionPositionEnabled (bool enableVisionPosition);
+
+ // compensates for a tilted camera
+ void SetCameraTiltCorrectionEnabled(bool enable);
+ bool IsCameraTiltCorrectionEnabled () const;
+
+ // Message Handling Logic
+ // -----------------------------------------------
+ // Notifies SensorFusion object about a new BodyFrame
+ // message from a sensor.
+ // Should be called by user if not attached to sensor.
+ void OnMessage (const MessageBodyFrame& msg);
+
+
+ // Interaction with vision
+ // -----------------------------------------------
+ // Handle observation from vision system (orientation, position, time)
+ virtual void OnVisionSuccess(const Transform<double>& cameraFromImu, UInt32 exposureCounter);
+
+ virtual void OnVisionPreviousFrame(const Transform<double>& cameraFromImu);
+ virtual void OnVisionFailure();
+ // Get a configuration that represents the change over a short time interval
+ virtual Transform<double> GetVisionPrediction(UInt32 exposureCounter);
+
+ double GetTime () const;
+ double GetVisionLatency () const;
+
+
+ // Detailed head dimension control
+ // -----------------------------------------------
+ // These are now deprecated in favour of SetUserHeadDimensions()
+ Vector3f GetHeadModel() const;
+ void SetHeadModel(const Vector3f &headModel, bool resetNeckPivot = true );
+ float GetCenterPupilDepth() const;
+ void SetCenterPupilDepth(float centerPupilDepth);
+
+
+ // Magnetometer and Yaw Drift Section:
+ // ---------------------------------------
+
+ // Enables/disables magnetometer based yaw drift correction.
+ // Must also have mag calibration data for this correction to work.
+ void SetYawCorrectionEnabled(bool enable);
+ // Determines if yaw correction is enabled.
+ bool IsYawCorrectionEnabled () const;
+
+ // Clear the reference points associating
+ // mag readings with orientations
+ void ClearMagReferences ();
+
+ // Sets the focus filter direction to the current HMD direction
+ void SetFocusDirection();
+ // Sets the focus filter to a direction in the body frame. Once set, a complementary filter
+ // will very slowly drag the world to keep the direction of the HMD within the FOV of the focus
+ void SetFocusDirection(Vector3d direction);
+ // Sets the FOV (in radians) of the focus. When the yaw difference between the HMD's current pose
+ // and the focus is smaller than the FOV, the complementary filter does not act.
+ void SetFocusFOV(double rads);
+ // Turns off the focus filter (equivalent to setting the focus to 0
+ void ClearFocus();
+
+private:
+
+ // -----------------------------------------------
+
+ class BodyFrameHandler : public NewOverrideBase, public MessageHandler
+ {
+ SensorFusion* pFusion;
+ public:
+ BodyFrameHandler(SensorFusion* fusion)
+ : pFusion(fusion) {}
+
+ ~BodyFrameHandler();
+
+ virtual void OnMessage(const Message& msg);
+ virtual bool SupportsMessageType(MessageType type) const;
+ };
+
+
+ // -----------------------------------------------
+
+ // State version stored in lockless updater "queue" and used for
+ // prediction by GetPoseAtTime/GetSensorStateAtTime
+ struct LocklessState
+ {
+ PoseState<double> State;
+ float Temperature;
+ Vector3d Magnetometer;
+ unsigned int StatusFlags;
+
+ LocklessState() : Temperature(0.0), StatusFlags(0) { };
+ };
+
+
+ // -----------------------------------------------
+
+ // Entry describing the state of the headset at the time of an exposure as reported by the DK2 board.
+ // This is used in combination with the vision data for
+ // incremental tracking based on IMU change and for drift correction
+ struct ExposureRecord
+ {
+ UInt32 ExposureCounter;
+ double ExposureTime;
+ // State of the headset at the time of exposure
+ PoseState<double> WorldFromImu;
+ // Change in state since the last exposure based on IMU data only
+ PoseState<double> ImuOnlyDelta;
+ // Did we have tracking for the entire interval between exposures
+ bool VisionTrackingAvailable;
+
+ ExposureRecord() : ExposureCounter(0), ExposureTime(0.0), VisionTrackingAvailable(true) { }
+ ExposureRecord(UInt32 exposureCounter, double exposureTime,
+ const PoseState<double>& worldFromImu, const PoseState<double>& imuOnlyDelta)
+ : ExposureCounter(exposureCounter), ExposureTime(exposureTime),
+ WorldFromImu(worldFromImu), ImuOnlyDelta(imuOnlyDelta), VisionTrackingAvailable(true) { }
+ };
+
+ // -----------------------------------------------
+
+ // Entry describing the magnetometer reference point
+ // Used for mag yaw correction
+ struct MagReferencePoint
+ {
+ Vector3d InImuFrame;
+ Transformd WorldFromImu;
+ int Score;
+
+ MagReferencePoint() { }
+ MagReferencePoint(const Vector3d& inImuFrame, const Transformd& worldFromImu, int score)
+ : InImuFrame(inImuFrame), WorldFromImu(worldFromImu), Score(score) { }
+ };
+
+ // -----------------------------------------------
+
+ // The phase of the head as estimated by sensor fusion
+ PoseState<double> WorldFromImu;
+
+ // State that can be read without any locks, so that high priority rendering thread
+ // doesn't have to worry about being blocked by a sensor/vision threads that got preempted.
+ LocklessUpdater<LocklessState> UpdatedState;
+
+ // The pose we got from Vision, augmented with velocity information from numerical derivatives
+ PoseState<double> CameraFromImu;
+ // Difference between the vision and sensor fusion poses at the time of last exposure adjusted
+ // by all the corrections applied since then
+ // NB: this one is unlike all the other poses/transforms we use, since it's a difference
+ // between 2 WorldFromImu transforms, but is stored in the world frame, not the IMU frame
+ // (see computeVisionError() for details)
+ // For composition purposes it should be considered a WorldFromWorld transform, where the left
+ // side comes from vision and the right - from sensor fusion
+ PoseState<double> VisionError;
+ // Past exposure records between the last update from vision and now
+ // (should only be one record unless vision latency is high)
+ CircularBuffer<ExposureRecord> ExposureRecordHistory;
+ // ExposureRecord that corresponds to the last pose we got from vision
+ ExposureRecord LastVisionExposureRecord;
+ // Incomplete ExposureRecord that will go into the history buffer when
+ // the new MessageExposureFrame is received
+ ExposureRecord NextExposureRecord;
+ // Timings of the previous exposure, used to populate ExposureRecordHistory
+ MessageExposureFrame LastMessageExposureFrame;
+ // Time of the last vision update
+ double LastVisionAbsoluteTime;
+
+ unsigned int Stage;
+ BodyFrameHandler *pHandler;
+
+ Vector3d FocusDirection;
+ double FocusFOV;
+
+ SensorFilterBodyFrame FAccelInImuFrame, FAccelInCameraFrame;
+ SensorFilterd FAngV;
+
+ Vector3d AccelOffset;
+
+ bool EnableGravity;
+
+ bool EnableYawCorrection;
+ bool MagCalibrated;
+ Array<MagReferencePoint> MagRefs;
+ int MagRefIdx;
+ Quatd MagCorrectionIntegralTerm;
+
+ bool EnableCameraTiltCorrection;
+ // Describes the pose of the camera in the world coordinate system
+ Transformd WorldFromCamera;
+ double WorldFromCameraConfidence;
+
+ bool MotionTrackingEnabled;
+ bool VisionPositionEnabled;
+
+ // This is a signed distance, but positive because Z increases looking inward.
+ // This is expressed relative to the IMU in the HMD and corresponds to the location
+ // of the cyclopean virtual camera focal point if both the physical and virtual
+ // worlds are isometrically mapped onto each other. -Steve
+ float CenterPupilDepth;
+ // Describes the position of the user eyes relative to the IMU
+ Transformd ImuFromCpf;
+ // Position of the center of the screen relative to the IMU (loaded from the headset firmware)
+ Transformd ImuFromScreen;
+ // Built-in head model for faking position using orientation only
+ Transformd CpfFromNeck;
+ // Last known base of the neck pose used for head model computations
+ Transformd WorldFromNeck;
+
+ //---------------------------------------------
+
+ // Internal handler for messages
+ // bypasses error checking.
+ void handleMessage(const MessageBodyFrame& msg);
+ void handleExposure(const MessageExposureFrame& msg);
+
+ // Compute the difference between vision and sensor fusion data
+ PoseStated computeVisionError();
+ // Apply headset yaw correction from magnetometer
+ // for models without camera or when camera isn't available
+ void applyMagYawCorrection(Vector3d mag, double deltaT);
+ // Apply headset tilt correction from the accelerometer
+ void applyTiltCorrection(double deltaT);
+ // Apply headset yaw correction from the camera
+ void applyVisionYawCorrection(double deltaT);
+ // Apply headset position correction from the camera
+ void applyPositionCorrection(double deltaT);
+ // Apply camera tilt correction from the accelerometer
+ void applyCameraTiltCorrection(Vector3d accel, double deltaT);
+ // Apply camera focus correction
+ void applyFocusCorrection(double deltaT);
+
+ // If you have a known-good pose, this sets the neck pivot position.
+ void setNeckPivotFromPose ( Transformd const &pose );
+};
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorFusion - Inlines
+
+inline bool SensorFusion::IsAttachedToSensor() const
+{
+ return pHandler->IsHandlerInstalled();
+}
+
+inline void SensorFusion::SetGravityEnabled(bool enableGravity)
+{
+ EnableGravity = enableGravity;
+}
+
+inline bool SensorFusion::IsGravityEnabled() const
+{
+ return EnableGravity;
+}
+
+inline void SensorFusion::SetYawCorrectionEnabled(bool enable)
+{
+ EnableYawCorrection = enable;
+}
+
+inline bool SensorFusion::IsYawCorrectionEnabled() const
+{
+ return EnableYawCorrection;
+}
+
+inline bool SensorFusion::IsVisionPositionEnabled() const
+{
+ return VisionPositionEnabled;
+}
+
+inline void SensorFusion::SetVisionPositionEnabled(bool enableVisionPosition)
+{
+ VisionPositionEnabled = enableVisionPosition;
+}
+
+inline void SensorFusion::SetCameraTiltCorrectionEnabled(bool enable)
+{
+ EnableCameraTiltCorrection = enable;
+}
+
+inline bool SensorFusion::IsCameraTiltCorrectionEnabled() const
+{
+ return EnableCameraTiltCorrection;
+}
+
+inline double SensorFusion::GetVisionLatency() const
+{
+ return LastVisionAbsoluteTime - CameraFromImu.TimeInSeconds;
+}
+
+inline double SensorFusion::GetTime() const
+{
+ return Timer::GetSeconds();
+}
+
+inline SensorFusion::BodyFrameHandler::~BodyFrameHandler()
+{
+ RemoveHandlerFromDevices();
+}
+
+inline bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const
+{
+ return (type == Message_BodyFrame || type == Message_ExposureFrame);
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** PoseState - Inlines
+
+// Stores and integrates gyro angular velocity reading for a given time step.
+template<class T>
+void PoseState<T>::StoreAndIntegrateGyro(Vector3d angVel, double dt)
+{
+ AngularVelocity = angVel;
+ double angle = angVel.Length() * dt;
+ if (angle > 0)
+ Pose.Rotation = Pose.Rotation * Quatd(angVel, angle);
+}
+
+template<class T>
+void PoseState<T>::StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt)
+{
+ LinearAcceleration = linearAccel;
+ Pose.Translation += LinearVelocity * dt + LinearAcceleration * (dt * dt * 0.5);
+ LinearVelocity += LinearAcceleration * dt;
+}
+
+// Performs integration of state by adding next state delta to it
+// to produce a combined state change
+template<class T>
+void PoseState<T>::AdvanceByDelta(const PoseState<T>& delta)
+{
+ Pose.Rotation = Pose.Rotation * delta.Pose.Rotation;
+ Pose.Translation += delta.Pose.Translation + LinearVelocity * delta.TimeInSeconds;
+ LinearVelocity += delta.LinearVelocity;
+ TimeInSeconds += delta.TimeInSeconds;
+}
+
+} // namespace OVR
+#endif