public SensorFusion(RiftHeadsetDevice sensor = null)
        {
            Stage = 0;
            RunningTime = 0;
            DeltaT = 0.001f;
            Gain = 0.05f;
            EnableGravity = true;
            EnablePrediction = true;
            PredictionDT = 0.03f;
            PredictionTimeIncrement = 0.001f;
            FRawMag = new SensorFilter(10);
            FAngV = new SensorFilter(20);
            GyroOffset = new Vector3f();
            TiltAngleFilter = new SensorFilterBase_float(1000);
            EnableYawCorrection = false;
            MagCalibrated = false;
            MagNumReferences = 0;
            MagRefIdx = -1;
            MagRefScore = 0;
            MotionTrackingEnabled = true;

            if (sensor != null)
                AttachToSensor(sensor);
            MagCalibrationMatrix = new Matrix4f();
            MagCalibrationMatrix.SetIdentity();
        }
Beispiel #2
0
        // Constructs quaternion for rotation around the axis by an angle.
        public Quatf(Vector3f axis, float angle)
        {
            Vector3f unitAxis = axis.Normalized();
            float sinHalfAngle = (float)System.Math.Sin(angle * (float)(0.5));

            w = (float)System.Math.Cos(angle * (float)(0.5));
            x = unitAxis.X * sinHalfAngle;
            y = unitAxis.Y * sinHalfAngle;
            z = unitAxis.Z * sinHalfAngle;
        }
 // Projects this vector onto a plane defined by a normal vector
 public Vector3f ProjectToPlane(Vector3f normal)
 {
     return this - this.ProjectTo(normal);
 }
 // Projects this vector onto the argument; in other words,
 // A.Project(B) returns projection of vector A onto B.
 public Vector3f ProjectTo(Vector3f b)
 {
     float l2 = b.LengthSq();
     Debug.Assert(l2 != 0);
     return b * (Dot(b) / l2);
 }
 // Linearly interpolates from this vector to another.
 // Factor should be between 0.0 and 1.0, with 0 giving full value to this.
 public Vector3f Lerp(Vector3f b, float f)
 {
     return this * (1 - f) + b * f;
 }
 // Entrywise product of two vectors
 public Vector3f EntrywiseMultiply(Vector3f b)
 {
     return new Vector3f(X * b.X, Y * b.Y, Z * b.Z);
 }
 // Dot product
 // Used to calculate angle q between two vectors among other things,
 // as (A dot B) = |a||b|cos(q).
 public float Dot(Vector3f b)
 {
     return X * b.X + Y * b.Y + Z * b.Z;
 }
 public Vector3f GetCalibratedMagValue(Vector3f rawMag)
 {
     Debug.Assert(HasMagCalibration());
     return MagCalibrationMatrix.Transform(rawMag);
 }
 // Compute cross product, which generates a normal vector.
 // Direction vector can be determined by right-hand rule: Pointing indeX finder in
 // direction a and middle finger in direction b, thumb will point in a.Cross(b).
 public Vector3f Cross(Vector3f b)
 {
     return new Vector3f(Y * b.Z - Z * b.Y, Z * b.X - X * b.Z, X * b.Y - Y * b.X);
 }
Beispiel #10
0
 // Compute axis and angle from quaternion
 public void GetAxisAngle(out Vector3f axis, out float angle)
 {
     if ( x*x + y*y + z*z > Vector3f.Tolerance * Vector3f.Tolerance ) {
         axis  = new Vector3f(x, y, z).Normalized();
         angle = (float)(2 * System.Math.Acos(w));
     }
     else
     {
         axis = new Vector3f(1, 0, 0);
         angle= 0;
     }
 }
        private void OnTrackerMessage(RiftInputReport report)
        {
            const float timeUnit = (1.0f / 1000.0f);

            if (SequenceValid) {
                uint timestampDelta;

                if (report.Timestamp < LastTimestamp)
                    timestampDelta = (uint)((((int)report.Timestamp) + 0x10000) - (int)LastTimestamp);
                else
                    timestampDelta = (uint)(report.Timestamp - LastTimestamp);

                // If we missed a small number of samples, replicate the last sample.
                if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254)) {
                    MessageBodyFrame sensors = new MessageBodyFrame(this);
                    sensors.TimeDelta = (timestampDelta - LastSampleCount) * timeUnit;
                    sensors.Acceleration = LastAcceleration;
                    sensors.RotationRate = LastRotationRate;
                    sensors.MagneticField = LastMagneticField;
                    sensors.Temperature = LastTemperature;

                    Sensor.OnMessage(sensors);
                }
            } else {
                LastAcceleration = new Vector3f();
                LastRotationRate = new Vector3f();
                LastMagneticField = new Vector3f();
                LastTemperature = 0;
                SequenceValid = true;
            }

            LastSampleCount = report.SampleCount;
            LastTimestamp = report.Timestamp;

            bool convertHMDToSensor = (Coordinates == CoordinateFrame.Sensor) && (HWCoordinates == CoordinateFrame.HMD);

            //if (HandlerRef.GetHandler())
            {
                MessageBodyFrame sensors = new MessageBodyFrame(this);
                Byte iterations = report.SampleCount;

                if (report.SampleCount > 3) {
                    iterations = 3;
                    sensors.TimeDelta = (report.SampleCount - 2) * timeUnit;
                } else {
                    sensors.TimeDelta = timeUnit;
                }

                for (Byte i = 0; i < iterations; i++) {
                    sensors.Acceleration = AccelFromBodyFrameUpdate(report, i, convertHMDToSensor);
                    sensors.RotationRate = EulerFromBodyFrameUpdate(report, i, convertHMDToSensor);
                    sensors.MagneticField = MagFromBodyFrameUpdate(report, convertHMDToSensor);
                    sensors.Temperature = report.Temperature * 0.01f;
                    Sensor.OnMessage(sensors);
                    // TimeDelta for the last two sample is always fixed.
                    sensors.TimeDelta = timeUnit;
                }

                LastAcceleration = sensors.Acceleration;
                LastRotationRate = sensors.RotationRate;
                LastMagneticField = sensors.MagneticField;
                LastTemperature = sensors.Temperature;
            }
            //else
            //{
            //    UByte i = (report.SampleCount > 3) ? 2 : (report.SampleCount - 1);
            //    LastAcceleration  = AccelFromBodyFrameUpdate(report, i, convertHMDToSensor);
            //    LastRotationRate  = EulerFromBodyFrameUpdate(report, i, convertHMDToSensor);
            //    LastMagneticField = MagFromBodyFrameUpdate(report, convertHMDToSensor);
            //    LastTemperature   = report.Temperature * 0.01f;
            //}
        }
        //public void        SetDelegateMessageHandler(MessageHandler* handler)
        //{ pDelegate = handler; }
        // Internal handler for messages; bypasses error checking.
        private void handleMessage(MessageBodyFrame msg)
        {
            // Put the sensor readings into convenient local variables
            Vector3f gyro = msg.RotationRate;
            Vector3f accel = msg.Acceleration;
            Vector3f mag = msg.MagneticField;

            // Insert current sensor data into filter history
            FRawMag.AddElement(mag);
            FAngV.AddElement(gyro);

            // Apply the calibration parameters to raw mag
            Vector3f calMag = MagCalibrated ? GetCalibratedMagValue(FRawMag.Mean()) : FRawMag.Mean();

            // Set variables accessible through the class API
            DeltaT = msg.TimeDelta;
            AngV = gyro;
            A = accel;
            RawMag = mag;
            CalMag = calMag;

            // Keep track of time
            Stage++;
            RunningTime += DeltaT;

            // Small preprocessing
            Quatf Qinv = Q.Inverted();
            Vector3f up = Qinv.Rotate(new Vector3f(0, 1, 0));

            Vector3f gyroCorrected = gyro;

            // Apply integral term
            // All the corrections are stored in the Simultaneous Orthogonal Rotations Angle representation,
            // which allows to combine and scale them by just addition and multiplication
            if (EnableGravity || EnableYawCorrection)
                gyroCorrected -= GyroOffset;

            if (EnableGravity) {
                const float spikeThreshold = 0.01f;
                const float gravityThreshold = 0.1f;
                float proportionalGain = 5 * Gain; // Gain parameter should be removed in a future release
                float integralGain = 0.0125f;

                Vector3f tiltCorrection = SensorFusion_ComputeCorrection(accel, up);

                if (Stage > 5) {
                    // Spike detection
                    float tiltAngle = up.Angle(accel);
                    TiltAngleFilter.AddElement(tiltAngle);
                    if (tiltAngle > TiltAngleFilter.Mean() + spikeThreshold)
                        proportionalGain = integralGain = 0;
                    // Acceleration detection
                    const float gravity = 9.8f;
                    if (System.Math.Abs(accel.Length() / gravity - 1) > gravityThreshold)
                        integralGain = 0;
                } else // Apply full correction at the startup
                {
                    proportionalGain = 1 / DeltaT;
                    integralGain = 0;
                }

                gyroCorrected += (tiltCorrection * proportionalGain);
                GyroOffset -= (tiltCorrection * integralGain * DeltaT);
            }

            if (EnableYawCorrection && MagCalibrated && RunningTime > 2.0f) {
                const float maxMagRefDist = 0.1f;
                const float maxTiltError = 0.05f;
                float proportionalGain = 0.01f;
                float integralGain = 0.0005f;

                // Update the reference point if needed
                if (MagRefIdx < 0 || calMag.Distance(MagRefsInBodyFrame[MagRefIdx]) > maxMagRefDist) {
                    // Delete a bad point
                    if (MagRefIdx >= 0 && MagRefScore < 0) {
                        MagNumReferences--;
                        MagRefsInBodyFrame[MagRefIdx] = MagRefsInBodyFrame[MagNumReferences];
                        MagRefsInWorldFrame[MagRefIdx] = MagRefsInWorldFrame[MagNumReferences];
                    }
                    // Find a new one
                    MagRefIdx = -1;
                    MagRefScore = 1000;
                    float bestDist = maxMagRefDist;
                    for (int i = 0; i < MagNumReferences; i++) {
                        float dist = calMag.Distance(MagRefsInBodyFrame[i]);
                        if (bestDist > dist) {
                            bestDist = dist;
                            MagRefIdx = i;
                        }
                    }
                    // Create one if needed
                    if (MagRefIdx < 0 && MagNumReferences < MagMaxReferences) {
                        MagRefIdx = MagNumReferences;
                        MagRefsInBodyFrame[MagRefIdx] = calMag;
                        MagRefsInWorldFrame[MagRefIdx] = Q.Rotate(calMag).Normalized();
                        MagNumReferences++;
                    }
                }

                if (MagRefIdx >= 0) {
                    Vector3f magEstimated = Qinv.Rotate(MagRefsInWorldFrame[MagRefIdx]);
                    Vector3f magMeasured = calMag.Normalized();

                    // Correction is computed in the horizontal plane (in the world frame)
                    Vector3f yawCorrection = SensorFusion_ComputeCorrection(magMeasured.ProjectToPlane(up),
                                                                            magEstimated.ProjectToPlane(up));

                    if (System.Math.Abs(up.Dot(magEstimated - magMeasured)) < maxTiltError) {
                        MagRefScore += 2;
                    } else // If the vertical angle is wrong, decrease the score
                    {
                        MagRefScore -= 1;
                        proportionalGain = integralGain = 0;
                    }
                    gyroCorrected += (yawCorrection * proportionalGain);
                    GyroOffset -= (yawCorrection * integralGain * DeltaT);
                }
            }

            // Update the orientation quaternion based on the corrected angular velocity vector
            float angle = gyroCorrected.Length() * DeltaT;
            if (angle > 0.0f)
                Q = Q * new Quatf(gyroCorrected, angle);

            // The quaternion magnitude may slowly drift due to numerical error,
            // so it is periodically normalized.
            if (Stage % 500 == 0)
                Q.Normalize();
        }
 // Compute a rotation required to transform "estimated" into "measured"
 // Returns an approximation of the goal rotation in the Simultaneous Orthogonal Rotations Angle representation
 // (vector direction is the axis of rotation, norm is the angle)
 public Vector3f SensorFusion_ComputeCorrection(Vector3f measured, Vector3f estimated)
 {
     measured.Normalize();
     estimated.Normalize();
     Vector3f correction = measured.Cross(estimated);
     float cosError = measured.Dot(estimated);
     // from the def. of cross product, correction.Length() = sin(error)
     // therefore sin(error) * sqrt(2 / (1 + cos(error))) = 2 * sin(error / 2) ~= error in [-pi, pi]
     // Mathf::Tolerance is used to avoid div by 0 if cos(error) = -1
     return correction * (float)System.Math.Sqrt(2 / (1 + cosError + Vector3f.Tolerance));
 }
 // Resets the current orientation.
 public void Reset()
 {
     Q                     = new Quatf();
     QUncorrected          = new Quatf();
     Stage                 = 0;
     RunningTime           = 0;
     MagNumReferences      = 0;
     MagRefIdx             = -1;
     GyroOffset            = new Vector3f();
 }
 // Returns the angle from this vector to b, in radians.
 public float Angle(Vector3f b)
 {
     float div = LengthSq() * b.LengthSq();
     Debug.Assert(div != 0);
     return (float)(System.Math.Acos((this.Dot(b)) / System.Math.Sqrt(div)));
 }
 // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance.
 public bool Compare(Vector3f b, float tolerance = Tolerance)
 {
     return (System.Math.Abs(b.X - X) < tolerance) &&
            (System.Math.Abs(b.Y - Y) < tolerance) &&
            (System.Math.Abs(b.Z - Z) < tolerance);
 }
Beispiel #17
0
 // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise,
 // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1.
 public Vector3f Rotate(Vector3f v)
 {
     return ((this * new Quatf(v.X, v.Y, v.Z, 0)) * Inverted()).Imag();
 }
 // Returns distance between two points represented by vectors.
 public float Distance(Vector3f b)
 {
     return (this - b).Length();
 }
 public Vector3f Transform(Vector3f v)
 {
     return new Vector3f(M[0, 0] * v.X + M[0, 1] * v.Y + M[0, 2] * v.Z + M[0, 3],
                         M[1, 0] * v.X + M[1, 1] * v.Y + M[1, 2] * v.Z + M[1, 3],
                         M[2, 0] * v.X + M[2, 1] * v.Y + M[2, 2] * v.Z + M[2, 3]);
 }