paparazzi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[paparazzi-commits] [5849] added libeknav


From: antoine drouin
Subject: [paparazzi-commits] [5849] added libeknav
Date: Fri, 10 Sep 2010 14:26:27 +0000

Revision: 5849
          http://svn.sv.gnu.org/viewvc/?view=rev&root=paparazzi&revision=5849
Author:   poine
Date:     2010-09-10 14:26:27 +0000 (Fri, 10 Sep 2010)
Log Message:
-----------
added libeknav

Added Paths:
-----------
    paparazzi3/trunk/conf/airframes/Poine/fuckyou.xml
    paparazzi3/trunk/sw/airborne/fms/libeknav/
    paparazzi3/trunk/sw/airborne/fms/libeknav/basic_ins_qkf.cpp
    paparazzi3/trunk/sw/airborne/fms/libeknav/hello_world.c
    paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf.hpp
    paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_gps_pvt.cpp
    paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_vector.cpp
    paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_predict.cpp
    paparazzi3/trunk/sw/airborne/fms/libeknav/quaternions.hpp

Added: paparazzi3/trunk/conf/airframes/Poine/fuckyou.xml
===================================================================
--- paparazzi3/trunk/conf/airframes/Poine/fuckyou.xml                           
(rev 0)
+++ paparazzi3/trunk/conf/airframes/Poine/fuckyou.xml   2010-09-10 14:26:27 UTC 
(rev 5849)
@@ -0,0 +1,20 @@
+<airframe name="FuckYou_1">
+
+
+  <makefile>
+    HOST=auto3
+
+    
+    # test 1
+    test42.ARCHDIR = omap
+    test42.CFLAGS  += -I$(ACINCLUDE) -I. -I$(PAPARAZZI_HOME)/var/include
+    test42.srcs     = fms/libeknav/hello_world.c
+#    test1.srcs    += fms/bla.c
+
+
+  </makefile>
+
+
+</airframe>
+
+

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/basic_ins_qkf.cpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/basic_ins_qkf.cpp                 
        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/basic_ins_qkf.cpp 2010-09-10 
14:26:27 UTC (rev 5849)
@@ -0,0 +1,152 @@
+/*
+ * basic_ins_qkf.cpp
+ *
+ *  Created on: Aug 11, 2009
+ *      Author: Jonathan Brandmeyer
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ins_qkf.hpp"
+#include "assertions.hpp"
+
+using namespace Eigen;
+
+basic_ins_qkf::basic_ins_qkf(
+               const Vector3d& estimate,
+               double pos_error, double bias_error, double v_error,
+               const Vector3d& gyro_white_noise,
+               const Vector3d& gyro_stability_noise,
+               const Vector3d& accel_white_noise,
+               const Vector3d& vel_estimate)
+       : gyro_stability_noise(gyro_stability_noise)
+       , gyro_white_noise(gyro_white_noise)
+       , accel_white_noise(accel_white_noise)
+{
+       avg_state.position = estimate;
+       avg_state.gyro_bias = Vector3d::Zero();
+       avg_state.orientation = Quaterniond::Identity();
+       avg_state.velocity = vel_estimate;
+
+       cov << Matrix3d::Identity()*bias_error*bias_error, Matrix<double, 3, 
9>::Zero(),
+               Matrix3d::Zero(), Matrix3d::Identity()*M_PI*M_PI*0.5, 
Matrix<double, 3, 6>::Zero(),
+               Matrix<double, 3, 6>::Zero(), 
Matrix3d::Identity()*pos_error*pos_error, Matrix3d::Zero(),
+               Matrix<double, 3, 9>::Zero(), 
Matrix3d::Identity()*v_error*v_error;
+       assert(is_real());
+}
+
+void
+basic_ins_qkf::counter_rotate_cov(const Quaterniond&)
+{
+       // Rotate the principle axes of the angular error covariance by the
+       // mean update.
+
+       // TODO: This is only required in the case that the system covariance is
+       // right-multiplied by the mean. The current design left-multiplies the
+       // covariance by the mean.
+       return;
+
+       // TODO: There should be an expression that makes both this matrix's
+       // construction and multiplication much more efficient.
+       // Matrix<double, 12, 12> counter_rot = Matrix<double, 12, 
12>::Identity();
+       // counter_rot.block<3, 3>(3, 3) = 
update.cast<double>().conjugate().toRotationMatrix();
+       // cov = (counter_rot * cov.cast<double>() * 
counter_rot.transpose()).cast<float>();
+}
+#if 0
+basic_ins_qkf::state
+basic_ins_qkf::average_sigma_points(const std::vector<state, 
aligned_allocator<state> >& points)
+{
+       state ret;
+       Vector3d sum;
+#define avg_vector_field(field) \
+       sum = Vector3d::Zero(); \
+       for (auto i = points.begin(), end = points.end(); i != end; ++i) { \
+               sum += i->field; \
+       } \
+       ret.field = sum / points.size()
+
+       avg_vector_field(gyro_bias);
+       avg_vector_field(velocity);
+
+       Vector3d p_sum = Vector3d::Zero();
+       for (auto i = points.begin(), end = points.end(); i != end; ++i) {
+               p_sum += i->position;
+       }
+       ret.position = p_sum / (double)points.size();
+
+       std::vector<Quaterniond> quat_points;
+       quat_points.reserve(points.size());
+       for (auto i = points.begin(), end = points.end(); i != end; ++i) {
+               quat_points.push_back(i->orientation);
+       }
+       ret.orientation = quaternion_avg_johnson(quat_points).normalized();
+       return ret;
+}
+#endif
+
+bool
+basic_ins_qkf::is_real(void) const
+{
+       return !(hasNaN(cov) || hasInf(cov)) && avg_state.is_real();
+}
+
+Quaterniond
+basic_ins_qkf::state::apply_kalman_vec_update(const Matrix<double, 12, 1> 
update)
+{
+       // std::cout << "***update available***\n"
+       //              << "\tstate: "; print(std::cout);
+       // std::cout << "\n\tupdate: " << update.transpose() << "\n";
+       gyro_bias += update.segment<3>(0);
+       Quaterniond posterior_update = exp<double>(update.segment<3>(3));
+       orientation = (orientation * posterior_update).normalized();
+       position += update.segment<3>(6);
+       velocity += update.segment<3>(9);
+       assert(is_real());
+       return posterior_update;
+}
+
+#if 0
+Quaterniond
+basic_ins_qkf::state::apply_left_kalman_vec_update(const Matrix<double, 12, 1> 
update)
+{
+       // std::cout << "***update available***\n"
+       //              << "\tstate: "; print(std::cout);
+       // std::cout << "\n\tupdate: " << update.transpose() << "\n";
+       gyro_bias += update.segment<3>(0);
+       Quaterniond posterior_update = exp<double>(update.segment<3>(3));
+       orientation = (posterior_update * orientation).normalized();
+       position += update.segment<3>(6);
+       velocity += update.segment<3>(9);
+       assert(is_real());
+       return posterior_update;
+}
+#endif
+
+bool
+basic_ins_qkf::state::has_nan(void)const
+{
+       return hasNaN(gyro_bias) || hasNaN(orientation.coeffs())
+                       || hasNaN(position) || hasNaN(velocity);
+}
+
+bool
+basic_ins_qkf::state::is_real(void) const
+{
+       return !(hasNaN(gyro_bias) || hasNaN(orientation.coeffs())
+                       || hasNaN(position) || hasNaN(velocity))
+                       && !(hasInf(gyro_bias) || hasInf(orientation.coeffs())
+                               || hasInf(position) || hasInf(velocity));
+}
+

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/hello_world.c
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/hello_world.c                     
        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/hello_world.c     2010-09-10 
14:26:27 UTC (rev 5849)
@@ -0,0 +1,12 @@
+
+
+#include <stdio.h>
+
+
+int main(int argc, char** argv) {
+
+  printf("hello world\n");
+
+  return 0;
+}
+

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf.hpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf.hpp                       
        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf.hpp       2010-09-10 
14:26:27 UTC (rev 5849)
@@ -0,0 +1,286 @@
+#ifndef AHRS_INS_QKF_HPP
+#define AHRS_INS_QKF_HPP
+/*
+ * ins_qkf.cpp
+ *
+ *      Author: Jonathan Brandmeyer
+ *
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sigma_points.hpp"
+#include "quaternions.hpp"
+#include <Eigen/StdVector>
+
+using Eigen::Vector3f;
+using Eigen::Vector3d;
+using Eigen::Vector2d;
+using Eigen::Quaterniond;
+using Eigen::Matrix;
+using Eigen::aligned_allocator;
+
+
+struct basic_ins_qkf
+{
+       EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+       /// The maximum number of satellites that may be tracked by the filter
+       static const size_t max_sv = 12;
+       /**
+        * The covariance of the zero-mean gaussian white noise that is added to
+        * the gyro bias at each time step.  This value is treated as a diagonal
+        *  matrix, in units of radians^2/second^2.
+        */
+       const Vector3d gyro_stability_noise;
+
+       /**
+        * The covariance of the zero-mean gaussian white noise that is added to
+        * the gyro measurement at each time step, in rad^2/second^2
+        */
+       const Vector3d gyro_white_noise;
+
+       /**
+        * The covariance of the zero-mean gaussian white noise that is added to
+        * the accelerometer measurement at each time step, in (m/s/s)^2
+        */
+       const Vector3d accel_white_noise;
+
+       /**
+        * A term for the basic state of the system
+        */
+       struct state
+       {
+               EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+               typedef std::vector<state, aligned_allocator<state> > 
container_t;
+
+               /**
+                * Construct a state variable from mean and error terms
+                * @param mean The average state
+                * @param error An error vector of length 12 or greater
+                */
+               template <typename Vector_T>
+               state(const state& mean, const Vector_T& error)
+                       : gyro_bias(mean.gyro_bias + error.template 
segment<3>(0))
+                       , orientation(mean.orientation * 
exp<double>(error.template segment<3>(3)))
+                       , position(mean.position + error.template 
segment<3>(6).template cast<double>())
+                       , velocity(mean.velocity + error.template segment<3>(9))
+               {
+                       assert(!has_nan());
+               }
+
+#if 0
+               template <typename Vector_T>
+               state(const state& mean, const Vector_T& error, bool)
+                       : gyro_bias(mean.gyro_bias + error.segment(0, 3))
+                       , orientation(mean.orientation * 
exp<double>(error.segment(3, 3)))
+                       , position(mean.position + error.segment(6, 3).template 
cast<double>())
+                       , velocity(mean.velocity + error.segment(9, 3))
+               {
+                       assert(!has_nan());
+               }
+#endif
+               /**
+                * Default-construct an undefined state object.
+                * @return
+                */
+               state(){}
+
+               /**
+                * Provided a kalman update vector, apply the vector as an 
offset to this
+                * state.
+                * @param update A 12-vector to be applied
+                * @return The rotation applied to the mean orientation
+                */
+               Quaterniond apply_kalman_vec_update(const Matrix<double, 12, 1> 
update);
+               Quaterniond apply_left_kalman_vec_update(const Matrix<double, 
12, 1> update);
+
+               /**
+                * An estimate of the bias error in the rate gyros, in 
radians/second
+                */
+               Vector3d gyro_bias;
+
+               /**
+                * An estimate of the orientation of the vehicle.  This 
quaternion represents
+                * a transformation from ECEF coordinates to the vehicle body 
frame.
+                */
+               Quaterniond orientation;
+
+               /// Position in Earth-centered Earth-fixed reference frame, in 
meters
+               Vector3d position;
+
+               /// Velocity in Earth-centered Earth-fixed reference frame, in 
m/s
+               Vector3d velocity;
+
+               /**
+                * @return True if the state vector contains any NaNs
+                */
+               bool has_nan(void) const;
+
+               /**
+                * @return True if the state vector does not contain any NaNs 
or Infs
+                */
+               bool is_real(void) const;
+
+               /**
+                * Print a representation of this object to the stream str.
+                * @param str An output stream.
+                */
+               void print(std::ostream& str);
+       };
+
+       /// The average state of the filter at any time t.
+       state avg_state;
+
+       /// Covariance term.  Elements are ordered exactly as in struct state
+       Matrix<double, 12, 12> cov;
+
+       /**
+        * Initialize a new basic INS QKF
+        * @param pos_estimate Initial estimate of the position
+        * @param pos_error one-sigma initial bounds for position error
+        * @param bias_error one-sigma initial bounds for bias error in the 
gyros
+        * @param v_error one-sigma bounds for velocity error (initial v == 0)
+        * @param gyro_white_noise The diagonal matrix of gyro white noise
+        * @param gyro_stability_noise The diagonal matrix of gyro instability 
noise
+        * @param accel_white_noise The diagonal matrix of accelerometer white 
noise
+        */
+       basic_ins_qkf(const Vector3d& pos_estimate,
+                       double pos_error, double bias_error, double v_error,
+                       const Vector3d& gyro_white_noise,
+                       const Vector3d& gyro_stability_noise,
+                       const Vector3d& accel_white_noise,
+                       const Vector3d& vel_estimate = Vector3d::Zero());
+
+       /**
+        * Report an INS observation, to propagate the filter forward by one 
time
+        * step. The coordinate system is maintained in ECEF coordinates.
+        * TODO: Provide a workspace parameter for storage that should be
+        * carried forward to an observation function carried out in this
+        * time step.
+        * @param gyro_meas The measured angular velocity, in rad/sec
+        * @param accel_meas The measured inertial reference frame 
acceleration, in m/s
+        * @param dt The elapsed time since the last measurement, in seconds.
+        */
+       void predict(const Vector3d& gyro_meas, const Vector3d& accel_meas, 
double dt);
+
+       /**
+        * Report an INS observation, to propagate the filter forward by one 
time
+        * step. This function differs from predict() in that it uses the NED 
frame
+        * instead of the ECEF frame.
+        */
+       void predict_ned(const Vector3d& gyro_meas, const Vector3d& accel_meas, 
double dt);
+
+       /**
+        * Make a single vector observation, with some angular uncertainty.
+        * Warning: only one of obs_vector or obs_gps_pv_report can be called
+        * after a single call to predict().
+        *      @param ref Reference vector, when the orientation is the 
identity.
+        *              Must be a unit vector.
+        *      @param obs Vector observation, should not be a unit vector
+        *      @param error one-sigma squared magnitude error in the 
observation
+        */
+       void obs_vector(const Vector3d& ref, const Vector3d& obs, double error);
+
+       /**
+        * Incorporate a GPS PVT report.
+        * @param pos The sensor position, in earth-centered, earth-fixed 
coords, meters
+        * @param vel The sensor velocity, in meters/second
+        * @param p_error The RMS position error, (m)^2
+        * @param v_error The RMS velocity error, (m/s)^2
+        */
+       void obs_gps_pv_report(const Vector3d& pos, const Vector3d& vel, const 
Vector3d& p_error, const Vector3d v_error);
+
+       /**
+        * Incorporate a GPS position report, in either ECEF or NED coordinates.
+        * @param pos The position, in meters
+        * @param p_error The position error, in meters.
+        */
+       void obs_gps_p_report(const Vector3d& pos, const Vector3d& p_error);
+
+       /**
+        * Incorporate a GPS velocity report, in ECEF 3d coordinates.
+        * @param vel The 3d velocity, relative to the fixed earth frame, in 
(m/s).
+        * @param v_error The one-sigma RMS velocity error (m/s)^2
+        */
+       void obs_gps_v_report(const Vector3d& vel, const Vector3d& v_error);
+
+       /**
+        * Observe a GPS vector track over ground report, in north-east-down 
coordinates
+        * @param vel The 2d velocity value, parallel to the ground, in m/s
+        * @param v_error The one-sigma RMS velocity error (m/s)^2
+        */
+       void obs_gps_vtg_report(const Vector2d vel, const double v_error);
+
+       /**
+        * Directly observe the gyro sensor bias. In practice, we cannot do 
this. However,
+        * the true bias is not a random walk. It tends to return towards zero 
when it is
+        * farther away from zero (not temperature dependent). Therefore, we can
+        * incorporate this extra knowledge through a periodic "observation" of 
zero 
+        * bias with a large error.
+        *
+        * Use with extreme caution. It is almost certainly better to just 
clamp the
+        * bias term after making an observation.
+        *
+        * @param bias The observed bias, in radians/sec
+        * @param bias_error The one-sigma estimate of the gyro bias error, in 
radians/sec
+        */
+       void obs_gyro_bias(const Vector3d& bias, const Vector3d& bias_error);
+
+
+       /**
+        * Measure the total angular error between the filter's attitude 
estimate
+        * and some other orientation.
+        * @param orientation The attitude to compare against
+        * @return The angular difference between them, in radians
+        */
+       double angular_error(const Quaterniond& orientation) const;
+
+       /**
+        * Measure the total gyro bias error between the filter's estimate and
+        * some other bias
+        * @param gyro_bias The gyro bias vector to compare against
+        * @return The vector difference between them, in radians/second
+        */
+       double gyro_bias_error(const Vector3d& gyro_bias) const;
+
+       /**
+        * Determine the statistical distance between an example sample point
+        * and the distribution computed by the estimator.
+        */
+       double mahalanobis_distance(const state& sample) const;
+
+private:
+
+       /**
+        * The type of an error term between two state vectors.
+        */
+       typedef Eigen::Matrix<double, 12, 1> state_error_t;
+       /// Compute the error difference between a sigma point and the mean as: 
point - mean
+       state_error_t sigma_point_difference(const state& mean, const state& 
point) const;
+
+
+public:
+       /** Perform the posterior counter-rotation of the covariance matrix by
+         the update that gets applied to the estimated state.
+         */
+       void counter_rotate_cov(const Quaterniond& update);
+
+       /**
+        * Verify that the covariance and average state are niether NaN nor Inf
+        * @return True, iff no element in the covariance or mean are NaN or Inf
+        */
+       bool is_real(void) const;
+};
+
+#endif

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_gps_pvt.cpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_gps_pvt.cpp       
                        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_gps_pvt.cpp       
2010-09-10 14:26:27 UTC (rev 5849)
@@ -0,0 +1,119 @@
+/*
+ * ins_qkf_observe_gps_pvt.cpp
+ *
+ *  Created on: Sep 2, 2009
+ *      Author: Jonathan Brandmeyer
+ *
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ins_qkf.hpp"
+#include "assertions.hpp"
+#include <Eigen/LU>
+#include "timer.hpp"
+
+#ifdef TIME_OPS
+#include <iostream>
+#endif
+
+using namespace Eigen;
+
+#define RANK_ONE_UPDATES
+
+void
+basic_ins_qkf::obs_gps_v_report(const Vector3d& vel, const Vector3d& v_error)
+{
+       Vector3d residual = vel - avg_state.velocity;
+       std::cout << "diff_v(" <<residual(0) << ", " << residual(1) << ", " << 
residual(2) <<")\n";
+
+#ifdef RANK_ONE_UPDATES
+       Matrix<double, 12, 1> update = Matrix<double, 12, 1>::Zero();
+       for (int i = 0; i < 3; ++i) {
+               double innovation_cov_inv = 1.0/(cov(9+i, 9+i) + v_error[i]);
+               Matrix<double, 12, 1> gain = cov.block<12, 1>(0, 9+i) * 
innovation_cov_inv;
+               update += gain * (residual[i] - update[9+i]);
+               cov -= gain * cov.block<1, 12>(9+i, 0);
+       }
+#else
+       Matrix<double, 3, 3> innovation_cov = cov.block<3, 3>(9, 9);
+       innovation_cov += v_error.asDiagonal();
+       Matrix<double, 3, 12> kalman_gain_t;
+       innovation_cov.qr().solve(cov.block<3, 12>(9, 0), &kalman_gain_t);
+       cov.part<Eigen::SelfAdjoint>() -= cov.block<12, 3>(0, 9) * 
kalman_gain_t; // .transpose() * cov.block<3, 12>(9, 0);
+       Matrix<double, 12, 1> update = kalman_gain_t.transpose() * residual;
+#endif
+       std::cout << "update(" << update.segment<6>(0).transpose()*180/M_PI << 
"\t" << update.segment<6>(6).transpose() << ")\n";
+
+       //update.segment<6>(0) = Matrix<double, 6, 1>::Zero(); // only for 
debugging
+       std::cout << "update(" << update.segment<6>(0).transpose()*180/M_PI << 
"\t" << update.segment<6>(6).transpose() << ")\n";
+       Quaterniond rotor = avg_state.apply_kalman_vec_update(update);
+       counter_rotate_cov(rotor);
+       assert(is_real());
+}
+
+void
+basic_ins_qkf::obs_gps_pv_report(
+               const Vector3d& pos, const Vector3d& vel,
+               const Vector3d& p_error, const Vector3d v_error)
+{
+#ifdef TIME_OPS
+       timer clock;
+       clock.start();
+#endif
+#if 1
+       obs_gps_p_report(pos, p_error);
+       obs_gps_v_report(vel, v_error);
+#else
+
+       // The observation model is strictly linear here, so use the linear
+       // form of the kalman gain and update
+       Matrix<double, 6, 12> obs_matrix;
+       obs_matrix << Matrix<double, 6, 6>::Zero(), Matrix<double, 6, 
6>::Identity();
+
+       Matrix<double, 6, 1> residual;
+       residual.segment<3>(0) = (pos - avg_state.position);
+       residual.segment<3>(3) = vel - avg_state.velocity;
+
+       Matrix<double, 6, 6> innovation_cov = cov.corner<6, 
6>(Eigen::BottomRight);
+       //innovation_cov = obs_matrix * cov * obs_matrix.transpose();
+       innovation_cov.corner<3, 3>(Eigen::TopLeft) += p_error.asDiagonal();
+       innovation_cov.corner<3, 3>(Eigen::BottomRight) += v_error.asDiagonal();
+#if 1
+       // Perform matrix inverse by LU decomposition instead of cofactor 
expansion.
+       // K = P*transpose(H)*inverse(S)
+       // K = P*transpose(transpose(transpose(H)*inverse(S)))
+       // K = P*transpose(transpose(inverse(S))*H)
+       // K = P*transpose(inverse(transpose(S))*H)
+       // S == transpose(S)
+       // K = P*transpose(inverse(S)*H)
+       // obs_matrx <- inverse(S)*H
+       Matrix<double, 6, 12> inv_s_h;
+       innovation_cov.qr().solve(obs_matrix, &inv_s_h);
+       Matrix<double, 12, 6> kalman_gain = cov * inv_s_h.transpose();
+#else
+       Matrix<double, 12, 6> kalman_gain = cov.block<12, 6>(0, 6) * 
innovation_cov.inverse();
+#endif
+       Quaterniond rotor = avg_state.apply_kalman_vec_update(kalman_gain * 
residual);
+       cov.part<Eigen::SelfAdjoint>() -= kalman_gain * obs_matrix * cov;
+       counter_rotate_cov(rotor);
+       assert(is_real());
+#endif
+
+#ifdef TIME_OPS
+       double time = clock.stop() * 1e6;
+       std::cout << "obs_gps_pvt time: " << time << "\n";
+#endif
+}
+

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_vector.cpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_vector.cpp        
                        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_observe_vector.cpp        
2010-09-10 14:26:27 UTC (rev 5849)
@@ -0,0 +1,192 @@
+/*
+ * ins_qkf_observe_vector.cpp
+ *
+ *  Created on: Sep 2, 2009
+ *      Author: Jonathan Brandmeyer
+
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ins_qkf.hpp"
+#include "assertions.hpp"
+#include "timer.hpp"
+
+#ifdef TIME_OPS
+#include <iostream>
+#endif
+
+using namespace Eigen;
+#define RANK_ONE_UPDATES
+
+void
+basic_ins_qkf::obs_gyro_bias(const Vector3d& bias, const Vector3d& bias_error)
+{
+       Matrix<double, 12, 3> kalman_gain = cov.block<12, 3>(0, 0) 
+                       * (cov.block<3, 3>(0, 0) + 
bias_error.asDiagonal()).inverse();
+       cov -= kalman_gain * cov.block<3, 12>(0, 0);
+       Vector3d innovation = bias - avg_state.gyro_bias;
+
+       // Apply the Kalman gain to obtain the posterior state and error 
estimates.
+       avg_state.apply_kalman_vec_update(kalman_gain * innovation);
+}
+
+void
+basic_ins_qkf::obs_vector(const Vector3d& ref, const Vector3d& obs, double 
error)
+{
+#ifdef TIME_OPS
+       timer clock;
+       clock.start();
+#endif
+#define DEBUG_VECTOR_OBS 0
+       // Optimization opportunity: re-use sigma points from the state 
prediction
+#if 0
+       std::vector<vector_obs_state, aligned_allocator<vector_obs_state> > 
points;
+       Matrix<double, 15, 30> state_errors;
+       decompose_sigma_points(points, state_errors, error);
+
+       std::vector<Quaterniond> projected_points;
+       projected_points.reserve(points.size());
+       for (auto i = points.begin(), end = points.end(); i != end; ++i) {
+               projected_points.push_back(observe_vector(*i, ref));
+       }
+
+       Vector3d expected_obs = avg_state.orientation * ref;
+       Quaterniond expected_observation_prediction = 
Quaterniond().setFromTwoVectors(ref, expected_obs);
+       // Compute the observation error matrix and its self-covariance
+       Quaterniond mean_observation_pred = 
quaternion_avg_johnson(projected_points);
+
+
+       // assert(obs_projection.transpose().isUnitary());
+#if DEBUG_VECTOR_OBS
+       std::cout << "\n\nref: " << ref.transpose();
+       std::cout << "\nobs: " << obs.transpose();
+       std::cout << "\nexpected observation: " << 
expected_observation_prediction.coeffs().transpose();
+       std::cout << "\nUKF observation: " << 
mean_observation_pred.coeffs().transpose();
+       std::cout << "\nexpected innovation: " << 
log<double>(Quaterniond().setFromTwoVectors(
+               expected_obs, obs)).transpose();
+       std::cout << "\ntangent space innovation: " << 
log<double>(avg_state.orientation.conjugate() *
+               Quaterniond().setFromTwoVectors(expected_obs, obs) * 
avg_state.orientation) << "\n";
+#endif
+       Matrix<double, 3, Dynamic> obs_errors(3, projected_points.size());
+       for (unsigned i = 0; i != projected_points.size(); ++i) {
+               if 
(projected_points[i].coeffs().dot(mean_observation_pred.coeffs()) < 0) {
+                       // Choose the point on the same hemisphere as the mean. 
 otherwise, the error vectors
+                       // get screwed up.
+                       projected_points[i].coeffs() *= -1;
+               }
+               obs_errors.col(i) = 
log<double>(mean_observation_pred.conjugate() * projected_points[i]);
+       }
+
+       // Construct an observation matrix composed of the two unit vectors
+       // orthogonal to the direction that pivots about the expected 
observation
+       // Vector3d obs = obs.normalized();
+       Matrix<double, 2, 3> obs_projection;
+#if 1
+       SelfAdjointEigenSolver<Matrix<double, 3, 3> > obs_cov((obs_errors * 
obs_errors.transpose())*0.5);
+       obs_projection = obs_cov.eigenvectors().block<3, 2>(0, 1).transpose();
+#else
+       typedef Vector3d vector_t;
+       obs_projection.row(0) = 
expected_obs.cross((expected_obs.dot(vector_t::UnitX()) < 0.707)
+                               ? vector_t::UnitX() : 
vector_t::UnitY()).normalized();
+       obs_projection.row(1) = expected_obs.cross(obs_projection.row(0));
+#endif
+       assert(!hasNaN(obs_projection));
+#if DEBUG_VECTOR_OBS
+       std::cout << "predicted obs: " << expected_obs.transpose() << "\n";
+       std::cout << "actual obs: " << obs.transpose() << "\n";
+       std::cout << "reference obs: " << ref.transpose() << "\n";
+       std::cout << "obs cov eigenvalues: " << 
obs_cov.eigenvalues().transpose() << "\n";
+       std::cout << "obs cov eigenvectors:\n" << obs_cov.eigenvectors() << 
"\n";
+       std::cout << "measurement subspace:\n" << obs_projection.transpose() << 
"\n";
+#endif
+
+       Matrix<double, 2, Dynamic> projected_obs_errors = 
obs_projection*obs_errors;
+       Matrix<double, 2, 2> obs_pred_cov = (projected_obs_errors * 
projected_obs_errors.transpose())*0.5;
+#if DEBUG_VECTOR_OBS
+       std::cout << "obs errors: " << obs_errors << "\n";
+       std::cout << "S: " << obs_pred_cov << "\n";
+       std::cout << "expected S: " << cov.block<3, 3>(3, 3) << "\n";
+#endif
+
+       Matrix<double, 12, 2> state_meas_cross_cov =
+                       (state_errors.block<12, 30>(0,0) * 
projected_obs_errors.transpose())*0.5;
+       Matrix<double, 12, 2> kalman_gain = state_meas_cross_cov * 
obs_pred_cov.inverse();
+       // Innovation rotation in the tangent space at the mean
+       Vector2d innovation = obs_projection*log<double>(
+                       Quaterniond().setFromTwoVectors((mean_observation_pred 
* ref), obs));
+#if DEBUG_VECTOR_OBS
+       std::cout << "inverse(S): " << obs_pred_cov.inverse() << "\n";
+       std::cout << "used innovation: " << 
log<double>(Quaterniond().setFromTwoVectors(
+               mean_observation_pred * ref, obs)).transpose() << "\n";
+       std::cout << "projected innovation: " << innovation.transpose() << "\n";
+       std::cout << "deprojected innovation: " << (obs_projection.transpose() 
* innovation).transpose() << "\n";
+#endif
+       // actually, cov -= K*S*K^T, but this saves a multiplication.
+       cov -= (kalman_gain * obs_pred_cov * kalman_gain.transpose());
+#else
+       // BIG optimization opportunity: Use a pseudo-linear measurement model.
+
+       Vector3d obs_ref = avg_state.orientation.conjugate()*obs;
+       Vector3d v_residual = log<double>(Quaterniond().setFromTwoVectors(ref, 
obs_ref));
+       std::cout << "v_residual(" <<v_residual(0)*180/M_PI << ", " << 
v_residual(1)*180/M_PI << ", " << v_residual(2)*180/M_PI <<")\n";
+
+       Matrix<double, 3, 2> h_trans;
+       h_trans.col(0) = ref.cross(
+                       (abs(ref.dot(obs_ref)) < 0.9994) ? obs_ref :
+                               (abs(ref.dot(Vector3d::UnitX())) < 0.707)
+                               ? Vector3d::UnitX() : 
Vector3d::UnitY()).normalized();
+       h_trans.col(1) = -ref.cross(h_trans.col(0));
+       assert(!hasNaN(h_trans));
+       assert(h_trans.isUnitary());
+       Vector2d innovation = h_trans.transpose() * v_residual;
+
+#ifdef RANK_ONE_UPDATES
+       // Running a rank-one update here is a strict win.
+       Matrix<double, 12, 1> update = Matrix<double, 12, 1>::Zero();
+       for (int i = 0; i < 2; ++i) {
+               double obs_error = error;
+               double obs_cov = (h_trans.col(i).transpose() * cov.block<3, 
3>(3, 3) * h_trans.col(i))[0];
+               Matrix<double, 12, 1> gain = cov.block<12, 3>(0, 3) * 
h_trans.col(i) / (obs_error + obs_cov);
+               update += gain * h_trans.col(i).transpose() * v_residual;
+               cov -= gain * h_trans.col(i).transpose() * cov.block<3, 12>(3, 
0);
+       }
+#else
+       Matrix<double, 12, 2> kalman_gain = cov.block<12, 3>(0, 3) * h_trans
+                       * (h_trans.transpose() * cov.block<3, 3>(3, 3) * 
h_trans 
+                               + (Vector2d() << error, 
error).finished().asDiagonal()).inverse();
+       cov -= kalman_gain * h_trans.transpose() * cov.block<3, 12>(3, 0);
+#endif
+
+#endif
+
+       // Apply the Kalman gain to obtain the posterior state and error 
estimates.
+#ifndef RANK_ONE_UPDATES
+       Matrix<double, 12, 1> update = (kalman_gain * innovation);
+#endif
+
+#if DEBUG_VECTOR_OBS
+       // std::cout << "projected update: " << (obs_projection * 
update.segment<3>(3)).transpose() << "\n";
+       std::cout << "deprojected update: " << update.segment<3>(3).transpose() 
<< "\n";
+#endif
+       Quaterniond posterior_update = 
avg_state.apply_kalman_vec_update(update);
+       counter_rotate_cov(posterior_update);
+
+       assert(is_real());
+#ifdef TIME_OPS
+       double time = clock.stop() * 1e6;
+       std::cout << "observe_vector(): " << time << "\n";
+#endif
+
+}

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_predict.cpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_predict.cpp               
                (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/ins_qkf_predict.cpp       
2010-09-10 14:26:27 UTC (rev 5849)
@@ -0,0 +1,164 @@
+/*
+ * ins_qkf_predict.cpp
+ *
+ *  Created on: Sep 2, 2009
+ *      Author: Jonathan Brandmeyer
+
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ins_qkf.hpp"
+#include "assertions.hpp"
+#include "timer.hpp"
+
+#ifdef TIME_OPS
+# include <iostream>
+#endif
+
+using namespace Eigen;
+
+namespace {
+
+Matrix<double, 3, 3>
+axis_scale(const Vector3d& axis, double scale)
+{
+       return (scale - 1) * axis * axis.transpose() + Matrix3d::Identity();
+}
+
+void
+linear_predict(basic_ins_qkf& _this, const Vector3d& gyro_meas, const 
Vector3d& accel_meas, double dt)
+{
+       // The two components of rotation that do not spin about the gravity 
vector
+       // have an influence on the position and velocity of the vehicle.
+       // Let r be an error axis of rotation, and z be the gravity vector.
+       // Increasing r creates increasing error in the direction _|_ to r and 
z.
+       // By the small angle theorem, the amount of error is ~ abs(r)*abs(z).
+       // Increasing r also creates increasing error in the direction || to -z.
+       // By the small angle theorem, the amount of error is ~ zero.
+       // Therefore, rotate the error block about the z axis by -90 degrees, 
and
+       // zero out the error vector in the z direction.
+       // accel_cov is the relationship between error vectors in the tangent 
space
+       // of the vehicle orientation and the translational reference frame.
+       Vector3d accel_body = 
_this.avg_state.orientation.conjugate()*accel_meas;               // accel_body 
is in ECEF!
+       std::cout << "   ACCEL_BODY(" << accel_body.transpose() << ")\n";
+       Vector3d accel_gravity = _this.avg_state.position.normalized()*9.81;
+       std::cout << "   ACCEL_GRAVITY(" << accel_gravity.transpose() << ")\n";
+       //Vector3d accel_resid = accel_body - accel_gravity;
+       Vector3d accel_resid = accel_body + accel_gravity;                      
                                        // This is better!
+       std::cout << "   ACCEL_RESID(" << accel_resid.transpose() << ")\n";
+#if 0
+       // This form works well with zero static acceleration.
+       Matrix<double, 3, 3> accel_cov =
+               Eigen::AngleAxisd(-M_PI*0.5, 
_this.avg_state.position.normalized())
+               * axis_scale(_this.avg_state.position.normalized(), 0) * 9.81;
+#elif 1
+       Matrix<double, 3, 3> accel_cov =
+               Eigen::AngleAxisd(-M_PI*0.5, accel_body.normalized())
+               * axis_scale(accel_body.normalized(), 0) * accel_meas.norm();
+#else
+       // The following form ends up being identical to the simpler one
+       // above
+       Matrix<double, 3, 3> accel_cov = 
+               Eigen::AngleAxisd(-M_PI*0.5, 
_this.avg_state.position.normalized())
+               * axis_scale(_this.avg_state.position.normalized(), 0) * 9.81
+               + Eigen::AngleAxisd(-M_PI*0.5, accel_resid.normalized())
+               * axis_scale(accel_resid.normalized(), 0)*accel_resid.norm();
+#endif
+       // TODO: Optimization opportunity: the accel_cov doesn't change much 
over
+       // the life of a mission. Precompute it once and then retain the 
original.
+       // Then, only one 3x3 block ever gets updated in the A matrix below.
+
+       // The linearized Kalman state projection matrix.
+#if 0
+       Matrix<double, 12, 12> A;
+            // gyro bias row
+       A << Matrix<double, 3, 3>::Identity(), Matrix<double, 3, 9>::Zero(),
+                // Orientation row
+                _this.avg_state.orientation.conjugate().toRotationMatrix()*-dt,
+                        Matrix<double, 3, 3>::Identity(), Matrix<double, 3, 
6>::Zero(),
+                // Position row
+                Matrix<double, 3, 3>::Zero(), -accel_cov*0.5*dt*dt,
+                        Matrix<double, 3, 3>::Identity(), Matrix<double, 3, 
3>::Identity()*dt,
+                // Velocity row
+                Matrix<double, 3, 3>::Zero(), -accel_cov * dt,
+                        Matrix<double, 3, 3>::Zero(), Matrix<double, 3, 
3>::Identity();
+
+       // 800x realtime, with vectorization
+       _this.cov.part<Eigen::SelfAdjoint>() = A * _this.cov * A.transpose();
+#else
+       // 1500x realtime, without vectorization, on 2.2 GHz Athlon X2
+       const Matrix<double, 12, 12> cov = _this.cov;
+       const Matrix3d dtR = dt * 
_this.avg_state.orientation.conjugate().toRotationMatrix();
+       const Matrix3d dtQ = accel_cov * dt;
+
+       _this.cov.block<3, 3>(0, 3) -= cov.block<3,3>(0, 0)*dtR.transpose();
+       _this.cov.block<3, 3>(0, 6) += dt * cov.block<3, 3>(0, 9);
+       _this.cov.block<3, 3>(0, 9) -= cov.block<3, 3>(0, 3) * dtQ.transpose();
+       _this.cov.block<3, 3>(3, 3).part<Eigen::SelfAdjoint>() += 
dtR*cov.block<3, 3>(0, 0)*dtR.transpose()
+                       - dtR*cov.block<3, 3>(0, 3) - cov.block<3, 3>(3, 
0)*dtR.transpose();
+       _this.cov.block<3, 3>(3, 6) += -dtR * (cov.block<3, 3>(0, 6) + 
dt*cov.block<3, 3>(0, 9))
+                       + dt*cov.block<3, 3>(3, 9);
+       _this.cov.block<3, 3>(3, 9) += -dtR*( -cov.block<3, 3>(0, 
3)*dtQ.transpose() + cov.block<3, 3>(0, 9))
+                       - cov.block<3, 3>(3, 3)*dtQ.transpose();
+       _this.cov.block<3, 3>(6, 6).part<Eigen::SelfAdjoint>() += 
dt*cov.block<3, 3>(6, 9) + dt*dt*cov.block<3, 3>(9, 9)
+                       + dt*cov.block<3, 3>(9, 6);
+       _this.cov.block<3, 3>(6, 9) += -cov.block<3, 3>(6, 3)*dtQ.transpose() + 
dt*cov.block<3, 3>(9, 9)
+                       - dt*cov.block<3, 3>(9, 3)*dtQ.transpose();
+       _this.cov.block<3, 3>(9, 9).part<Eigen::SelfAdjoint>() += 
dtQ*cov.block<3, 3>(3, 3)*dtQ.transpose()
+                       - dtQ*cov.block<3, 3>(3, 9) - cov.block<3, 3>(9, 
3)*dtQ.transpose();
+
+       _this.cov.block<3, 3>(3, 0) = _this.cov.block<3, 3>(0, 3).transpose();
+       _this.cov.block<3, 3>(6, 0) = _this.cov.block<3, 3>(0, 6).transpose();
+       _this.cov.block<3, 3>(6, 3) = _this.cov.block<3, 3>(3, 6).transpose();
+       _this.cov.block<3, 3>(9, 0) = _this.cov.block<3, 3>(0, 9).transpose();
+       _this.cov.block<3, 3>(9, 3) = _this.cov.block<3, 3>(3, 9).transpose();
+       _this.cov.block<3, 3>(9, 6) = _this.cov.block<3, 3>(6, 9).transpose();
+#endif
+
+       _this.cov.block<3, 3>(0, 0) += _this.gyro_stability_noise.asDiagonal() 
* dt;
+       _this.cov.block<3, 3>(3, 3) += _this.gyro_white_noise.asDiagonal() * dt;
+       _this.cov.block<3, 3>(6, 6) += _this.accel_white_noise.asDiagonal() * 
0.5*dt*dt;
+       _this.cov.block<3, 3>(9, 9) += _this.accel_white_noise.asDiagonal() * 
dt;
+
+       Quaterniond orientation = exp<double>((gyro_meas - 
_this.avg_state.gyro_bias) * dt)
+                       * _this.avg_state.orientation;
+       Vector3d accel = accel_body - _this.avg_state.position.normalized() * 
9.81;
+       Vector3d position = _this.avg_state.position + _this.avg_state.velocity 
* dt + 0.5*accel*dt*dt;
+       Vector3d velocity = _this.avg_state.velocity + accel*dt;
+
+       _this.avg_state.position = position;
+       _this.avg_state.velocity = velocity;
+       _this.avg_state.orientation = orientation;
+}
+
+} // !namespace (anon)
+
+void
+basic_ins_qkf::predict(const Vector3d& gyro_meas, const Vector3d& accel_meas, 
double dt)
+{
+#ifdef TIME_OPS
+       timer clock;
+       clock.start();
+#endif
+
+       // Always use linearized prediction
+       linear_predict(*this, gyro_meas, accel_meas, dt);
+
+#ifdef TIME_OPS
+       double time = clock.stop()*1e6;
+       std::cout << "unscented predict time: " << time << "\n";
+#endif
+}
+

Added: paparazzi3/trunk/sw/airborne/fms/libeknav/quaternions.hpp
===================================================================
--- paparazzi3/trunk/sw/airborne/fms/libeknav/quaternions.hpp                   
        (rev 0)
+++ paparazzi3/trunk/sw/airborne/fms/libeknav/quaternions.hpp   2010-09-10 
14:26:27 UTC (rev 5849)
@@ -0,0 +1,143 @@
+#ifndef AHRS_QUATERNIONS_HPP
+#define AHRS_QUATERNIONS_HPP
+
+/*
+ * quaternions.cpp
+ *
+ *      Author: Jonathan Brandmeyer
+ *
+ *          This file is part of libeknav.
+ *
+ *  Libeknav is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3.
+ *
+ *  Libeknav is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+
+ *  You should have received a copy of the GNU General Public License
+ *  along with libeknav.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+using Eigen::Quaternion;
+
+template<typename FloatT>
+Quaternion<FloatT> operator-(const Quaternion<FloatT>& q)
+{
+       return Quaternion<FloatT>(-q.w(), -q.x(), -q.y(), -q.z());
+}
+
+/**
+ * Convert a rotation from modified Rodrigues parameters to a quaternion.
+ * @param v The multiplication of the rotation angle by the tangent of the 
angle/4
+ * @return The quaternion corresponding to that rotation.
+ */
+template<typename FloatT>
+Quaternion<FloatT> exp_r(const Eigen::Matrix<FloatT, 3, 1>& v)
+{
+       // a2 = tan^2(theta/4)
+       FloatT a2 = v.squaredNorm();
+       Quaternion<FloatT> ret;
+       // sin(theta/2) = 2*tan(theta/4) / (1 + tan^2(theta/4))
+       // v == v.normalized() * tan^2(theta/4)
+       ret.vec() = v*(2/(1+a2));
+       // cos(theta/2) = (1 - tan^2(theta/4)) / (1 + tan^2(theta/4))
+       ret.w() = (1-a2)/(1+a2);
+       return ret;
+}
+
+template<typename FloatT>
+Eigen::Matrix<FloatT, 3, 1> log_r(const Quaternion<FloatT>& q)
+{
+       // Note: This algorithm is reasonably safe when using double
+       // precision (to within 1e-10 of precision), but not float.
+       /*
+        * q.w() == cos(theta/2)
+        * q.vec() == sin(theta/2)*v_hat
+        *
+        *
+        * Normal rodrigues params:
+        * v*tan(theta/2) = v*sin(theta/2) / cos(theta/2)
+        * v*tan(theta/2) = q.vec() / q.w()
+        *
+        * Modified rodrigues params (pulls away from infinity at theta=pi)
+        * tan(theta/2) == sin(theta) / (1 + cos(theta))
+        * therefore, tan(theta/4)*v_hat = sin(theta/2)*v_hat / (1 + 
cos(theta/2))
+        */
+       return q.vec() / (1.0+q.w());
+}
+
+/**
+ * Convert an angle/axis 3-vector to a unit quaternion
+ * @param v A 3-vector whose length is between 0 and 2*pi
+ * @return The quaternion that represents the same rotation.
+ */
+template<typename FloatT>
+Quaternion<FloatT> exp(Eigen::Matrix<FloatT, 3, 1> v);
+
+template<typename FloatT>
+Quaternion<FloatT> exp(Eigen::Matrix<FloatT, 3, 1> v)
+{
+       FloatT angle = v.norm();
+       if (angle <= Eigen::machine_epsilon<FloatT>()) {
+               // std::cerr << "Warning: tiny quaternion flushed to zero\n";
+               return Quaternion<FloatT>::Identity();
+       }
+       else {
+               Quaternion<FloatT> ret;
+#if 0
+               if (angle > 1.999*M_PI) {
+                       // TODO: I really, really don't like this hack. It 
should
+                       // be impossible to compute an angular measurement 
update
+                       // with a rotation angle greater than this number...
+                       v *= 1.999*M_PI / angle;
+                       angle = 1.999*M_PI;
+               }
+#endif
+               assert(angle <= FloatT(2.0*M_PI));
+#if 0
+               // Oddly enough, this attempt to make the formula faster by 
reducing
+               // the number of trig calls actually runs slower.
+               FloatT tan_x = std::tan(angle * 0.25);
+               FloatT cos_angle = (1 - tan_x*tan_x)/(1+tan_x*tan_x);
+               FloatT sin_angle = 2*tan_x/(1+tan_x*tan_x);
+               ret.w() = cos_angle;
+               ret.vec() = (sin_angle/angle)*v;
+#else
+               ret.w() = std::cos(angle*0.5);
+               ret.vec() = (std::sin(angle*0.5)/angle)*v;
+#endif
+               return ret;
+               // return Quaternion<FloatT>(Eigen::AngleAxis<FloatT>(angle, v 
/ angle));
+       }
+}
+
+/**
+ * Convert a unit quaternion to multiplied angle/axis form.
+ * @param q A unit quaternion.  The quaternion's norm should be close to unity,
+ * but may be slightly too large or small.
+ * @return The 3-vector in the tangent space of the quaternion q.
+ */
+template<typename FloatT>
+Eigen::Matrix<FloatT, 3, 1> log(const Quaternion<FloatT>& q) 
__attribute__((noinline));
+
+template<typename FloatT>
+Eigen::Matrix<FloatT, 3, 1> log(const Quaternion<FloatT>& q)
+{
+       FloatT mag = q.vec().norm();
+       if (mag <= Eigen::machine_epsilon<FloatT>()) {
+               // Flush to zero for very small angles.  This avoids division 
by zero.
+               return Eigen::Matrix<FloatT, 3, 1>::Zero();
+       }
+       FloatT angle = 2.0*std::atan2(mag, q.w());
+       return q.vec() * (angle/mag);
+       // Eigen::AngleAxis<FloatT> res(q /*mag <= 1.0) ? q : q.normalized() 
*/);
+       // return res.axis() * res.angle();
+}
+
+#endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]