slam入门 | 卡尔曼滤波器的C++实现

一、关于卡尔曼滤波器

1.状态估计问题一般分为线性/分线性,高斯/非高斯,批量/递归,而卡尔曼滤波器是一种递归优化方法,是线性高斯系统下的最优无偏估计。

2.卡尔曼滤波器需要给定初始值{x,P},即给定初始值后还需要其协方差矩阵。

3.卡尔曼滤波器有很多推导方式,一般可以由MAP或贝叶斯推断推导。

二、环境

CMake

Eigen

opencv(只是利用给定随机值的函数)

vector

yaml-cpp

三、C++代码

3.1 建立一个KalmanFilter库,这里借鉴了博主

https://blog.csdn.net/shuoyueqishilove/article/details/81713142?ops_request_misc=&request_id=&biz_id=102&utm_term=C%20++%20kalman%E6%BB%A4%E6%B3%A2&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-81713142.142^v100^pc_search_result_base9&spm=1018.2226.3001.4187icon-default.png?t=N7T8https://blog.csdn.net/shuoyueqishilove/article/details/81713142?ops_request_misc=&request_id=&biz_id=102&utm_term=C%20++%20kalman%E6%BB%A4%E6%B3%A2&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-81713142.142^v100^pc_search_result_base9&spm=1018.2226.3001.4187

using namespace std;
using namespace Eigen;

KalmanFilter::KalmanFilter(int stateSize_, int measSize_, int uSize_) : stateSize(stateSize_), measSize(measSize_),
                                                                        uSize(uSize_) {
    if (stateSize == 0 || measSize == 0) {
        cout << "Error" << endl;
    }

    x.resize(stateSize);
    x.setZero();

    A.resize(stateSize, stateSize);
    A.setIdentity();

    u.resize(uSize);
    u.setZero();

    B.resize(stateSize, uSize);
    B.setZero();

    P.resize(stateSize, stateSize);
    P.setIdentity();

    H.resize(measSize, stateSize);
    H.setZero();

    z.resize(uSize);
    z.setZero();

    Q.resize(stateSize, stateSize);
    Q.setIdentity();

    R.resize(measSize, measSize);
    R.setIdentity();
}

void KalmanFilter::init(VectorXd &x_, MatrixXd &P_, MatrixXd &R_, MatrixXd &Q_) {
    x = x_;
    P = P_;
    R = R_;
    Q = Q_;
}

VectorXd KalmanFilter::predict(MatrixXd &A_, MatrixXd &B_, VectorXd &u_) {
    A = A_;
    B = B_;
    u = u_;
    x = A * x + B * u;
    MatrixXd A_t = A.transpose();
    P = A * P * A_t + Q;
    return x;
}

VectorXd KalmanFilter::update(Eigen::MatrixXd &H_, Eigen::VectorXd &z_meas) {
    H = H_;
    MatrixXd temp1, temp2, H_t;
    H_t = H.transpose();
    temp1 = H * P * H_t;
    temp2 = temp1.inverse();
    MatrixXd K = P * H_t * temp2;
    z = H * x;
    x = x + K * (z_meas - z);
    MatrixXd I = MatrixXd::Identity(stateSize, stateSize);
    P = (I - K * H) * P;
    return x;
}

3.2 数学库

double sample(double x0, double v0, double acc, double t) {
    return x0 + v0 * t + 1 / 2 * acc * t * t;
}

3.3 主代码

using namespace std;
using namespace Eigen;

int main(int argc, char *argv[]) {

    // 读取config文件
    if (argc != 2) {
        cout << "Error,No configfile" << endl;
    }

    YAML::Node config = YAML::LoadFile(argv[1]);
    auto T = config["T"].as<double>();
    auto N = config["N"].as<int>();

    // 导出文件
    ofstream fout;
    fout.open(config["out_path"].as<string>());

    // 设置初始值,u,z的标准差
    double ax_sigma = 1.0;
    double ay_sigma = 1.0;
    double zx_sigma = 0.01;
    double zy_sigma = 0.01;

    // 设置x,u,z矩阵纬度
    auto stateSize = config["stateSize"].as<int>();
    auto measSize = config["measSize"].as<int>();
    auto uSize = config["uSize"].as<int>();

    // 构建kf类
    KalmanFilter kf(stateSize, measSize, uSize);

    // 初始化矩阵和向量
    MatrixXd A(stateSize, stateSize);
    MatrixXd B(stateSize, uSize);
    VectorXd u(uSize);
    VectorXd x(stateSize);
    VectorXd z(measSize);
    MatrixXd H(measSize, stateSize);
    MatrixXd P(stateSize, stateSize);
    MatrixXd Q(stateSize, stateSize);
    MatrixXd R(measSize, measSize);

    // 设置矩阵值
    const YAML::Node &matrix_A = config["A"];
    const YAML::Node &matrix_B = config["B"];
    const YAML::Node &matrix_H = config["H"];
    const YAML::Node &matrix_Q = config["Q"];
    const YAML::Node &matrix_R = config["R"];
    const YAML::Node &matrix_x = config["x"];
    const YAML::Node &matrix_P = config["P"];

    int index = 0;
    for (int row = 0; row < stateSize; row++) {
        for (int col = 0; col < stateSize; col++) {
            A(row, col) = matrix_A[index++].as<double>();
        }
    }

//    A << 1, 0, T, 0,
//            0, 1, 0, T,
//            0, 0, 1, 0,
//            0, 0, 0, 1;

    index = 0;
    for (int row = 0; row < stateSize; row++) {
        for (int col = 0; col < uSize; col++) {
            B(row, col) = matrix_B[index++].as<double>();
        }
    }

//    B << 1 / 2 * T * T, 0,
//            0, 1 / 2 * T * T,
//            T, 0,
//            0, T;

    index = 0;
    for (int row = 0; row < measSize; row++) {
        for (int col = 0; col < stateSize; col++) {
            H(row, col) = matrix_H[index++].as<double>();
        }
    }

//    H << 1, 0, 0, 0,
//            0, 1, 0, 0;

    index = 0;
    for (int row = 0; row < stateSize; row++) {
        for (int col = 0; col < stateSize; col++) {
            P(row, col) = matrix_P[index++].as<double>();
        }
    }

//    P << 1, 0, 0, 0,
//            0, 1, 0, 0,
//            0, 0, 1, 0,
//            0, 0, 0, 1;

    index = 0;
    for (int row = 0; row < stateSize; row++) {
        for (int col = 0; col < stateSize; col++) {
            Q(row, col) = matrix_Q[index++].as<double>();
        }
    }

//    Q << 1 / 4 * ax_sigma * ax_sigma, 0, 0, 0,
//            0, 1 / 4 * ay_sigma * ay_sigma, 0, 0,
//            0, 0, ax_sigma * ax_sigma, 0,
//            0, 0, 0, ay_sigma * ay_sigma;

    index = 0;
    for (int row = 0; row < measSize; row++) {
        for (int col = 0; col < measSize; col++) {
            R(row, col) = matrix_R[index++].as<double>();
        }
    }

//    R << zx_sigma * zx_sigma, 0,
//            0, zy_sigma * zy_sigma;

    // 设置x0的值
    index = 0;
    for (int row = 0; row < stateSize; row++) {
        x(row) = matrix_x[index++].as<double>();
    }

//    x << 0,
//            0,
//            0,
//            0;

    // 构建向量u,创建随机数
    vector<double> ax, ay, rax, ray;
    cv::RNG rng;
    random_device rd1;
    random_device rd2;
    mt19937 gen1(rd1());
    mt19937 gen2(rd2());
    uniform_real_distribution<> dis(0.0, 5.0);
    for (int i = 0; i < N; i++) {
        rax.push_back(dis(gen1));
        ray.push_back(dis(gen2));
        ax.push_back(rax[i] + rng.gaussian(ax_sigma * ax_sigma));
        ay.push_back(ray[i] + rng.gaussian(ay_sigma * ay_sigma));
    }

    // 设置真值
    vector<double> rx, ry, rvx, rvy;
    for (int i = 0; i < N; i++) {
        if (i == 0) {
            rx.push_back(sample(0, 0, rax[i], T));
            ry.push_back(sample(0, 0, ray[i], T));
            rvx.push_back(rax[i] * T);
            rvy.push_back(ray[i] * T);
            continue;
        }
        rx.push_back(sample(rx[i-1], rvx[i-1], rax[i], T));
        ry.push_back(sample(ry[i-1], rvy[i-1], ray[i], T));
        rvx.push_back(rvx[i - 1] + ax[i] * T);
        rvy.push_back(rvy[i - 1] + ay[i] * T);
    }

    // 构建向量z
    vector<double> zx, zy;
    vector<double> vx, vy;
    for (int i = 0; i < N; i++) {
        zx.push_back(rx[i] + rng.gaussian(zx_sigma * zy_sigma));
        zy.push_back(ry[i] + rng.gaussian(zy_sigma * zy_sigma));
    }

    // 卡尔曼滤波过程
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now();// 记录时间函数,开始计时
    VectorXd out;
    fout << "x" << "\t\t" << "y" << "\t\t" << "vx" << "\t\t" << "vy" << "\t\t" << "x_u" << "\t\t" << "y_u" << "\t\t"
         << "vx_u" << "\t\t" << "vy_u" << "\t\t" << "rx" << "\t\t" << "ry" << "\t\t" << endl;
    for (int i = 0; i < N; i++) {
        if (i == 0) {
            kf.init(x, P, R, Q);
        }
        // 先验
        u << ax[i],
                ay[i];
        out = kf.predict(A, B, u);
        fout << out(0) << "\t\t" << out(1) << "\t\t" << out(2) << "\t\t" << out(3) << "\t\t";

        // 后验
        z << zx[i],
                zy[i];
        out = kf.update(H, z);
        fout << out(0) << "\t\t" << out(1) << "\t\t" << out(2) << "\t\t" << out(3) << "\t\t" << rx[i] << "\t\t"
             << ry[i] << endl;
    }
    chrono::steady_clock::time_point t2 = chrono::steady_clock::now();//结束计时
    chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cout << "solve time cost = " << time_used.count() << "seconds." << endl;

    /*
    // 打印随机数据
    fout<<"ax"<<"\t\t"<<"ay"<<"\t\t"<<"rx"<<"\t\t"<<"ry"<<"\t\t"<<"zx"<<"\t\t"<<"zy"<<"\t\t"<<"rvx"<<"\t\t"<<"rvy"<<endl;
    for(int i=0;i<N;i++){
        fout<<ax[i]<<"\tt"<<ay[i]<<"\t\t"<<rx[i]<<"\t\t"<<ry[i]<<"\t\t"<<zx[i]<<"\t\t"<<zy[i]<<"\t\t"<<rvx[i]<<"\t\t"<<rvy[i]<<endl;
    }
    */

    fout.close();

    return 0;
};

3.4 config.yaml部分

T: 0.01  #时间间隔
N: 1000  #数据量

stateSize: 4  #状态量x个数
measSize: 2  #观测量z个数
uSize: 2  #控制量u个数

out_path: "../data/data.txt"
u_path: ""
z_path: ""

A: [1, 0, 0.01, 0,
    0, 1, 0, 0.01,
    0, 0, 1, 0,
    0, 0, 0, 1]  #状态矩阵
B: [0.00005, 0,
    0, 0.00005,
    0.01, 0,
    0, 0.01]  #控制矩阵
H: [1, 0, 0, 0,
    0, 1, 0, 0]  #观测的矩阵
Q: [0.25, 0, 0, 0,
    0, 0.25, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1]  #控制协方差矩阵
R: [1, 0,
    0, 1] #观测协方差矩阵
x: [0, 0, 0, 0] #状态量初始值
P: [1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1]  #状态量初始值协方差矩阵

四、结果

后面应该是速度累积太大了,导致即使时间间隔设置的很小,也会产生极大的漂移

将数据量设置成500

nice!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值