/* * © Copyright 2014 – Urheberrechtshinweis * Alle Rechte vorbehalten / All Rights Reserved * * Programmcode ist urheberrechtlich geschuetzt. * Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner. * Keine Verwendung ohne explizite Genehmigung. * (vgl. § 106 ff UrhG / § 97 UrhG) */ #ifndef MOTIONDETECTION_H #define MOTIONDETECTION_H #include "GravityData.h" #include "LinearAccelerationData.h" #include "../../data/Timestamp.h" #include "../../math/MovingAverageTS.h" #include "../../misc/Debug.h" #include #include #include #include #include #include #include #include #include "../../Assertions.h" class MotionDetection { private: bool newAccelerationMeasurementArrived = false; bool newGravityMeasurementArrived = false; Eigen::Vector3f curGravity; Eigen::Vector3f curLinearAcceleration; //fast algo Eigen::Matrix2f sumProjectedCov = Eigen::Matrix2f::Identity(); //sum of the projection of curLinearAcceleartion into perpendicular plane of curGravity as semmetric matrix int numMeasurementsPerInterval, updateCnt; int updateInterval; //defines how often a new motion axis is calculated in milliseconds. default = 500ms struct Motion{ Eigen::Vector2f vec = Eigen::Vector2f::Identity(); Timestamp lastEstimation; }; Motion curMotion; Motion prevMotion; const char* name = "MotionDetection"; public: /** ctor */ MotionDetection(int updateInterval = 500) : updateInterval(updateInterval), numMeasurementsPerInterval(0), updateCnt(0) { ; } void addGravity(const Timestamp& ts, const GravityData& grav){ curGravity << grav.x, grav.y, grav.z; newGravityMeasurementArrived = true; updateProjectionVectorFast(ts); } void addLinearAcceleration(const Timestamp& ts, const LinearAccelerationData& acc) { curLinearAcceleration << acc.x, acc.y, acc.z; newAccelerationMeasurementArrived = true; updateProjectionVectorFast(ts); } /** return the current motion axis * NOTE: if no data is available, this vector is the Identity */ Eigen::Vector2f getCurrentMotionAxis(){ return curMotion.vec; } /** returns the radians between [-pi, pi] between successive motion axis estimations */ float getMotionChangeInRad(){ //TODO: put this in an EigenHelper Class within geo const float crossProduct = curMotion.vec.x() * prevMotion.vec.y() - curMotion.vec.y() * prevMotion.vec.x(); const float ang = (crossProduct < 0 ? 1:-1) * std::acos(std::min(std::max(curMotion.vec.dot(prevMotion.vec) / curMotion.vec.norm() * prevMotion.vec.norm(), -1.0f), 1.0f)); //nan? if(std::isnan(ang)){ Log::add(name, "The motion change angle is nAn, this is not correct!"); } if(updateCnt < 2){ return 0; } return ang; } private: FRIEND_TEST(MotionDetection, motionAngle); FRIEND_TEST(MotionDetection, motionAxis); Eigen::Vector2f updateMotionAxis(Eigen::Matrix2f covarianceMatrix){ Eigen::SelfAdjointEigenSolver solver(covarianceMatrix); return solver.eigenvectors().col(1); //returns the eigenvector corresponding to the biggest eigenvalue } void updateProjectionVectorFast(const Timestamp& ts){ //make sure we have both measurements for calculation if(newGravityMeasurementArrived && newAccelerationMeasurementArrived){ numMeasurementsPerInterval++; //project acc into perpendicular plane of grav (using standard vector projection) Eigen::Vector3f proj = (curLinearAcceleration.dot(curGravity) / curGravity.dot(curGravity)) * curGravity; //if the acc vector is perpendicular to the gravity vector, the dot product results in 0, therefore, we need to do this if(proj.isZero()){ proj = curLinearAcceleration; Log::add(name, "The LinearAcceleration vector is perpendicular to the gravity, is this correct?"); } //we are only interested in x,y Eigen::Vector2f vec; vec << proj.x(), proj.y(); // sum this up for later averaging. sumProjectedCov += vec*vec.transpose(); // start with the first available timestamp if (curMotion.lastEstimation.isZero()) {curMotion.lastEstimation = ts;} //update the motion axis depending on the update interval if(ts - curMotion.lastEstimation > Timestamp::fromMS(updateInterval)){ prevMotion = curMotion; //calculate the average of the coveriance matrix Eigen::Matrix2f Q = sumProjectedCov / numMeasurementsPerInterval; curMotion.vec = updateMotionAxis(Q); curMotion.lastEstimation = ts; reset(); } newGravityMeasurementArrived = false; newAccelerationMeasurementArrived = false; } //do nothing } void reset(){ numMeasurementsPerInterval = 0; sumProjectedCov = Eigen::Matrix2f::Zero(); ++updateCnt; } }; #endif // MOTIONDETECTION_H