C++ based UKF code example to track a cubic polynomial curve

Reference one

Here is a C++ based UKF code example for tracking a cubic polynomial curve:

#include <iostream>
#include <Eigen/Dense>
#include <cmath>

using namespace std;
using namespace Eigen;

//定义状态向量和量测向量的维度
const int n = 3;
const int m = 1;

//定义UKF的参数
const double alpha = 0.1;  // 0 <= alpha <= 1
const double beta = 2.0;
const double kappa = 0.0;

//定义噪声的协方差矩阵
const Matrix<double, n, n> Q = Matrix<double, n, n>::Identity() * 0.1;
const Matrix<double, m, m> R = Matrix<double, m, m>::Identity() * 1.0;

//定义状态向量和量测向量
Vector3d x(0.0, 0.0, 0.0);
Vector3d xp(0.0, 0.0, 0.0);
Vector3d xm(0.0, 0.0, 0.0);
VectorXd z(m);

//定义UKF所需的矩阵和向量
Matrix<double, n, n> P = Matrix<double, n, n>::Identity();
Matrix<double, n, n> PP = Matrix<double, n, n>::Identity();
Matrix<double, n, m> K = Matrix<double, n, m>::Zero();
Matrix<double, 2 * n + 1, n> X = Matrix<double, 2 * n + 1, n>::Zero();
VectorXd Wm(2 * n + 1);
VectorXd Wc(2 * n + 1);

//定义三次多项式曲线
double a0 = 0.0;
double a1 = 1.0;
double a2 = 0.5;
double a3 = -0.1;

//定义状态转移函数
void f(Vector3d& x, const Vector3d& xp)
{
    double dt = 0.1;
    x(0) = xp(0) + xp(1) * dt + 0.5 * xp(2) * dt * dt;
    x(1) = xp(1) + xp(2) * dt;
    x(2) = xp(2);
}

//定义量测函数
void h(const Vector3d& x, double& z)
{
    z = a0 + a1 * x(0) + a2 * pow(x(0), 2) + a3 * pow(x(0), 3);
}

//定义非线性方程的近似线性化
void sigma_points()
{
    Matrix<double, n, n> L = (P + Q).llt().matrixL();
    X.col(0) = x;
    for (int i = 0; i < n; i++)
    {
        X.col(i + 1) = x + sqrt(n + kappa) * L.col(i);
        X.col(i + n + 1) = x - sqrt(n + kappa) * L.col(i);
    }
}

//定义权重向量
void weights()
{
    Wm(0) = kappa / (n + kappa);
    Wc(0) = Wm(0) + (1 - alpha * alpha + beta);
    for (int i = 1; i < 2 * n + 1; i++)
    {
        Wm(i) = 0.5 / (n + kappa);
        Wc(i) = Wm(i);
    }
}

//定义UKF的预测步骤
void predict()
{
    sigma_points();
    for (int i = 0; i < 2 * n + 1; i++)
    {
        f(X.col(i), xm);
        XP.col(i) = xm;
    }
    x = Vector3d::Zero();
    for (int i = 0; i < 2 * n + 1; i++)
    {
        x += Wm(i) * XP.col(i);
    }
    P = Matrix<double, n, n>::Zero();
    for (int i = 0; i < 2 * n + 1; i++)
    {
        P += Wc(i) * (XP.col(i) - x) * (XP.col(i) - x).transpose();
    }
    P += Q;
}

//定义UKF的更新步骤
void update(double z_meas)
{
    for (int i = 0; i < 2 * n + 1; i++)
    {
        h(X.col(i), z(i));
        Z.col(i) = z(i);
    }
    double z_pred = 0.0;
    for (int i = 0; i < 2 * n + 1; i++)
    {
        z_pred += Wm(i) * Z(i);
    }
    K = Matrix<double, n, m>::Zero();
    for (int i = 0; i < 2 * n + 1; i++)
    {
        K += Wc(i) * (XP.col(i) - x) * (Z(i) - z_pred).transpose();
    }
    K *= R.inverse();
    x += K * (z_meas - z_pred);
    P -= K * R * K.transpose();
}

int main()
{
    cout.precision(2);
    cout << fixed;
    weights();
    for (int i = 0; i < 100; i++)
    {
        predict();
        double z_meas = a0 + a1 * x(0) + a2 * pow(x(0), 2) + a3 * pow(x(0), 3) + (rand() / RAND_MAX - 0.5) * 2 * 0.5;
        update(z_meas);
        cout << "x = " << x.transpose() << endl;
    }
    return 0;
}

This is a simple example code that can be modified and extended as needed.

Reference two

The following is a CPP code implementation of a basic UKF tracking algorithm for tracking cubic polynomial curves:

#include <iostream>
#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>

using namespace std;
using namespace Eigen;

const int n = 5; // 状态数量
const int m = 3; // 观测量数量
const double dt = 0.1; // 时间间隔

// 系统状态转移函数
VectorXd f(const VectorXd &x) {
    VectorXd x_next(5);
    x_next[0] = x[0] + x[1]*dt + 0.5*x[2]*dt*dt + 0.1667*x[3]*dt*dt*dt;
    x_next[1] = x[1] + x[2]*dt + 0.3333*x[3]*dt*dt;
    x_next[2] = x[2] + x[3]*dt + 0.5*x[4]*dt*dt;
    x_next[3] = x[3] + x[4]*dt;
    x_next[4] = x[4];
    return x_next;
}

// 系统观测函数
VectorXd h(const VectorXd &x) {
    VectorXd z(3);
    z[0] = x[0];
    z[1] = x[1];
    z[2] = x[2];
    return z;
}

int main() {
    // 初始状态和协方差矩阵
    MatrixXd P0(n, n);
    P0.setZero();
    P0.diagonal() << 10, 10, 10, 10, 10;
    VectorXd x0(n);
    x0 << 0, 0, 0, 0, 0;

    // 测量协方差矩阵和观测矩阵
    MatrixXd Q(m, m);
    Q.setIdentity(); // 没有测量噪声
    MatrixXd R(m, m);
    R << 1, 0, 0,
         0, 1, 0,
         0, 0, 1;
    MatrixXd H(m, n);
    H.setZero();
    H.diagonal() << 1, 1, 1;

    // UKF参数
    double alpha = 1;
    double beta = 2;
    double kappa = 0;
    int lambda = alpha*alpha*(n+kappa)-n;

    // 初始化UKF
    VectorXd x(n);
    x = x0;
    MatrixXd P(n, n);
    P = P0;

    // 仿真
    for (double t = 0; t < 10; t += dt) {
        // 预测
        MatrixXd L = (P + lambda*Q).llt().matrixL();
        MatrixXd X(n, 2*n+1);
        X.col(0) = x;
        for (int i = 0; i < n; i++) {
            X.col(i+1) = x + L.col(i);
            X.col(i+1+n) = x - L.col(i);
        }
        VectorXd x_pred(n);
        x_pred.setZero();
        for (int i = 0; i < 2*n+1; i++) {
            x_pred += f(X.col(i));
        }
        x_pred /= 2*n+1;
        MatrixXd P_pred(n, n);
        P_pred.setZero();
        for (int i = 0; i < 2*n+1; i++) {
            VectorXd dx = f(X.col(i)) - x_pred;
            while (dx[3] > M_PI) dx[3] -= 2*M_PI;
            while (dx[3] < -M_PI) dx[3] += 2*M_PI;
            P_pred += dx*dx.transpose();
        }
        P_pred /= 2*n+1;
        P_pred += Q;

        // 更新
        MatrixXd L_pred = (P_pred + lambda*R).llt().matrixL();
        MatrixXd Z(m, 2*n+1);
        for (int i = 0; i < 2*n+1; i++) {
            Z.col(i) = h(X.col(i));
        }
        VectorXd z_pred(m);
        z_pred.setZero();
        for (int i = 0; i < 2*n+1; i++) {
            z_pred += Z.col(i);
        }
        z_pred /= 2*n+1;
        MatrixXd Pzz(m, m);
        Pzz.setZero();
        for (int i = 0; i < 2*n+1; i++) {
            VectorXd dz = Z.col(i) - z_pred;
            Pzz += dz*dz.transpose();
        }
        Pzz /= 2*n+1;
        Pzz += R;
        MatrixXd Pxz(n, m);
        Pxz.setZero();
        for (int i = 0; i < 2*n+1; i++) {
            VectorXd dx = X.col(i) - x_pred;
            VectorXd dz = Z.col(i) - z_pred;
            Pxz += dx*dz.transpose();
        }
        Pxz /= 2*n+1;
        MatrixXd K(n, m);
        K = Pxz*Pzz.inverse();
        x = x_pred + K*(VectorXd(3) << sin(t), cos(t), t).finished() - z_pred;
        P = P_pred - K*Pzz*K.transpose();

        // 打印结果
        cout << x.transpose() << endl;
    }

    return 0;
}

Note that this code assumes no measurement noise and a fixed observation matrix is ​​used. If you need to account for measurement noise, and use a different observation matrix, then you need to modify your code accordingly.

Reference three

The following is an example of CPP code for a simple UKF tracking algorithm to track a cubic polynomial curve:

#include <iostream>
#include <math.h>
#include <Eigen/Dense>

using namespace std;
using namespace Eigen;

// 定义状态向量
typedef Matrix<double, 4, 1> stateVec;

// 定义测量向量
typedef Matrix<double, 2, 1> measVec;

// 定义噪声向量
typedef Matrix<double, 2, 1> noiseVec;

// 定义系统噪声协方差矩阵
const Matrix2d Q = 0.01 * Matrix2d::Identity();

// 定义测量噪声协方差矩阵
const Matrix2d R = 0.1 * Matrix2d::Identity();

// 定义状态转移函数
stateVec f(const stateVec& x, double dt) {
    
    
  stateVec xNew;
  xNew << x(0) + x(1) * dt + 0.5 * x(2) * pow(dt, 2) + (1.0 / 6.0) * x(3) * pow(dt, 3),
          x(1) + x(2) * dt + 0.5 * x(3) * pow(dt, 2),
          x(2) + x(3) * dt,
          x(3);
  return xNew;
}

// 定义测量函数
measVec h(const stateVec& x) {
    
    
  measVec z;
  z << x(0), x(2);
  return z;
}

// 定义卡尔曼增益
Matrix<double, 4, 2> K(const Matrix<double, 4, 4>& P, const Matrix<double, 2, 4>& H) {
    
    
  Matrix<double, 2, 2> S = H * P * H.transpose() + R;
  Matrix<double, 4, 2> K = P * H.transpose() * S.inverse();
  return K;
}

// 定义UKF单步迭代函数
void ukfStep(stateVec& x, Matrix<double, 4, 4>& P, double dt, const measVec& z, const noiseVec& v) {
    
    
  // 参数设置
  int n = 4;  // 状态向量维数
  int k = 2 * n + 1;  // 采样点个数
  double alpha = 0.001;  // Sigma点分布参数
  double beta = 2;  // 确信度参数
  double kappa = 0;  // 权重参数

  // Sigma点计算
  Matrix<double, 4, 4> L = P.llt().matrixL();  // Cholesky分解
  Matrix<double, 4, 4> M = sqrt(n + lambda) * L;  // 公式(13)
  Matrix<double, 4, 1> xk;
  Matrix<double, 4, k> X;
  Matrix<double, 2, k> Y;
  xk = x;
  X.col(0) = xk;
  for(int i = 0; i < n; i++) {
    
    
    X.col(i+1) = xk + M.col(i);
    X.col(i+1+n) = xk - M.col(i);
  }

  // 权重计算
  double lambda = pow(alpha, 2) * (n + kappa) - n;
  double Wc0 = lambda/(n + lambda) + (1 - pow(alpha, 2) + beta);
  double Wc = 0.5/(n + lambda);
  double Wm0 = lambda/(n + lambda);
  double Wm = 0.5/(n + lambda);

  // 预测步骤
  Matrix<double, 4, 1> xp = Matrix<double, 4, 1>::Zero();
  for(int i = 0; i < k; i++) {
    
    
    xp += Wm * f(X.col(i), dt);
  }
  Matrix<double, 4, 4> Pp = Q;
  for(int i = 0; i < k; i++) {
    
    
    Matrix<double, 4, 1> temp = f(X.col(i), dt) - xp;
    Pp += Wc * temp * temp.transpose();
  }

  // 更新步骤
  Matrix<double, 2, 4> H = Matrix<double, 2, 4>::Zero();
  H(0,0) = 1;
  H(1,2) = 1;
  Matrix<double, 2, 1> yp = Matrix<double, 2, 1>::Zero();
  for(int i = 0; i < k; i++) {
    
    
    yp += Wm * h(X.col(i));
  }
  Matrix<double, 2, 2> Sp = R;
  for(int i = 0; i < k; i++) {
    
    
    Matrix<double, 2, 1> temp = h(X.col(i)) - yp;
    Sp += Wc * temp * temp.transpose();
  }
  Matrix<double, 4, 2> Kp = K(Pp, H);

  x = xp + Kp * (z - yp - H * (xp - xk) - v);
  P = Pp - Kp * H * Pp;
}

int main() {
    
    
  // 初始化状态向量和协方差矩阵
  stateVec x;
  x << 0, 1, 0, 0;
  Matrix<double, 4, 4> P = Matrix<double, 4, 4>::Zero();
  P(0,0) = 1;
  P(1,1) = 1;
  P(2,2) = 1;
  P(3,3) = 1;

  // 模拟测量数据
  double dt = 0.1;
  measVec z;
  noiseVec v;
  for(int i = 0; i < 100; i++) {
    
    
    double t = i * dt;
    z << 0.5 * pow(t, 3), t;
    v = sqrt(R) * Matrix<double, 2, 1>::Random();
    ukfStep(x, P, dt, z, v);
  }

  // 输出结果
  cout << "State vector x = " << x.transpose() << endl;
  cout << "Covariance matrix P = " << P << endl;
  
  return 0;
}

In this example, we define a 4D state vector (including position, velocity and acceleration) and a 2D measurement vector (including position and acceleration) and estimate them using the UKF algorithm. The f function and h function in the code represent the state transition function and the measurement function respectively, the K function represents the Kalman gain, and the ukfStep function represents the UKF single-step iterative function. In the main function, we first initialize the state vector and covariance matrix, then simulate the measurement data, and call the ukfStep function to update the state vector and covariance matrix, and finally output the results.


The following is a sample code for implementing UKF to track a cubic polynomial curve using Python:

import numpy as np
from filterpy.kalman import UnscentedKalmanFilter as UKF

# Define system dynamics
def f(x, dt):
    F = np.array([[1, dt, 0.5*dt**2],
                  [0, 1, dt],
                  [0, 0, 1]])
    return F @ x

# Define measurement function
def h(x):
    H = np.array([[1, 0, 0],
                  [0, 1, 0]])
    return H @ x

# Create UKF instance
ukf = UKF(dim_x=3, dim_z=2, dt=0.1, fx=f, hx=h)

# Set initial state and covariance
x0 = np.array([0, 0, 0])
P0 = np.eye(3) * 10

ukf.x = x0
ukf.P = P0

# Define process and measurement noise
Q = np.diag([0.1, 0.1, 0.1])
R = np.diag([1, 1])

ukf.Q = Q
ukf.R = R

# Generate ground truth data
t = np.arange(0, 10, ukf.dt)
x_gt = np.array([3*t**3-2*t**2, 9*t**2-4*t, 6*t+10])

# Generate noisy measurements
z = h(x_gt) + np.random.normal(size=(2, len(t))) * R.diagonal()

# Run filter
x_est = []
for i, z_i in enumerate(z.T):
    ukf.predict()
    ukf.update(z_i)
    x_est.append(ukf.x)

x_est = np.array(x_est).T

# Plot results
import matplotlib.pyplot as plt

plt.figure()
plt.plot(t, x_gt[0], label='Ground truth')
plt.plot(t, x_est[0], label='Estimate')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Position (m)')
plt.title('Position estimate')

plt.figure()
plt.plot(t, x_gt[1], label='Ground truth')
plt.plot(t, x_est[1], label='Estimate')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Velocity (m/s)')
plt.title('Velocity estimate')

plt.show()

Among them, system dynamics fare defined by functions, and measurement functions hare defined by functions. In this example, the system dynamics are cubic polynomial curves, measured as position and velocity on the trajectory. Process noise Qand measurement noise Rare represented by a diagonal matrix. The program generates a simulation dataset containing the real state and measured values ​​of the system, and estimates the real state by UKF. Finally, the program plots the time evolution of the estimated results.

ukf track lane lines

The following is a complete C++ code for a simple UKF-based lane line tracking algorithm. The algorithm uses a measurement-based model to estimate the location of lane markings.

#include <iostream>
#include <vector>
#include <cmath>
#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>

using namespace std;
using namespace Eigen;

//定义状态向量和测量向量的维度
const int x_dim = 5;
const int z_dim = 2;

//定义UKF的参数
const double alpha = 1e-1;
const double beta = 2.0;
const double kappa = 0.0;

//定义汽车的轮距和轴距
const double L = 2.6;
const double lf = 1.5;
const double lr = L - lf;

//定义测量误差和过程噪声的协方差矩阵
const Matrix<double, z_dim, z_dim> R = Matrix<double, z_dim, z_dim>::Identity() * 0.1;
const Matrix<double, x_dim, x_dim> Q = Matrix<double, x_dim, x_dim>::Identity() * 0.01;

//定义状态向量
struct State {
    double px;
    double py;
    double psi;
    double v;
    double cte;
};

//将状态向量转换为矩阵
Matrix<double, x_dim, 1> state2vec(const State& x) {
    Matrix<double, x_dim, 1> v;
    v << x.px, x.py, x.psi, x.v, x.cte;
    return v;
}

//将矩阵转换为状态向量
State vec2state(const Matrix<double, x_dim, 1>& v) {
    State s;
    s.px = v(0);
    s.py = v(1);
    s.psi = v(2);
    s.v = v(3);
    s.cte = v(4);
    return s;
}

//定义车辆模型
State vehicleModel(const State& x, double a, double delta, double dt) {
    State x_next;

    double delta_f = atan2(lr * tan(delta) , lf + lr);
    x_next.px = x.px + x.v * cos(x.psi) * dt;
    x_next.py = x.py + x.v * sin(x.psi) * dt;
    x_next.psi = x.psi + x.v / lr * sin(delta_f) * dt;
    x_next.v = x.v + a * dt;
    x_next.cte = x.cte - (x.v * sin(x.psi) + x.v * delta_f) * dt;

    return x_next;
}

//计算卡尔曼增益
Matrix<double, x_dim, z_dim> kalmanGain(const Matrix<double, x_dim, x_dim>& P,
    const Matrix<double, x_dim, 1>& x, const Matrix<double, z_dim, 1>& z_pred) {
    Matrix<double, z_dim, x_dim> H;
    H << 1, 0, 0, 0, 0,
         0, 1, 0, 0, 0;

    Matrix<double, z_dim, z_dim> S = H * P * H.transpose() + R;
    Matrix<double, x_dim, z_dim> K = P * H.transpose() * S.inverse();
    return K;
}

//计算预测测量值
Matrix<double, z_dim, 1> predictMeasurement(const State& x) {
    Matrix<double, z_dim, 1> z;
    z << x.px, x.cte;
    return z;
}

//计算状态转移矩阵
Matrix<double, x_dim, x_dim> stateTransition(const State& x, double a, double delta, double dt) {
    double delta_f = atan2(lr * tan(delta) , lf + lr);

    Matrix<double, x_dim, x_dim> F;
    F << 1, 0, -x.v * sin(x.psi) * dt, cos(delta_f) * dt, -sin(x.psi) * dt,
         0, 1, x.v * cos(x.psi) * dt, sin(delta_f) * dt, cos(x.psi) * dt,
         0, 0, 1, x.v / lr * cos(delta_f) * dt, x.v * sin(delta_f) / lr * dt,
         0, 0, 0, 1, 0,
         0, 0, -x.v * sin(x.psi) * dt, -sin(delta_f) * dt - x.v * delta_f * cos(delta_f) * dt / (lf + lr), cos(x.psi) * dt;

    return F;
}

int main() {
    //初始化状态向量和协方差矩阵
    State x;
    x.px = 0;
    x.py = 0;
    x.psi = 0;
    x.v = 10;
    x.cte = 0;

    Matrix<double, x_dim, x_dim> P = Matrix<double, x_dim, x_dim>::Identity() * 0.1;

    //初始化UKF参数
    double lambda = alpha * alpha * (x_dim + kappa) - x_dim;
    int n_sigma = 2 * x_dim + 1;
    double weights_0 = lambda / (x_dim + lambda);
    double weights_i = 1.0 / (2.0 * (x_dim + lambda));

    Matrix<double, x_dim, n_sigma> X;
    Matrix<double, z_dim, n_sigma> Z_pred;

    //开始循环处理
    while (true) {
        //生成sigma点
        Matrix<double, x_dim, x_dim> L = (P + lambda * Matrix<double, x_dim, x_dim>::Identity()).llt().matrixL();
        X.col(0) = state2vec(x);
        for (int i = 0; i < x_dim; ++i) {
            X.col(i + 1) = state2vec(x) + sqrt(x_dim + lambda) * L.col(i);
            X.col(i + 1 + x_dim) = state2vec(x) - sqrt(x_dim + lambda) * L.col(i);
        }

        //计算每个sigma点的权重
        Matrix<double, n_sigma, 1> weights;
        weights(0) = weights_0;
        for (int i = 1; i < n_sigma; ++i) {
            weights(i) = weights_i;
        }

        //预测sigma点
        for (int i = 0; i < n_sigma; ++i) {
            State x_pred = vec2state(vehicleModel(vec2state(X.col(i)), 0, 0, 1));
            X.col(i) = state2vec(x_pred);
        }

        //计算预测状态向量和协方差矩阵
        State x_hat;
        Matrix<double, x_dim, x_dim> P_hat;
        x_hat = vec2state(weights(0) * X.col(0));
        for (int i = 1; i < n_sigma; ++i) {
            x_hat = x_hat + vec2state(weights(i) * X.col(i));
        }
        for (int i = 0; i < n_sigma; ++i) {
            Matrix<double, x_dim, 1> delta_x = X.col(i) - state2vec(x_hat);
            P_hat = P_hat + weights(i) * delta_x * delta_x.transpose();
        }
        P_hat = P_hat + Q;

        //计算预测测量值
        for (int i = 0; i < n_sigma; ++i) {
            Z_pred.col(i) = predictMeasurement(vec2state(X.col(i)));
        }

        //计算预测测量向量和协方差矩阵
        Matrix<double, z_dim, 1> z_hat;
        Matrix<double, z_dim, z_dim> S;
        z_hat = weights(0) * Z_pred.col(0);
        for (int i = 1; i < n_sigma; ++i) {
            z_hat = z_hat + weights(i) * Z_pred.col(i);
        }
        for (int i = 0; i < n_sigma; ++i) {
            Matrix<double, z_dim, 1> delta_z = Z_pred.col(i) - z_hat;
            S = S + weights(i) * delta_z * delta_z.transpose();
        }
        S = S + R;

        //计算卡尔曼增益
        Matrix<double, x_dim, z_dim> K = kalmanGain(P_hat, state2vec(x_hat), z_hat);

        //更新状态向量和协方差矩阵
        x = vec2state(state2vec(x_hat) + K * (z_pred - z_hat));
        P = P_hat - K * S * K.transpose();

        //输出结果
        cout << "px: " << x.px << ", py: " << x.py << ", psi: " << x.psi << ", v: " << x.v << ", cte: " << x.cte << endl;
    }
    return 0;
}

Note that this is just a simple sample code. The actual lane line tracking should consider more factors, such as curvature radius, lane line width, etc. In addition, the algorithm also has some limitations, such as high sensitivity to noise and uncertainty, and requires more fine-tuning of parameters.


UKF (Unscented Kalman Filter) is a nonlinear filtering algorithm that can be used to track lane lines. The following is a complete C++ code example of UKF tracking lane lines based on OpenCV:

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include "Eigen/Dense"

using namespace std;
using namespace cv;
using namespace Eigen;

// 定义测量值和状态向量的维度
const int z_dim = 2; // (x, y) 坐标
const int x_dim = 4; // (x, y) 坐标和速度 (vx, vy)

// 定义UKF参数
const double alpha = 0.1; // 控制sigma点的分布
const double beta = 2; // 用于计算协方差矩阵的权重参数
const double kappa = 0; // 控制sigma点的数量

// 定义边界框和ROI的大小
const int box_width = 100;
const int box_height = 50;
const int roi_width = 300;
const int roi_height = 200;

// 定义测量噪声和模型噪声的协方差矩阵
MatrixXd R(z_dim, z_dim); // 测量噪声
MatrixXd Q(x_dim, x_dim); // 模型噪声

// 定义状态向量和测量向量
VectorXd x(x_dim); // 状态向量
VectorXd z(z_dim); // 测量向量

// 定义状态转移函数和测量函数
void f(const VectorXd& x, const VectorXd& u, VectorXd& x_pred);
void h(const VectorXd& x, VectorXd& z_pred);

int main(int argc, char** argv) {
    
    
    // 读取视频帧
    VideoCapture cap("lane_detection.mp4");
    if (!cap.isOpened()) {
    
    
        cout << "Error opening video stream" << endl;
        return -1;
    }

    // 初始化测量噪声和模型噪声的协方差矩阵
    R << 50, 0,
         0, 50;
    Q << 25, 0, 10, 0,
         0, 25, 0, 10,
         10, 0, 25, 0,
         0, 10, 0, 25;

    // 初始化状态向量
    x << 0, 0, 0, 0;

    while (true) {
    
    
        // 读取视频帧
        Mat frame;
        cap >> frame;
        if (frame.empty()) {
    
    
            break;
        }

        // 将彩色图像转换为灰度图像
        Mat gray;
        cvtColor(frame, gray, COLOR_BGR2GRAY);

        // 对灰度图像进行高斯模糊
        Mat blur;
        GaussianBlur(gray, blur, Size(5, 5), 0);

        // 对灰度图像进行Canny边缘检测
        Mat edges;
        Canny(blur, edges, 50, 150);

        // 对边缘图像进行霍夫直线变换
        vector<Vec2f> lines;
        HoughLines(edges, lines, 1, CV_PI/180, 100);

        // 将直线转换为车道线
        vector<Vec4i> lanes;
        for (size_t i = 0; i < lines.size(); i++) {
    
    
            float rho = lines[i][0];
            float theta = lines[i][1];
            double a = cos(theta), b = sin(theta);
            double x0 = a*rho, y0 = b*rho;
            Point pt1(cvRound(x0 + 1000*(-b)), cvRound(y0 + 1000*(a)));
            Point pt2(cvRound(x0 - 1000*(-b)), cvRound(y0 - 1000*(a)));
            if (abs(pt1.x - pt2.x) > 50) {
    
     // 判断是否为水平直线
                lanes.push_back(Vec4i(pt1.x, pt1.y, pt2.x, pt2.y));
            }
        }

        // 对车道线进行曲线拟合
        vector<Point> curve;
        if (!lanes.empty()) {
    
    
            Mat mask = Mat::zeros(frame.size(), CV_8U);
            for (size_t i = 0; i < lanes.size(); i++) {
    
    
                line(mask, Point(lanes[i][0], lanes[i][1]), Point(lanes[i][2], lanes[i][3]), Scalar(255), 20);
            }
            Rect box(frame.cols/2 - box_width/2, frame.rows - box_height, box_width, box_height);
            Mat roi = mask(box);
            Mat hist;
            reduce(roi, hist, 0, REDUCE_SUM, CV_32S);
            Point max_loc;
            minMaxLoc(hist, nullptr, nullptr, nullptr, &max_loc);
            int x_offset = max_loc.x - roi_width/2;
            Rect roi2(frame.cols/2 - roi_width/2 + x_offset, frame.rows - roi_height, roi_width, roi_height);
            Mat roi2_gray;
            cvtColor(frame(roi2), roi2_gray, COLOR_BGR2GRAY);
            threshold(roi2_gray, roi2_gray, 0, 255, THRESH_BINARY | THRESH_OTSU);
            findNonZero(roi2_gray, curve);
            for (size_t i = 0; i < curve.size(); i++) {
    
    
                curve[i].x += roi2.x;
                curve[i].y += roi2.y;
            }
        }

        // 初始化UKF
        int n = x_dim; // 状态向量的维度
        double lambda = alpha*alpha*(n + kappa) - n; // UKF的参数
        int num_sigma = 2*n + 1; // sigma点的数量
        MatrixXd X(n, num_sigma); // sigma点
        VectorXd x_pred(n); // 预测状态向量
        MatrixXd P_pred(n, n); // 预测状态协方差矩阵
        MatrixXd K(n, z_dim); // Kalman增益矩阵
        VectorXd x_post(n); // 更新后的状态向量
        MatrixXd P_post(n, n); // 更新后的状态协方差矩阵
        VectorXd z_pred(z_dim); // 预测测量向量
        MatrixXd S(z_dim, z_dim); // 预测测量协方差矩阵
        MatrixXd Z(z_dim, num_sigma); // 测量sigma点
        VectorXd innovation(z_dim); // 测量不确定性
        double lambda_sum = lambda + n; // UKF的参数和状态向量维度之和
        double alpha_sqrt = sqrt(lambda_sum); // 控制sigma点的分布的参数
        double w_mean = lambda_sum / (2*lambda_sum); // 均值权重
        double w_cov = w_mean + (1 - alpha*alpha + beta); // 协方差权重

        // 从曲线中提取测量向量
        if (!curve.empty()) {
    
    
            z << curve[0].x, curve[0].y;
        } else {
    
    
            z << 0, 0;
        }

        // 进行UKF预测和更新
        if (curve.empty()) {
    
    
            // 如果无法检测到车道线,则将速度设置为零
            x << x(0), x(1), 0, 0;
        } else if (curve.size() == 1) {
    
    
            // 如果只检测到一条车道线,则根据该车道线的位置调整状态向量
            x << curve[0].x, curve[0].y, x(2), x(3);
        } else {
    
    
            // 如果检测到两条车道线,则使用Kalman滤波器进行跟踪
            // 将检测到的车道线分为左右两个区域
            vector<Point> left_curve;
            vector<Point> right_curve;
            for (size_t i = 0; i < curve.size(); i++) {
    
    
                if (curve[i].x <= frame.cols/2) {
    
    
                    left_curve.push_back(curve[i]);
                } else {
    
    
                    right_curve.push_back(curve[i]);
                }
            }

            // 如果左右区域都包含足够的点,则使用Kalman滤波器进行跟踪
            if (left_curve.size() > 10 && right_curve.size() > 10) {
    
    
                // 从左右区域中提取x和y坐标,用于计算测量向量
                vector<double> left_x, left_y, right_x, right_y;
                for (size_t i = 0; i < left_curve.size(); i++) {
    
    
                    left_x.push_back(left_curve[i].x);
                    left_y.push_back(left_curve[i].y);
                }
                for (size_t i = 0; i < right_curve.size(); i++) {
    
    
                    right_x.push_back(right_curve[i].x);
                    right_y.push_back(right_curve[i].y);
                }
                double left_mean_x = accumulate(left_x.begin(), left_x.end(), 0.0) / left_x.size();
                double left_mean_y = accumulate(left_y.begin(), left_y.end(), 0.0) / left_y.size();
                double right_mean_x = accumulate(right_x.begin(), right_x.end(), 0.0) / right_x.size();
                double right_mean_y = accumulate(right_y.begin(), right_y.end(), 0.0) / right_y.size();
                z << (left_mean_x + right_mean_x) / 2, (left_mean_y + right_mean_y) / 2;

                // 使用UKF进行状态估计
                VectorXd u(x_dim); // 控制向量
                u << 0, 0, 0, 0;
                MatrixXd Wm(num_sigma, 1); // sigma点均值权重
                MatrixXd Wc(num_sigma, 1); // sigma点协方差权重

                // 计算sigma点
                MatrixXd A = alpha_sqrt * P_pred.llt().matrixL();
                X.col(0) = x_pred;
                for (int i = 0; i < n; i++) {
    
    
                    X.col(i + 1) = x_pred + A.col(i);
                    X.col(i + 1 + n) = x_pred - A.col(i);
                }

                // 计算均值权重和协方差权重
                Wm.fill(w_mean);
                Wc.fill(w_cov);
                Wc(0) = lambda / lambda_sum;

                // 对每个sigma点进行状态预测
                for (int i = 0; i < num_sigma; i++) {
    
    
                    VectorXd x_i(n);
                    f(X.col(i), u, x_i);
                    X.col(i) = x_i;
                }

                // 计算预测状态向量和协方差矩阵
                x_pred.setZero();
                for (int i = 0; i < num_sigma; i++) {
    
    
                    x_pred += Wm(i) * X.col(i);
                }
                P_pred.setZero();
                for (int i = 0; i < num_sigma; i++) {
    
    
                    VectorXd x_diff = X.col(i) - x_pred;
                    P_pred += Wc(i) * x_diff * x_diff.transpose();
                }
                P_pred += Q;

                // 对每个sigma点进行测量预测
                for (int i = 0; i < num_sigma; i++) {
    
    
                    VectorXd z_i(z_dim);
                    h(X.col(i), z_i);
                    Z.col(i) = z_i;
                }

                // 计算预测测量向量和协方差矩阵
                z_pred.setZero();
                for (int i = 0; i < num_sigma; i++) {
    
    
                    z_pred += Wm(i) * Z.col(i);
                }
                S.setZero();
                for (int i = 0; i < num_sigma; i++) {
    
    
                    VectorXd z_diff = Z.col(i) - z_pred;
                    S += Wc(i) * z_diff * z_diff.transpose();
                }
                S += R;

                // 计算Kalman增益矩阵
                K = P_pred * S.inverse();

                // 计算状态更新
                innovation = z - z_pred;
                x_post = x_pred + K * innovation;
                P_post = P_pred - K * S * K.transpose();
            } else {
    
    
                // 如果至少一个区域缺少足够的点,则将速度设置为零
                x << z(0), z(1), 0, 0;
            }
        }

        // 绘制车道线和状态向量
        for (size_t i = 0; i < lanes.size(); i++) {
    
    
            line(frame, Point(lanes[i][0], lanes[i][1]), Point(lanes[i][2], lanes[i][3]), Scalar(0, 0, 255), 2);
        }
        for (size_t i = 0; i < curve.size(); i++) {
    
    
            circle(frame, curve[i], 5, Scalar(0, 255, 0), -1);
        }
        rectangle(frame, Point(frame.cols/2 - box_width/2, frame.rows - box_height),
            Point(frame.cols/2 + box_width/2, frame.rows), Scalar(255, 0, 0), 2);
        if (curve.empty()) {
    
    
            putText(frame, "No lane detected", Point(50, 50), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
        } else {
    
    
            line(frame, Point(x(0), x(1)), Point(x_post(0), x_post(1)), Scalar(0, 255, 255), 2);
            circle(frame, Point(x_post(0), x_post(1)), 5, Scalar(0, 255, 255), -1);
            putText(frame, "x: " + to_string(x_post(0)), Point(50, 50), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
            putText(frame, "y: " + to_string(x_post(1)), Point(50, 100), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
        }

        // 显示视频帧
        imshow("Lane detection", frame);
        if (waitKey(30) == 27) {
    
    
            break;
        }
    }

    // 释放视频流
    cap.release();
    destroyAllWindows();
    return 0;
}

void f(const VectorXd& x, const VectorXd& u, VectorXd& x_pred) {
    
    
    // 状态转移函数
    x_pred(0) = x(0) + x(2);
    x_pred(1) = x(1) + x(3);
    x_pred(2) = x(2);
    x_pred(3) = x(3);
}

void h(const VectorXd& x, VectorXd& z_pred) {
    
    
    // 测量函数
    z_pred(0) = x(0);
    z_pred(1) = x(1);
}

NOTE: This is just a sample code and may not work in all situations. You need to modify and optimize it according to the actual situation.

Guess you like

Origin blog.csdn.net/qq_39506862/article/details/130912585