| |
| |
| |
| |
| |
| |
| |
|
|
| #pragma once |
|
|
| #include <math.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <stdio.h> |
|
|
| template <typename T> |
| void print_arr(T *arr, int m, int n, const char *name = "arr") { |
| |
| #if EI_LOG_LEVEL == 5 |
| ei_printf("%s:\n", name); |
| for (int i = 0; i < m; i++) { |
| for (int j = 0; j < n; j++) { |
| ei_printf("[%d][%d] = %g ", i, j, arr[i * n + j]); |
| } |
| ei_printf("\n"); |
| } |
| ei_printf("\n"); |
| #endif |
| } |
|
|
| class TinyEKF { |
| public: |
| TinyEKF(const float* x0, uint32_t EKF_N, uint32_t EKF_M, |
| float dt = 0.1, |
| float *u = nullptr, |
| float process_noise_scale = 0.1, |
| float observation_noise_scale=0.1) |
| { |
| |
| this->EKF_N = EKF_N; |
| this->EKF_M = EKF_M; |
| this->dt = dt; |
|
|
| x = new float[this->EKF_N]; |
| memset(x, 0, sizeof(float) * this->EKF_N); |
| |
| x[0] = x0[0]; |
| x[1] = x0[1]; |
| x[2] = x0[0]; |
| x[3] = x0[1]; |
|
|
| |
| print_arr(x, 1, this->EKF_N, "init x"); |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| F = new float[16]; |
| memset(F, 0, sizeof(float) * 16); |
| for (int i = 0; i < 4; ++i) { |
| for (int j = 0; j < 4; ++j) { |
| F[i * 4 + j] = (i == j) ? 1 : 0; |
| } |
| } |
| F[2] = F[7] = this->dt; |
|
|
| |
| print_arr(F, 4, 4, "init F"); |
|
|
| |
| H = new float[8]; |
| memset(H, 0, sizeof(float) * 8); |
|
|
| H[0] = H[5] = 1; |
|
|
| |
| print_arr(H, 2, 4, "init H"); |
|
|
| |
| Q = new float[16]; |
| memset(Q, 0, sizeof(float) * 16); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| Q[0] = Q[5] = pow(dt, 4) / 4; |
| Q[2] = Q[7] = Q[8] = Q[13] = pow(dt, 3) / 2; |
| Q[10] = Q[15] = pow(dt, 2); |
|
|
| for (int i = 0; i < 4; ++i) { |
| for (int j = 0; j < 4; ++j) { |
| Q[i * 4 + j] = Q[i * 4 + j] * pow(process_noise_scale, 2); |
| } |
| } |
|
|
| |
| print_arr(Q, 4, 4, "init Q"); |
|
|
| |
| R = new float[4]; |
| memset(R, 0, sizeof(float) * 4); |
|
|
| for (int i = 0; i < 2; ++i) { |
| for (int j = 0; j < 2; ++j) { |
| R[i * 2 + j] = (i == j) ? (pow(observation_noise_scale, 2)) : 0; |
| } |
| } |
| |
| print_arr(R, 2, 2, "init R"); |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| B = new float[this->EKF_N * this->EKF_N * 2]; |
| memset(B, 0, sizeof(float) * this->EKF_N * this->EKF_N * 2); |
| B[0] = B[3] = (dt * dt) / 2; |
| B[4] = B[7] = dt; |
|
|
| if (u == nullptr) { |
| u = new float[2]; |
| u[0] = u[1] = 0.1; |
| } |
| this->u = u; |
|
|
| |
| P = new float[16]; |
| memset(P, 0, sizeof(float) * 16); |
|
|
| for (int i = 0; i < 4; ++i) { |
| for (int j = 0; j < 4; ++j) { |
| P[i * 4 + j] = (i == j) ? 1 : 0; |
| } |
| } |
|
|
| |
| print_arr(P, 4, 4, "init P"); |
| } |
|
|
| ~TinyEKF() { |
| delete[] x; |
| delete[] P; |
| delete[] Q; |
| delete[] F; |
| delete[] H; |
| delete[] R; |
| delete[] B; |
| delete[] u; |
| } |
|
|
| void predict(const float *fx); |
| bool update(const float *z, const float *hx); |
| float *x; |
| private: |
| uint32_t EKF_N; |
| uint32_t EKF_M; |
|
|
| float *P; |
| float *Q; |
| float *F; |
| float *H; |
| float *R; |
|
|
| float *B; |
| float *u; |
| float dt; |
|
|
| void update_step3(float *GH); |
|
|
| |
| static void _mulmat( |
| const float * a, |
| const float * b, |
| float * c, |
| const int arows, |
| const int acols, |
| const int bcols) |
| { |
| for (int i=0; i<arows; ++i) { |
| for (int j=0; j<bcols; ++j) { |
| c[i*bcols+j] = 0; |
| for (int k=0; k<acols; ++k) { |
| c[i*bcols+j] += a[i*acols+k] * b[k*bcols+j]; |
| } |
| } |
| } |
| } |
|
|
| |
| static void _mulvec( |
| const float * a, |
| const float * x, |
| float * y, |
| const int m, |
| const int n) |
| { |
| for (int i=0; i<m; ++i) { |
| y[i] = 0; |
| for (int j=0; j<n; ++j) |
| y[i] += x[j] * a[i*n+j]; |
| } |
| } |
|
|
| |
| static void _transpose( |
| const float * a, float * at, const int m, const int n) |
| { |
| for (int i=0; i<m; ++i) |
| for (int j=0; j<n; ++j) { |
| at[j*m+i] = a[i*n+j]; |
| } |
| } |
|
|
| |
| static void _addmat( |
| const float * a, const float * b, float * c, |
| const int m, const int n) |
| { |
| for (int i=0; i<m; ++i) { |
| for (int j=0; j<n; ++j) { |
| c[i*n+j] = a[i*n+j] + b[i*n+j]; |
| } |
| } |
| } |
|
|
| |
| static void _negate(float * a, const int m, const int n) |
| { |
| for (int i=0; i<m; ++i) { |
| for (int j=0; j<n; ++j) { |
| a[i*n+j] = -a[i*n+j]; |
| } |
| } |
| } |
|
|
| |
| static void _addeye(float * a, const int n) |
| { |
| for (int i=0; i<n; ++i) { |
| a[i*n+i] += 1; |
| } |
| } |
|
|
| |
| |
|
|
| |
| static int _choldc1(float * a, float * p, const int n) |
| { |
| for (int i = 0; i < n; i++) { |
| for (int j = i; j < n; j++) { |
| float sum = a[i*n+j]; |
| for (int k = i - 1; k >= 0; k--) { |
| sum -= a[i*n+k] * a[j*n+k]; |
| } |
| if (i == j) { |
| if (sum <= 0) { |
| return 1; |
| } |
| p[i] = sqrt(sum); |
| } |
| else { |
| a[j*n+i] = sum / p[i]; |
| } |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| |
| static int _choldcsl(const float * A, float * a, float * p, const int n) |
| { |
| for (int i = 0; i < n; i++) { |
| for (int j = 0; j < n; j++) { |
| a[i*n+j] = A[i*n+j]; |
| } |
| } |
| if (_choldc1(a, p, n)) { |
| return 1; |
| } |
| for (int i = 0; i < n; i++) { |
| a[i*n+i] = 1 / p[i]; |
| for (int j = i + 1; j < n; j++) { |
| float sum = 0; |
| for (int k = i; k < j; k++) { |
| sum -= a[j*n+k] * a[k*n+i]; |
| } |
| a[j*n+i] = sum / p[j]; |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| |
| static int _cholsl(const float * A, float * a, float * p, const int n) |
| { |
| if (_choldcsl(A,a,p,n)) { |
| return 1; |
| } |
|
|
| for (int i = 0; i < n; i++) { |
| for (int j = i + 1; j < n; j++) { |
| a[i*n+j] = 0.0; |
| } |
| } |
| for (int i = 0; i < n; i++) { |
| a[i*n+i] *= a[i*n+i]; |
| for (int k = i + 1; k < n; k++) { |
| a[i*n+i] += a[k*n+i] * a[k*n+i]; |
| } |
| for (int j = i + 1; j < n; j++) { |
| for (int k = j; k < n; k++) { |
| a[i*n+j] += a[k*n+i] * a[k*n+j]; |
| } |
| } |
| } |
| for (int i = 0; i < n; i++) { |
| for (int j = 0; j < i; j++) { |
| a[i*n+j] = a[j*n+i]; |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| |
| static void _addvec( |
| const float * a, const float * b, float * c, const int n) |
| { |
| for (int j=0; j<n; ++j) { |
| c[j] = a[j] + b[j]; |
| } |
| } |
|
|
| |
| static void _sub( |
| const float * a, const float * b, float * c, const int n) |
| { |
| for (int j=0; j<n; ++j) { |
| c[j] = a[j] - b[j]; |
| } |
| } |
|
|
| |
| static bool invert(const float * a, float * ainv, uint32_t EKF_M) |
| { |
| float tmp[EKF_M]; |
|
|
| return _cholsl(a, ainv, tmp, EKF_M) == 0; |
| } |
| }; |
|
|
| void TinyEKF::predict(const float *fx) { |
|
|
| |
|
|
| float Bu[4]; |
| _mulmat(this->B, this->u, Bu, 4, 2, 1); |
|
|
| |
| print_arr(Bu, 4, 1, "Bu"); |
|
|
| |
| print_arr(this->x, 1, 4, "x before"); |
|
|
| float Fx[8]; |
| _mulmat(this->F, this->x, Fx, 4, 4, 2); |
|
|
| |
| print_arr(Fx, 4, 2, "Fx"); |
| |
| print_arr(this->x, 1, 4, "x after"); |
|
|
| |
| this->x[0] = Fx[0] + Bu[0]; |
| this->x[1] = Fx[1] + Bu[1]; |
| this->x[2] = Fx[2] + Bu[0]; |
| this->x[3] = Fx[3] + Bu[1]; |
| this->x[4] = Fx[4] + Bu[2]; |
| this->x[5] = Fx[5] + Bu[3]; |
| this->x[6] = Fx[6] + Bu[2]; |
| this->x[7] = Fx[7] + Bu[3]; |
|
|
| |
| |
|
|
| |
| float FP[16]; |
| _mulmat(F, P, FP, 4, 4, 4); |
| |
| print_arr(FP, 4, 4, "FP"); |
|
|
| |
| float Ft[16]; |
| _transpose(F, Ft, 4, 4); |
|
|
| |
| float FPFt[16]; |
| _mulmat(FP, Ft, FPFt, 4, 4, 4); |
|
|
| |
| _addmat(FPFt, Q, P, 4, 4); |
|
|
| |
| print_arr(P, 4, 4, "P"); |
| } |
|
|
| bool TinyEKF::update(const float *z, const float *hx) { |
|
|
| float Ht[8]; |
| _transpose(H, Ht, 2, 4); |
|
|
| |
| print_arr(Ht, 4, 2, "Ht"); |
|
|
| float PHt[8]; |
| _mulmat(P, Ht, PHt, 4, 4, 2); |
|
|
| float HP[8]; |
| _mulmat(H, P, HP, 2, 4, 4); |
|
|
| float HpHt[4]; |
| _mulmat(HP, Ht, HpHt, 2, 4, 2); |
|
|
| float HpHtR[4]; |
| _addmat(HpHt, R, HpHtR, 2, 2); |
|
|
| float HPHtRinv[4]; |
| if (!invert(HpHtR, HPHtRinv, 2)) { |
| return false; |
| } |
|
|
| float G[8]; |
| _mulmat(PHt, HPHtRinv, G, 4, 2, 2); |
|
|
| |
| print_arr(G, 4, 2, "G"); |
|
|
| |
| print_arr(this->x, 1, 4, "x in update"); |
|
|
| |
| float z_hx[4]; |
| |
| z_hx[0] = z[0] - hx[0]; |
| z_hx[1] = z[1] - hx[1]; |
| z_hx[2] = z[0] - hx[0]; |
| z_hx[3] = z[1] - hx[1]; |
|
|
| |
| print_arr(z_hx, 2, 2, "z_hx"); |
|
|
| float Gz_hx[8]; |
| _mulmat(G, z_hx, Gz_hx, 4, 2, 2); |
|
|
| |
| print_arr(Gz_hx, 4, 2, "Gz_hx"); |
|
|
| _addvec(this->x, Gz_hx, this->x, 8); |
|
|
| float GH[16]; |
| _mulmat(G, H, GH, 4, 2, 4); |
| update_step3(GH); |
| return true; |
| } |
|
|
| |
| void TinyEKF::update_step3(float *GH) |
| { |
| _negate(GH, 4, 4); |
| _addeye(GH, 4); |
|
|
| float GHP[16]; |
| _mulmat(GH, P, GHP, 4, 4, 4); |
| memcpy(P, GHP, 16 * sizeof(float)); |
| } |