190 lines
4.5 KiB
C++
190 lines
4.5 KiB
C++
/*
|
||
* © 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 POSEDETECTION3_H
|
||
#define POSEDETECTION3_H
|
||
|
||
#include "AccelerometerData.h"
|
||
#include "GyroscopeData.h"
|
||
|
||
#include "../../data/Timestamp.h"
|
||
#include "../../math/Matrix3.h"
|
||
#include "../../math/Quaternion.h"
|
||
|
||
#include "../../geo/Point3.h"
|
||
|
||
#include "PoseDetectionPlot.h"
|
||
#include "PoseProvider.h"
|
||
|
||
|
||
class PoseDetection3 : public PoseProvider {
|
||
|
||
private:
|
||
|
||
struct {
|
||
//Eigen::Matrix3f rotationMatrix = Eigen::Matrix3f::Identity();
|
||
Matrix3 rotationMatrix = Matrix3::identity();
|
||
bool isKnown = false;
|
||
Timestamp lastEstimation;
|
||
} orientation;
|
||
|
||
#ifdef WITH_DEBUG_PLOT
|
||
PoseDetectionPlot plot;
|
||
PoseDetectionPlotAngles plotAngles;
|
||
#endif
|
||
|
||
Vector3 lastGyroReading;
|
||
Timestamp lastGyroReadingTS;
|
||
|
||
public:
|
||
|
||
/** ctor */
|
||
PoseDetection3() {
|
||
#ifdef WITH_DEBUG_PLOT
|
||
plot.setName("PoseDetection3");
|
||
#endif
|
||
}
|
||
|
||
/** get the smartphone's rotation matrix */
|
||
const Matrix3& getMatrix() const override {
|
||
return orientation.rotationMatrix;
|
||
}
|
||
|
||
/** is the pose known and stable? */
|
||
bool isKnown() const override {
|
||
return orientation.isKnown;
|
||
}
|
||
|
||
|
||
bool first = true;
|
||
|
||
Quaternion qGyro = Quaternion(1, 0,0,0);
|
||
Quaternion qGyroUpdate;
|
||
Quaternion qAccel;
|
||
Quaternion qFiltered;
|
||
|
||
void addAccelerometer(const Timestamp& ts, const AccelerometerData& acc) {
|
||
|
||
const Vector3 vec(-acc.x, -acc.y, acc.z);
|
||
const Vector3 v = vec.normalized();
|
||
|
||
|
||
const float mag = -std::acos(v.dot(Vector3(0,0,1)));//-std::acos(v.z); << same
|
||
const Vector3 n = Vector3(v.y, -v.x, 0).normalized();
|
||
qAccel = Quaternion::fromAxisAngle(mag, n.x, n.y, n.z);
|
||
|
||
//const float magY = std::atan2(acc.x, acc.z);
|
||
//const float magX = std::atan2(acc.y, acc.z);
|
||
//qAccel = Quaternion::fromAxisAngle(magX, 1, 0, 0) * Quaternion::fromAxisAngle(-magY, 0, 1, 0);
|
||
|
||
|
||
|
||
//Vector3 dv = qAccel.toEuler();
|
||
//dv.z = 0;
|
||
//std::cout << dv.x << " " << dv.y << " " << dv.z << std::endl;
|
||
|
||
// for plotting
|
||
if (first) {
|
||
qGyro = qAccel;
|
||
first = false;
|
||
}
|
||
|
||
int i = 0; (void) i;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
void addGyroscope(const Timestamp& ts, const GyroscopeData& gyro) {
|
||
|
||
Vector3 vec(gyro.x, gyro.y, gyro.z);
|
||
|
||
// ignore the first reading completely, just remember its timestamp
|
||
if (lastGyroReadingTS.isZero()) {lastGyroReadingTS = ts; return;}
|
||
|
||
// time-difference between previous and current reading
|
||
const Timestamp curDiff = ts - lastGyroReadingTS;
|
||
lastGyroReadingTS = ts;
|
||
|
||
// fast sensors might lead to delay = 0 ms. filter those values
|
||
if (curDiff.isZero()) {return;}
|
||
|
||
// current area
|
||
//const Vector3 curArea = (lastGyroReading * curDiff.sec());
|
||
|
||
|
||
// length of the rotation vector
|
||
const float theta = curDiff.sec() * lastGyroReading.norm();
|
||
const Vector3 v = lastGyroReading.normalized();
|
||
lastGyroReading = vec;
|
||
|
||
if (theta == 0) {return;}
|
||
qGyroUpdate = Quaternion::fromAxisAngle(theta, v.x, v.y, v.z);// qUpdate(cos(theta/2), v.x * sin(theta/2), v.y * sin(theta/2), v.z * sin(theta/2));
|
||
|
||
// update
|
||
qGyro = qGyroUpdate * qGyro;
|
||
|
||
|
||
|
||
update(ts);
|
||
|
||
}
|
||
|
||
Matrix3 getMatrixAcc() const {
|
||
Vector3 r = qAccel.toEuler();
|
||
return Matrix3::getRotationRad(r.x, r.y, r.z);
|
||
}
|
||
Matrix3 getMatrixGyro() const {
|
||
Vector3 r = qGyro.toEuler();
|
||
return Matrix3::getRotationRad(r.x, r.y, r.z);
|
||
}
|
||
|
||
private:
|
||
|
||
Vector3 curAngle_rad;
|
||
int cnt = 0;
|
||
|
||
void update(const Timestamp ts) {
|
||
|
||
|
||
//Quaternion qFused = qGyro;
|
||
//qFiltered = qAccel;
|
||
//Quaternion qFused = (qGyro * alpha) + (qAccel * (1-alpha));
|
||
qFiltered = Quaternion::lerp(qFiltered*qGyroUpdate, qAccel, 0.05);
|
||
//qFiltered = Quaternion::fromAxisAngle(qAccel.w*0.02, qAccel.x, qAccel.y, qAccel.z) * (qFiltered * qGyroUpdate) * 0.98;
|
||
|
||
Vector3 fused = qFiltered.toEuler();
|
||
//std::cout << fused.x <<" " << fused.y << " " << fused.z << std::endl;
|
||
|
||
// update
|
||
orientation.rotationMatrix = Matrix3::getRotationRad(fused.x, fused.y, fused.z); //getMatrixFor(cur);
|
||
|
||
|
||
orientation.isKnown = true;
|
||
orientation.lastEstimation = ts;
|
||
|
||
// debug-plot (if configured)
|
||
#ifdef WITH_DEBUG_PLOT
|
||
plot.add(ts, Vector3(0,0,0), orientation.rotationMatrix);
|
||
if (++cnt % 2 == 0) {
|
||
plotAngles.addAcc(ts, qAccel.toEuler().x, qAccel.toEuler().y);
|
||
plotAngles.addGyro(ts, qGyro.toEuler().x, qGyro.toEuler().y);
|
||
plotAngles.addFused(ts, fused.x, fused.y);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
|
||
|
||
};
|
||
|
||
#endif // POSEDETECTION3_H
|