This repository has been archived on 2020-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Indoor/sensors/imu/MotionDetection.h
2018-10-25 11:50:12 +02:00

174 lines
5.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* © 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 <eigen3/Eigen/Dense>
#include <cmath>
#include <vector>
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotSplot.h>
#include <KLib/misc/gnuplot/GnuplotSplotElementLines.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
#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<Eigen::Matrix2f> 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