一、关于卡尔曼滤波器
1.状态估计问题一般分为线性/分线性,高斯/非高斯,批量/递归,而卡尔曼滤波器是一种递归优化方法,是线性高斯系统下的最优无偏估计。
2.卡尔曼滤波器需要给定初始值{x,P},即给定初始值后还需要其协方差矩阵。
3.卡尔曼滤波器有很多推导方式,一般可以由MAP或贝叶斯推断推导。
二、环境
CMake
Eigen
opencv(只是利用给定随机值的函数)
vector
yaml-cpp
三、C++代码
3.1 建立一个KalmanFilter库,这里借鉴了博主
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!