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/PoseDetection2.h
frank 857d7a1553 fixed some issues
added new pose/turn detections
new helper classes
define-flags for libEigen
2018-09-04 10:49:00 +02:00

302 lines
9.1 KiB
C++

#ifndef POSEDETECTION2_H
#define POSEDETECTION2_H
#include "AccelerometerData.h"
#include "GyroscopeData.h"
#include "../../data/Timestamp.h"
//#include "../../math/MovingAverageTS.h"
//#include "../../math/MovingMedianTS.h"
#include "../../math/Matrix3.h"
#include "../../math/filter/Complementary.h"
#include "../../geo/Point3.h"
//#include "../../math/FixedFrequencyInterpolator.h"
#include "PoseDetectionPlot.h"
#include "PoseProvider.h"
/**
* estimate the smartphones world-pose,
* based on the accelerometer's data
*
* https://robotics.stackexchange.com/questions/6953/how-to-calculate-euler-angles-from-gyroscope-output
*/
class PoseDetection2 : public PoseProvider {
private:
struct {
//Eigen::Matrix3f rotationMatrix = Eigen::Matrix3f::Identity();
Matrix3 rotationMatrix = Matrix3::identity();
bool isKnown = false;
Timestamp lastEstimation;
} orientation;
/** how the pose is estimated */
//LongTermMovingAverage est = LongTermMovingAverage(Timestamp::fromMS(1250));
//EstMovingAverage est = EstMovingAverage(Timestamp::fromMS(450));
//EstMovingMedian est = EstMovingMedian(Timestamp::fromMS(300));
#ifdef WITH_DEBUG_PLOT
PoseDetectionPlot plot;
PoseDetectionPlotAngles plotAngles;
#endif
Vector3 lastGyroReading;
Timestamp lastGyroReadingTS;
//FixedFrequencyInterpolator<Vector3> interpolAcc;
//FixedFrequencyInterpolator<Vector3> interpolGyro;
Filter::Complementary<Vector3> filter;
//std::vector<Vector3> accBuf;
//std::vector<Vector3> gyroBuf;
Vector3 curAccel;
bool firstAccel = true;
Vector3 curAccel_rad;
Vector3 curGyroSum_rad;
//Matrix3 curGyroMatrix = Matrix3::identity();
static constexpr float splitFreq = 2.5;
static constexpr float sRate = 50;
static constexpr int sRateTS = 1000/sRate;
public:
/** ctor */
//PoseDetection2() : interpolAcc(Timestamp::fromMS(sRateTS)), interpolGyro(Timestamp::fromMS(sRateTS)), filter(splitFreq, sRate) {
PoseDetection2() : filter(1, 2.5) {
#ifdef WITH_DEBUG_PLOT
plot.setName("PoseDetection2");
#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;
}
float angleX_old_rad = 0;
float angleY_old_rad = 0;
void addAccelerometer(const Timestamp& ts, const AccelerometerData& acc) {
// https://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/#docs
float angleX_rad = std::atan2( acc.y, acc.z);
float angleY_rad = std::atan2(-acc.x, std::sqrt(acc.y*acc.y + acc.z*acc.z));
float angleZ_rad = 0;
if (std::abs(angleX_rad-angleX_old_rad) > M_PI) {angleX_rad += 2*M_PI;}
if (std::abs(angleY_rad-angleY_old_rad) > M_PI) {angleY_rad += 2*M_PI;}
angleX_old_rad = angleX_rad;
angleY_old_rad = angleY_rad;
// float angleX_rad = std::atan2(acc.y, std::sqrt(acc.x*acc.x + acc.z*acc.z));
// float angleY_rad = std::atan2(-acc.x, std::sqrt(acc.y*acc.y + acc.z*acc.z));
// float angleZ_rad = 0;
// Point2 ref(0,1);
// Point2 xz( acc.x, acc.z);
// Point2 yz(-acc.y, acc.z);
// float angleY_rad = std::atan2( determinant(ref,xz), dot(ref,xz) );
// float angleX_rad = std::atan2( determinant(ref,yz), dot(ref,yz) );
// float angleZ_rad = 0.0;
// float angleX_rad = std::atan2(acc.y, acc.z);
// float angleY_rad = -std::atan2(acc.x, acc.z);
// float angleZ_rad = 0;//-std::atan2(acc.y, acc.x);
// float angleX2_rad = (angleX1_rad > 0) ? (angleX1_rad - 2*M_PI) : (angleX1_rad + 2*M_PI);
// float angleY2_rad = (angleY1_rad > 0) ? (angleY1_rad - 2*M_PI) : (angleY1_rad + 2*M_PI);
// float angleX_rad = (std::abs(curAngle_rad.x-angleX1_rad) < std::abs(curAccel_rad.x-angleX2_rad)) ? (angleX1_rad) : (angleX2_rad);
// float angleY_rad = (std::abs(curAngle_rad.y-angleY1_rad) < std::abs(curAccel_rad.y-angleY2_rad)) ? (angleY1_rad) : (angleY2_rad);
curAccel = Vector3(acc.x, acc.y, acc.z);
curAccel_rad = Vector3(angleX_rad, angleY_rad, angleZ_rad);
//curAccel_rad = Matrix3::getRotationRad(0,0,-angleZ_rad) * Vector3(angleX_rad, angleY_rad, angleZ_rad);
if (firstAccel) {
curGyroSum_rad = curAccel_rad;
firstAccel = false;
}
}
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());
// // update sum
// curAngle_rad += curArea;
// curGyroSum_rad += curArea;
// // PAPER
const float dx = 1 * lastGyroReading.x + std::sin(curGyroSum_rad.x)*std::sin(curGyroSum_rad.y)/std::cos(curGyroSum_rad.y)*lastGyroReading.y + std::cos(curGyroSum_rad.x)*std::sin(curGyroSum_rad.y)/std::cos(curGyroSum_rad.y)*lastGyroReading.z;
const float dy = std::cos(curGyroSum_rad.x)*lastGyroReading.y + -std::sin(curGyroSum_rad.x)*lastGyroReading.z;
const float dz = std::sin(curGyroSum_rad.x)/std::cos(curGyroSum_rad.y)*lastGyroReading.y + std::cos(curGyroSum_rad.x)/std::cos(curGyroSum_rad.y)*lastGyroReading.z;
curGyroSum_rad.x += dx*curDiff.sec();
curGyroSum_rad.y += dy*curDiff.sec();
curGyroSum_rad.z += dz*curDiff.sec();
// // PAPER
// const Vector3 n = lastGyroReading / lastGyroReading.norm();
// const float mag = lastGyroReading.norm() * curDiff.sec();
// if (mag != 0) {
// curGyroMatrix = Matrix3::getRotationVec(n.x, n.y, n.z, mag).transposed() * curGyroMatrix;
// }
// DEBUG PLOT
// update old reading
lastGyroReading = vec;
update(ts);
}
Matrix3 getMatrixGyro() const {
return Matrix3::getRotationRad(curGyroSum_rad.x, curGyroSum_rad.y, curGyroSum_rad.z);
}
Matrix3 getMatrixAcc() const {
return Matrix3::getRotationRad(curAccel_rad.x, curAccel_rad.y, curAccel_rad.z);
}
private:
Vector3 curAngle_rad;
int cnt = 0;
void update(const Timestamp ts) {
// complementary filter x = alpha * x + (1-alpha) * y;
const float alpha = 0.98f;
//curAngle_rad = curAngle_rad*alpha + curAccel_rad*(1-alpha);
//curAngle_rad = curAccel_rad;
//curAngle_rad = curGyroSum_rad;
//std::cout << curGyroSum_rad.x <<" " << curGyroSum_rad.y << " " << curGyroSum_rad.z << std::endl;
//curAngle_rad = filter.get();
filter.addSlow(ts, curAccel_rad);
filter.addFast(ts, curGyroSum_rad);
static Vector3 fused;
static Vector3 pref;
Vector3 diff = curGyroSum_rad - pref;
fused = (fused+diff) * alpha + curAccel_rad * (1-alpha);
pref = curGyroSum_rad;
// update
//orientation.rotationMatrix = Matrix3::getRotationRad(curAccel_rad.x, curAccel_rad.y, curAccel_rad.z); //getMatrixFor(cur);
//orientation.rotationMatrix = Matrix3::getRotationRad(curGyroSum_rad.x, curGyroSum_rad.y, curGyroSum_rad.z); //getMatrixFor(cur);
//orientation.rotationMatrix = curGyroMatrix;
//orientation.rotationMatrix = Matrix3::getRotationRadZ(curGyroSum_rad.z) * Matrix3::getRotationRadX(curGyroSum_rad.x) * Matrix3::getRotationRadY(curGyroSum_rad.y);
//orientation.rotationMatrix = Matrix3::getRotationRad(curGyroSum_rad.x, curGyroSum_rad.y, curGyroSum_rad.z);
orientation.rotationMatrix = Matrix3::getRotationRad(fused.x, fused.y, 0); //getMatrixFor(cur);
orientation.isKnown = true;
orientation.lastEstimation = ts;
// debug-plot (if configured)
#ifdef WITH_DEBUG_PLOT
plot.add(ts, curAccel, orientation.rotationMatrix);
if (++cnt % 2 == 0) {
plotAngles.addAcc(ts, curAccel_rad.x, curAccel_rad.y);
plotAngles.addGyro(ts, curGyroSum_rad.x, curGyroSum_rad.y);
plotAngles.addFused(ts, fused.x, fused.y);
}
#endif
}
// /** get the current rotation matrix estimation */
// Matrix3 getMatrixFor(const Vector3 cur) const {
// // rotate average-accelerometer into (0,0,1)
// //Eigen::Vector3f zAxis; zAxis << 0, 0, 1;
// const Vector3 zAxis(0,0,1);
// const Matrix3 rotMat = getRotationMatrix(cur.normalized(), zAxis);
// //const Matrix3 rotMat = getRotationMatrix(zAxis, avg.normalized()); // INVERSE
// //const Eigen::Matrix3f rotMat = getRotationMatrix(avg.normalized(), zAxis);
// // just a small sanity check. after applying to rotation the acc-average should become (0,0,1)
// //Eigen::Vector3f aligned = (rotMat * avg).normalized();
// const Vector3 aligned = (rotMat * cur).normalized();
// Assert::isTrue((aligned-zAxis).norm() < 0.1f, "deviation too high");
// return rotMat;
// }
// /** get a matrix that rotates the vector "from" into the vector "to" */
// static Matrix3 getRotationMatrix(const Vector3& from, const Vector3 to) {
// // http://math.stackexchange.com/questions/293116/rotating-one-3d-vector-to-another
// const Vector3 v = from.cross(to) / from.cross(to).norm();
// const float angle = std::acos( from.dot(to) / from.norm() / to.norm() );
// Matrix3 A({
// 0.0f, -v.z, v.y,
// v.z, 0.0f, -v.x,
// -v.y, v.x, 0.0f
// });
// return Matrix3::identity() + (A * std::sin(angle)) + ((A*A) * (1-std::cos(angle)));
// }
};
#endif // POSEDETECTION2_H