project_inertial-control/processing/the_main_block.cpp

338 lines
12 KiB
C++
Raw 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.

#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <cmath>
#include <unistd.h>
#include "sqlite3.h"
using namespace std;
static const double G_MS2 = 9.81; // g -> м/с²
static const double DEG2RAD = M_PI / 180.0;
class Kalman1D {
private:
double x; // положение
double v; // скорость
double P[2][2]; // ковариационная матрица 2x2
double Q; // интенсивность шума процесса (ускорение)
double R; // дисперсия шума измерения
double dt; // временной шаг
public:
Kalman1D(double q_accel, double r_measure, double init_x = 0.0, double init_v = 0.0, double dt0 = 0.01)
: Q(q_accel), R(r_measure), dt(dt0), x(init_x), v(init_v)
{
P[0][0] = 1.0;
P[0][1] = 0.0;
P[1][0] = 0.0;
P[1][1] = 100.0;
}
void setDt(double new_dt) { dt = new_dt; }
void predict() {
double new_x = x + v * dt;
double new_v = v;
double P00 = P[0][0] + dt * (P[1][0] + P[0][1]) + dt * dt * P[1][1];
double P01 = P[0][1] + dt * P[1][1];
double P10 = P[1][0] + dt * P[1][1];
double P11 = P[1][1];
double Qv = Q * dt;
double Qp = Q * dt * dt * dt / 3.0;
double Qpv = Q * dt * dt / 2.0;
P00 += Qp;
P01 += Qpv;
P10 += Qpv;
P11 += Qv;
P[0][0] = P00; P[0][1] = P01;
P[1][0] = P10; P[1][1] = P11;
x = new_x;
v = new_v;
}
double update(double measurement) {
double y = measurement - x;
double S = P[0][0] + R;
double K_pos = P[0][0] / S;
double K_vel = P[1][0] / S;
x += K_pos * y;
v += K_vel * y;
double P00_new = (1.0 - K_pos) * P[0][0];
double P01_new = (1.0 - K_pos) * P[0][1];
double P11_new = P[1][1] - K_vel * P[1][0];
P[0][0] = P00_new; P[0][1] = P01_new;
P[1][0] = P01_new; P[1][1] = P11_new;
return x;
}
double getState() const { return x; }
double getVelocity() const { return v; }
};
// ─────────────────────────────────────────────
// Структуры
// ─────────────────────────────────────────────
struct AccSample {
double timestamp;
double ax, ay, az;
};
struct GyroSample {
double timestamp;
double gx, gy, gz;
};
// ─────────────────────────────────────────────
// Чтение из БД
// ─────────────────────────────────────────────
// Читаем последний timestamp и последнее состояние навигации из trajectory
double readLastTrajectoryState(sqlite3* db,
double& px, double& py, double& pz,
double& vx, double& vy, double& vz,
double& roll, double& pitch, double& yaw)
{
double last_ts = 0.0;
sqlite3_stmt* stmt = nullptr;
if (sqlite3_prepare_v2(db,
"SELECT timestamp, x, y, z, vx, vy, vz, roll, pitch, yaw "
"FROM trajectory ORDER BY timestamp DESC LIMIT 1",
-1, &stmt, nullptr) == SQLITE_OK)
{
if (sqlite3_step(stmt) == SQLITE_ROW) {
last_ts = sqlite3_column_double(stmt, 0);
px = sqlite3_column_double(stmt, 1);
py = sqlite3_column_double(stmt, 2);
pz = sqlite3_column_double(stmt, 3);
vx = sqlite3_column_double(stmt, 4);
vy = sqlite3_column_double(stmt, 5);
vz = sqlite3_column_double(stmt, 6);
roll = sqlite3_column_double(stmt, 7);
pitch = sqlite3_column_double(stmt, 8);
yaw = sqlite3_column_double(stmt, 9);
}
}
sqlite3_finalize(stmt);
return last_ts;
}
vector<AccSample> readAcc(sqlite3* db, double last_timestamp) {
vector<AccSample> data;
sqlite3_stmt* stmt = nullptr;
sqlite3_prepare_v2(db,
"SELECT timestamp, ax, ay, az "
"FROM filtered_acc_data WHERE timestamp > ? ORDER BY timestamp ASC",
-1, &stmt, nullptr);
sqlite3_bind_double(stmt, 1, last_timestamp);
while (sqlite3_step(stmt) == SQLITE_ROW) {
AccSample s;
s.timestamp = sqlite3_column_double(stmt, 0);
s.ax = sqlite3_column_double(stmt, 1);
s.ay = sqlite3_column_double(stmt, 2);
s.az = sqlite3_column_double(stmt, 3);
data.push_back(s);
}
sqlite3_finalize(stmt);
return data;
}
vector<GyroSample> readGyro(sqlite3* db, double last_timestamp) {
vector<GyroSample> data;
sqlite3_stmt* stmt = nullptr;
sqlite3_prepare_v2(db,
"SELECT timestamp, gx, gy, gz "
"FROM filtered_gyro_data WHERE timestamp > ? ORDER BY timestamp ASC",
-1, &stmt, nullptr);
sqlite3_bind_double(stmt, 1, last_timestamp);
while (sqlite3_step(stmt) == SQLITE_ROW) {
GyroSample s;
s.timestamp = sqlite3_column_double(stmt, 0);
s.gx = sqlite3_column_double(stmt, 1);
s.gy = sqlite3_column_double(stmt, 2);
s.gz = sqlite3_column_double(stmt, 3);
data.push_back(s);
}
sqlite3_finalize(stmt);
return data;
}
// ─────────────────────────────────────────────
// Запись в БД
// ─────────────────────────────────────────────
void createTable(sqlite3* db) {
char* err = nullptr;
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS trajectory ("
"timestamp REAL, "
"x REAL, y REAL, z REAL, "
"vx REAL, vy REAL, vz REAL, "
"roll REAL, pitch REAL, yaw REAL)",
nullptr, nullptr, &err);
}
void writePoint(sqlite3_stmt* stmt,
double ts,
double x, double y, double z,
double vx, double vy, double vz,
double roll, double pitch, double yaw)
{
sqlite3_reset(stmt);
sqlite3_bind_double(stmt, 1, ts);
sqlite3_bind_double(stmt, 2, x);
sqlite3_bind_double(stmt, 3, y);
sqlite3_bind_double(stmt, 4, z);
sqlite3_bind_double(stmt, 5, vx);
sqlite3_bind_double(stmt, 6, vy);
sqlite3_bind_double(stmt, 7, vz);
sqlite3_bind_double(stmt, 8, roll);
sqlite3_bind_double(stmt, 9, pitch);
sqlite3_bind_double(stmt, 10, yaw);
sqlite3_step(stmt);
}
// ─────────────────────────────────────────────
// main
// ─────────────────────────────────────────────
int main()
{
sqlite3* db = nullptr;
if (sqlite3_open("/home/grant/inertial_control/inertial_data.db", &db) != SQLITE_OK) {
cerr << "Не удалось открыть БД" << endl;
return 1;
}
sqlite3_busy_timeout(db, 5000);
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", nullptr, nullptr, nullptr);
createTable(db);
// Начальные условия — читаем из последней точки trajectory
double px = 0.0, py = 0.0, pz = 0.0;
double vx = 0.0, vy = 0.0, vz = 0.0;
double roll = 0.0, pitch = 0.0, yaw = 0.0;
double last_ts = readLastTrajectoryState(db, px, py, pz, vx, vy, vz, roll, pitch, yaw);
// Инициализация фильтров Калмана один раз при старте
Kalman1D kalmanAx(0.01, 0.1, 0.0, 0.0);
Kalman1D kalmanAy(0.01, 0.1, 0.0, 0.0);
Kalman1D kalmanAz(0.01, 0.1, 0.0, 0.0);
Kalman1D kalmanGx(0.001, 0.01, 0.0, 0.0);
Kalman1D kalmanGy(0.001, 0.01, 0.0, 0.0);
Kalman1D kalmanGz(0.001, 0.01, 0.0, 0.0);
sqlite3_stmt* ins = nullptr;
sqlite3_prepare_v2(db,
"INSERT INTO trajectory "
"(timestamp,x,y,z,vx,vy,vz,roll,pitch,yaw) "
"VALUES (?,?,?,?,?,?,?,?,?,?)",
-1, &ins, nullptr);
// Бесконечный цикл — читаем только новые данные каждые 3 секунды
while (true) {
last_ts = readLastTrajectoryState(db, px, py, pz, vx, vy, vz, roll, pitch, yaw);
auto accData = readAcc(db, last_ts);
auto gyroData = readGyro(db, last_ts);
if (accData.empty() || gyroData.empty()) {
sleep(3);
continue;
}
size_t N = min(accData.size(), gyroData.size());
cout << "Новых: acc=" << accData.size() << " gyro=" << gyroData.size() << endl;
sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr);
size_t written = 0;
for (size_t i = 1; i < N; i++) {
double dt = 0.01;
if (dt <= 0.0 || dt > 0.5) dt = 0.01;
kalmanAx.setDt(dt);
kalmanAy.setDt(dt);
kalmanAz.setDt(dt);
kalmanGx.setDt(dt);
kalmanGy.setDt(dt);
kalmanGz.setDt(dt);
// Предсказание и обновление для акселерометра
kalmanAx.predict(); kalmanAx.update(accData[i].ax);
kalmanAy.predict(); kalmanAy.update(accData[i].ay);
kalmanAz.predict(); kalmanAz.update(accData[i].az);
// Предсказание и обновление для гироскопа
kalmanGx.predict(); kalmanGx.update(gyroData[i].gx);
kalmanGy.predict(); kalmanGy.update(gyroData[i].gy);
kalmanGz.predict(); kalmanGz.update(gyroData[i].gz);
// Получаем отфильтрованные значения
double ax_f = kalmanAx.getState();
double ay_f = kalmanAy.getState();
double az_f = kalmanAz.getState();
double gx_f = kalmanGx.getState();
double gy_f = kalmanGy.getState();
double gz_f = kalmanGz.getState();
// Перевод в м/с²
double ax_ms2 = ax_f * G_MS2;
double ay_ms2 = ay_f * G_MS2;
double az_ms2 = az_f * G_MS2;
// Интегрирование углов (используем отфильтрованные угловые скорости)
roll += gx_f * dt;
pitch += gy_f * dt;
yaw += gz_f * dt;
double Gx = G_MS2 * sin(pitch * DEG2RAD);
double Gy = G_MS2 * cos(pitch * DEG2RAD) * sin(roll * DEG2RAD) + fmod(sin(yaw * DEG2RAD) * G_MS2, sqrt(2));
double Gz = G_MS2 * cos(pitch * DEG2RAD) * cos(roll * DEG2RAD) - fmod(sin(yaw * DEG2RAD) * G_MS2, sqrt(2));
ax_ms2 -= Gx;
ay_ms2 += Gy;
az_ms2 -= Gz;
// Интегрирование скорости: v += a * dt
vx += ax_ms2 * dt;
vy += ay_ms2 * dt;
vz += az_ms2 * dt;
// Интегрирование позиции: p += v * dt
px += vx * dt;
py += vy * dt;
pz += vz * dt;
// Запись
writePoint(ins,
accData[i].timestamp,
px, py, pz,
vx, vy, vz,
roll, pitch, yaw);
written++;
}
sqlite3_exec(db, "COMMIT;", nullptr, nullptr, nullptr);
cout << "Готово. Записано " << written << " точек в trajectory." << endl;
cout << "Позиция в метрах, углы в градусах." << endl;
sleep(3);
}
sqlite3_finalize(ins);
sqlite3_close(db);
return 0;
}