当前位置: 首页 > article >正文

最优化理论与自动驾驶(十一):基于iLQR的自动驾驶轨迹跟踪算法(c++和python版本)

最优化理论与自动驾驶(四):iLQR原理、公式及代码演示

之前的章节我们介绍过,iLQR(迭代线性二次调节器)是一种用于求解非线性系统最优控制最优控制最优控制和规划问题的算法。本章节介绍采用iLQR算法对设定的自动驾驶轨迹进行跟踪,与第十章节纯跟踪算法采用同样跟踪轨迹,同时,我们仅对控制量的上下边界进行约束,使用简单的投影法进行约束(更详细的约束参考第九章 CILQR约束条件下的ILQR求解)。其实,iLQR可以直接进行轨迹规划,主要做法是将障碍物或者边界约束通过增广拉格朗日法将原始问题的约束条件通过拉格朗日乘子和惩罚项结合到代价函数中,逐步逼近最优解,这个将在后续章节进行讨论。

1. 问题定义

假设给定一个目标轨迹(x_{ref}, u_{ref}),其中x_{ref}是状态轨迹,u_{ref}是控制输入,任务是找到一组控制输入u_k使得车辆从初始状态出发,最小化实际轨迹与目标轨迹之间的偏差。

(1) 状态方程

车辆的运动可以通过车辆动力学模型表示,通常有以下几种模型:

  • 简化运动学模型

    x_{k+1} = f(x_k, u_k)

    其中x_k是车辆的状态,u_k是控制输入。

根据参考轨迹,将非线性系统模型在当前状态和控制输入附近进行线性化:

f(x_k, u_k) \approx A_k \Delta x_k + B_k \Delta u_k + c_k

其中A_k = \frac{\partial f}{\partial x}B_k = \frac{\partial f}{\partial u}是对状态和控制的线性化,\Delta x_k = x_k - x_{ref}\Delta u_k = u_k - u_{ref}​。

(2) 代价函数

定义一个二次型代价函数,用于衡量实际轨迹与目标轨迹的偏差:

J = \sum_{k=0}^{N-1} \left( (x_k - x_{ref})^T Q (x_k - x_{ref}) + (u_k - u_{ref})^T R (u_k - u_{ref}) \right) + (x_N - x_{ref})^T Q_f (x_N - x_{ref})

其中QRQ_f分别是状态、控制输入和最终状态的权重矩阵。

(3) 反向递推

利用动态规划方法,从最终时刻 N开始,向前递推计算值函数的梯度和Hessian矩阵:

V_x = \frac{\partial J}{\partial x}, \quad V_{xx} = \frac{\partial^2 J}{\partial x^2}

同时计算控制增量的最优更新策略:

\Delta u_k = K_k \Delta x_k + d_k

其中K_k是控制增量的反馈增益,d_k是前馈控制增量。

(4) 前向传播

基于反向递推得到的最优控制策略,在给定初始状态下,通过前向传播计算新的状态和控制轨迹,更新参考轨迹:

x_{k+1} = f(x_k, u_k)

重复步骤(1)至(4)直到收敛,得到最优控制序列。

2. 示例代码

import numpy as np
import math
import matplotlib.pyplot as plt
import imageio

# 车辆模型
class Vehicle:
    def __init__(self, x=0.0, y=0.0, theta=0.0, v=0.0):
        self.x = x
        self.y = y
        self.theta = theta  # 航向角
        self.v = v          # 速度

    def update(self, a, omega, dt):
        """
        更新车辆状态
        a: 加速度
        omega: 角速度
        dt: 时间步长
        """
        self.x += self.v * np.cos(self.theta) * dt
        self.y += self.v * np.sin(self.theta) * dt
        self.theta += omega * dt
        self.v += a * dt

# 轨迹
class Trajectory:
    def __init__(self):
        self.cx = np.linspace(0, 50, 500)
        self.cy = [np.sin(ix / 5.0) * ix / 2.0 for ix in self.cx]
        self.theta = [np.arctan2(self.cy[i+1] - self.cy[i], self.cx[i+1] - self.cx[i]) if i < len(self.cx)-1 else 0.0 for i in range(len(self.cx))]
        self.v = np.full_like(self.cx, 3.0)  # 目标速度为3 m/s

    def get_reference(self, index):
        """
        获取参考轨迹点
        """
        return np.array([self.cx[index], self.cy[index], self.theta[index], self.v[index]])

# iLQR控制器
class iLQRController:
    def __init__(self, N=50, max_iter=10, dt=0.1):
        self.N = N  # 控制时域长度
        self.max_iter = max_iter  # 最大迭代次数
        self.dt = dt  # 时间步长
        self.Q = np.diag([1.0, 1.0, 0.5, 0.1])  # 状态权重矩阵
        self.R = np.diag([0.1, 0.1])  # 控制权重矩阵
        self.Qf = self.Q * 10  # 终端状态权重矩阵

    def ilqr(self, vehicle, trajectory, index):
        """
        使用iLQR计算最优控制序列
        """
        # 初始化状态和控制序列
        x_dim = 4  # 状态维度 [x, y, theta, v]
        u_dim = 2  # 控制维度 [a, omega]
        xs = np.zeros((self.N + 1, x_dim))
        us = np.zeros((self.N, u_dim))
        
        # 初始状态
        xs[0] = np.array([vehicle.x, vehicle.y, vehicle.theta, vehicle.v])
        
        # 初始猜测控制序列(全零)
        us = np.zeros((self.N, u_dim))
        
        for iteration in range(self.max_iter):
            # 前向传播,计算状态轨迹
            for k in range(self.N):
                xs[k+1] = self.dynamics(xs[k], us[k], self.dt)
            
            # 计算代价函数梯度和Hessian矩阵
            Vx = self.Qf @ (xs[-1] - trajectory.get_reference(index + self.N))
            Vxx = self.Qf
            k_list = []
            K_list = []
            for k in reversed(range(self.N)):
                xk = xs[k]
                uk = us[k]
                x_ref = trajectory.get_reference(index + k)
                # 计算状态和控制的偏导数
                fx, fu = self.linearize_dynamics(xk, uk, self.dt)
                lx = self.Q @ (xk - x_ref)
                lu = self.R @ uk
                lxx = self.Q
                luu = self.R
                lux = np.zeros((u_dim, x_dim))
                # 计算Q函数的二次近似
                Qx = lx + fx.T @ Vx
                Qu = lu + fu.T @ Vx
                Qxx = lxx + fx.T @ Vxx @ fx
                Quu = luu + fu.T @ Vxx @ fu
                Qux = lux + fu.T @ Vxx @ fx
                # 计算控制增量
                Quu_inv = np.linalg.inv(Quu + np.eye(u_dim) * 1e-6)  # 加小值防止矩阵奇异
                k = -Quu_inv @ Qu
                K = -Quu_inv @ Qux
                # 更新V函数
                Vx = Qx + K.T @ Quu @ k + K.T @ Qu + Qux.T @ k
                Vxx = Qxx + K.T @ Quu @ K + K.T @ Qux + Qux.T @ K
                # 存储k和K
                k_list.insert(0, k)
                K_list.insert(0, K)
            # 更新控制序列并前向模拟
            x_new = np.copy(xs[0])
            xs_new = [x_new]
            us_new = []
            for k in range(self.N):
                du = k_list[k] + K_list[k] @ (x_new - xs[k])
                us_new.append(us[k] + du)
                x_new = self.dynamics(x_new, us_new[-1], self.dt)
                xs_new.append(x_new)
            xs = np.array(xs_new)
            us = np.array(us_new)
            # 判断收敛性
            cost = self.compute_total_cost(xs, us, trajectory, index)
            print(f"Iteration {iteration}, Cost: {cost}")
            if cost < 1e-6:
                break
        return us[0]  # 返回当前时刻的最优控制

    def dynamics(self, x, u, dt):
        """
        动力学模型
        """
        x_next = np.zeros_like(x)
        x_next[0] = x[0] + x[3] * np.cos(x[2]) * dt  # x
        x_next[1] = x[1] + x[3] * np.sin(x[2]) * dt  # y
        x_next[2] = x[2] + u[1] * dt                 # theta
        x_next[3] = x[3] + u[0] * dt                 # v
        return x_next

    def linearize_dynamics(self, x, u, dt):
        """
        线性化动力学模型,返回状态和控制的雅可比矩阵
        """
        fx = np.eye(4)
        fx[0, 2] = -x[3] * np.sin(x[2]) * dt
        fx[0, 3] = np.cos(x[2]) * dt
        fx[1, 2] = x[3] * np.cos(x[2]) * dt
        fx[1, 3] = np.sin(x[2]) * dt
        fx[2, 2] = 1.0
        fx[3, 3] = 1.0

        fu = np.zeros((4, 2))
        fu[2, 1] = dt  # theta 对 omega 的偏导
        fu[3, 0] = dt  # v 对 a 的偏导

        return fx, fu

    def compute_total_cost(self, xs, us, trajectory, index):
        """
        计算总的代价函数
        """
        cost = 0.0
        for k in range(self.N):
            xk = xs[k]
            uk = us[k]
            x_ref = trajectory.get_reference(index + k)
            dx = xk - x_ref
            cost += dx.T @ self.Q @ dx + uk.T @ self.R @ uk
        # 终端代价
        dx = xs[-1] - trajectory.get_reference(index + self.N)
        cost += dx.T @ self.Qf @ dx
        return cost

# 主函数
def main():
    vehicle = Vehicle()
    trajectory = Trajectory()
    controller = iLQRController(N=50, max_iter=10, dt=0.1)
    dt = 0.1
    x_history = []
    y_history = []
    total_time = len(trajectory.cx) - controller.N - 1

    # 创建图形
    fig, ax = plt.subplots()
    frames = []

    for t in range(total_time):
        # 获取当前最优控制
        u_opt = controller.ilqr(vehicle, trajectory, t)
        # 更新车辆状态
        vehicle.update(u_opt[0], u_opt[1], dt)
        # 记录轨迹
        x_history.append(vehicle.x)
        y_history.append(vehicle.y)

        # 绘制
        ax.cla()
        ax.plot(trajectory.cx, trajectory.cy, "-r", label="Reference Trajectory")
        ax.plot(x_history, y_history, "-b", label="Vehicle Trajectory")
        ax.set_xlim(0, 50)
        ax.set_ylim(-20, 25)
        ax.set_title(f"iLQR Trajectory Tracking - Step {t}")
        ax.set_xlabel("x [m]")
        ax.set_ylabel("y [m]")
        ax.grid(True)

        # 渲染当前帧
        fig.canvas.draw()
        image = np.frombuffer(fig.canvas.tostring_rgb(), dtype='uint8').reshape(fig.canvas.get_width_height()[::-1] + (3,))
        frames.append(image)

        # 实时显示
        plt.pause(0.001)

    # 保存为GIF
    imageio.mimsave('ilqr_trajectory_tracking.gif', frames, fps=10)
    plt.show()

if __name__ == '__main__':
    main()

2.1. 车辆模型 (Vehicle 类)

车辆模型采用一个简单的运动学模型,其中更新车辆的状态包括:

位置 (x, y)
航向角 (theta)
速度 (v)
通过给定加速度 (a) 和角速度 (omega),每个时间步长内,车辆的状态都会更新。

class Vehicle:
    def __init__(self, x=0.0, y=0.0, theta=0.0, v=0.0):
        self.x = x
        self.y = y
        self.theta = theta  # 航向角
        self.v = v          # 速度

    def update(self, a, omega, dt):
        """
        更新车辆状态
        a: 加速度
        omega: 角速度
        dt: 时间步长
        """
        self.x += self.v * np.cos(self.theta) * dt
        self.y += self.v * np.sin(self.theta) * dt
        self.theta += omega * dt
        self.v += a * dt

2. 轨迹 (Trajectory 类) 

轨迹类生成了一条目标轨迹,cx 和 cy 分别是参考轨迹的 x 和 y 轴坐标。车辆的目标是跟随这条正弦曲线,并在参考轨迹的每个点上都有相应的目标航向角 (theta) 和速度 (v)。

# 轨迹
class Trajectory:
    def __init__(self):
        self.cx = np.linspace(0, 50, 500)
        self.cy = [np.sin(ix / 5.0) * ix / 2.0 for ix in self.cx]
        self.theta = [np.arctan2(self.cy[i+1] - self.cy[i], self.cx[i+1] - self.cx[i]) if i < len(self.cx)-1 else 0.0 for i in range(len(self.cx))]
        self.v = np.full_like(self.cx, 3.0)  # 目标速度为3 m/s

    def get_reference(self, index):
        """
        获取参考轨迹点
        """
        return np.array([self.cx[index], self.cy[index], self.theta[index], self.v[index]])

3. iLQR 控制器 (iLQRController 类) 

iLQR 控制器的主要职责是计算从当前时刻开始的最优控制序列,使得车辆的状态逼近目标轨迹。以下是关键步骤:

初始化:定义状态和控制权重矩阵 (Q, R, Qf) 以惩罚偏离目标状态和过大的控制输入。
前向传播:给定初始控制输入,模拟车辆状态随时间的演化。
反向递推:通过动态规划的方法,逐步计算代价函数的梯度和 Hessian,并优化控制策略。
线性化动力学:在当前状态下线性化系统的动力学方程,计算状态和控制的雅可比矩阵。
更新控制序列:通过更新控制增量,生成新的控制序列并前向传播,直到算法收敛。

# iLQR控制器
class iLQRController:
    def __init__(self, N=50, max_iter=10, dt=0.1):
        self.N = N  # 控制时域长度
        self.max_iter = max_iter  # 最大迭代次数
        self.dt = dt  # 时间步长
        self.Q = np.diag([1.0, 1.0, 0.5, 0.1])  # 状态权重矩阵
        self.R = np.diag([0.1, 0.1])  # 控制权重矩阵
        self.Qf = self.Q * 10  # 终端状态权重矩阵

    def ilqr(self, vehicle, trajectory, index):
        """
        使用iLQR计算最优控制序列
        """
        # 初始化状态和控制序列
        x_dim = 4  # 状态维度 [x, y, theta, v]
        u_dim = 2  # 控制维度 [a, omega]
        xs = np.zeros((self.N + 1, x_dim))
        us = np.zeros((self.N, u_dim))
        
        # 初始状态
        xs[0] = np.array([vehicle.x, vehicle.y, vehicle.theta, vehicle.v])
        
        # 初始猜测控制序列(全零)
        us = np.zeros((self.N, u_dim))
        
        for iteration in range(self.max_iter):
            # 前向传播,计算状态轨迹
            for k in range(self.N):
                xs[k+1] = self.dynamics(xs[k], us[k], self.dt)
            
            # 计算代价函数梯度和Hessian矩阵
            Vx = self.Qf @ (xs[-1] - trajectory.get_reference(index + self.N))
            Vxx = self.Qf
            k_list = []
            K_list = []
            for k in reversed(range(self.N)):
                xk = xs[k]
                uk = us[k]
                x_ref = trajectory.get_reference(index + k)
                # 计算状态和控制的偏导数
                fx, fu = self.linearize_dynamics(xk, uk, self.dt)
                lx = self.Q @ (xk - x_ref)
                lu = self.R @ uk
                lxx = self.Q
                luu = self.R
                lux = np.zeros((u_dim, x_dim))
                # 计算Q函数的二次近似
                Qx = lx + fx.T @ Vx
                Qu = lu + fu.T @ Vx
                Qxx = lxx + fx.T @ Vxx @ fx
                Quu = luu + fu.T @ Vxx @ fu
                Qux = lux + fu.T @ Vxx @ fx
                # 计算控制增量
                Quu_inv = np.linalg.inv(Quu + np.eye(u_dim) * 1e-6)  # 加小值防止矩阵奇异
                k = -Quu_inv @ Qu
                K = -Quu_inv @ Qux
                # 更新V函数
                Vx = Qx + K.T @ Quu @ k + K.T @ Qu + Qux.T @ k
                Vxx = Qxx + K.T @ Quu @ K + K.T @ Qux + Qux.T @ K
                # 存储k和K
                k_list.insert(0, k)
                K_list.insert(0, K)
            # 更新控制序列并前向模拟
            x_new = np.copy(xs[0])
            xs_new = [x_new]
            us_new = []
            for k in range(self.N):
                du = k_list[k] + K_list[k] @ (x_new - xs[k])
                us_new.append(us[k] + du)
                x_new = self.dynamics(x_new, us_new[-1], self.dt)
                xs_new.append(x_new)
            xs = np.array(xs_new)
            us = np.array(us_new)
            # 判断收敛性
            cost = self.compute_total_cost(xs, us, trajectory, index)
            print(f"Iteration {iteration}, Cost: {cost}")
            if cost < 1e-6:
                break
        return us[0]  # 返回当前时刻的最优控制

    def dynamics(self, x, u, dt):
        """
        动力学模型
        """
        x_next = np.zeros_like(x)
        x_next[0] = x[0] + x[3] * np.cos(x[2]) * dt  # x
        x_next[1] = x[1] + x[3] * np.sin(x[2]) * dt  # y
        x_next[2] = x[2] + u[1] * dt                 # theta
        x_next[3] = x[3] + u[0] * dt                 # v
        return x_next

    def linearize_dynamics(self, x, u, dt):
        """
        线性化动力学模型,返回状态和控制的雅可比矩阵
        """
        fx = np.eye(4)
        fx[0, 2] = -x[3] * np.sin(x[2]) * dt
        fx[0, 3] = np.cos(x[2]) * dt
        fx[1, 2] = x[3] * np.cos(x[2]) * dt
        fx[1, 3] = np.sin(x[2]) * dt
        fx[2, 2] = 1.0
        fx[3, 3] = 1.0

        fu = np.zeros((4, 2))
        fu[2, 1] = dt  # theta 对 omega 的偏导
        fu[3, 0] = dt  # v 对 a 的偏导

        return fx, fu

    def compute_total_cost(self, xs, us, trajectory, index):
        """
        计算总的代价函数
        """
        cost = 0.0
        for k in range(self.N):
            xk = xs[k]
            uk = us[k]
            x_ref = trajectory.get_reference(index + k)
            dx = xk - x_ref
            cost += dx.T @ self.Q @ dx + uk.T @ self.R @ uk
        # 终端代价
        dx = xs[-1] - trajectory.get_reference(index + self.N)
        cost += dx.T @ self.Qf @ dx
        return cost

4. 主循环 (main 函数)

主函数执行轨迹跟踪的模拟过程。每一步:

调用 iLQR 控制器生成最优控制输入。
根据控制输入更新车辆状态。
绘制当前的车辆轨迹与参考轨迹,并生成一帧图像。
最终,所有帧被保存为 GIF 文件,展示整个轨迹跟踪过程。

5. 执行结果

6. c++版本

在实际的工程开发中,一般采用c++代码进行开发。为了方便应用,将上述代码转为C++,涉及的矩阵运算主要采用eigen库(参考 C++教程(一):超详细的C++矩阵操作和运算(附实例代码,与python对比))。同时为了方便展示动态运行过程,采用了python的matplotlib的c++库matplotlibcpp,也非常容易使用,直接include头文件即可。C++代码如下:

#include <iostream>
#include <vector>
#include <cmath>
#include <Eigen/Dense>
#include "matplotlibcpp.h"

namespace plt = matplotlibcpp;
using Eigen::MatrixXd;
using Eigen::VectorXd;

// 车辆模型类
class Vehicle {
public:
    double x, y, theta, v;

    // 车辆构造函数,初始化车辆的状态
    Vehicle(double x_init = 0.0, double y_init = 0.0, double theta_init = 0.0, double v_init = 0.0)
        : x(x_init), y(y_init), theta(theta_init), v(v_init) {}

    // 更新车辆状态,a为加速度,omega为角速度,dt为时间步长
    void update(double a, double omega, double dt) {
        x += v * cos(theta) * dt;    // 更新x坐标
        y += v * sin(theta) * dt;    // 更新y坐标
        theta += omega * dt;         // 更新航向角theta
        v += a * dt;                 // 更新速度v
    }
};

// 轨迹类,用于存储参考轨迹信息
class Trajectory {
public:
    std::vector<double> cx, cy, theta, v;

    // 轨迹构造函数,初始化参考轨迹
    Trajectory() {
        // 生成参考轨迹的x和y坐标
        for (double i = 0; i < 50; i += 0.1) {
            cx.push_back(i);
            cy.push_back(sin(i / 5.0) * i / 2.0);
        }
        // 计算每个轨迹点的航向角theta
        for (size_t i = 0; i < cx.size() - 1; ++i) {
            theta.push_back(atan2(cy[i+1] - cy[i], cx[i+1] - cx[i]));
        }
        theta.push_back(0.0);  // 最后一个点的theta设置为0
        v.assign(cx.size(), 3.0);  // 设置所有点的目标速度为3 m/s
    }

    // 获取指定索引处的参考状态向量[x, y, theta, v]
    VectorXd get_reference(size_t index) const {
        VectorXd ref(4);
        ref << cx[index], cy[index], theta[index], v[index];
        return ref;
    }
};

// iLQR(迭代线性二次调节器)控制器类
class iLQRController {
public:
    int N;             // 控制步数
    int max_iter;      // 最大迭代次数
    double dt;         // 时间步长
    MatrixXd Q, R, Qf; // 代价函数的权重矩阵

    // 控制器构造函数
    iLQRController(int N_input = 50, int max_iter_input = 10, double dt_input = 0.1)
        : N(N_input), max_iter(max_iter_input), dt(dt_input) {
        // 初始化状态代价矩阵Q,控制代价矩阵R,终端状态代价矩阵Qf
        Q = MatrixXd::Zero(4, 4);
        Q(0, 0) = 1.0; Q(1, 1) = 1.0; Q(2, 2) = 0.5; Q(3, 3) = 0.1;
        R = MatrixXd::Identity(2, 2) * 0.1;
        Qf = Q * 10.0;
    }

    // iLQR算法的实现,返回当前时刻的最优控制输入
    VectorXd ilqr(Vehicle &vehicle, const Trajectory &trajectory, size_t index) {
        int x_dim = 4;  // 状态维度 [x, y, theta, v]
        int u_dim = 2;  // 控制维度 [a, omega]
        std::vector<VectorXd> xs(N + 1, VectorXd::Zero(x_dim));  // 状态序列
        std::vector<VectorXd> us(N, VectorXd::Zero(u_dim));      // 控制序列

        // 初始化状态
        xs[0] << vehicle.x, vehicle.y, vehicle.theta, vehicle.v;

        // 迭代更新控制输入和状态
        for (int iter = 0; iter < max_iter; ++iter) {
            // 前向传播,基于当前控制序列预测状态序列
            for (int i = 0; i < N; ++i) {
                xs[i + 1] = dynamics(xs[i], us[i], dt);
            }

            // 反向传播,更新控制增量和反馈矩阵
            VectorXd Vx = Qf * (xs[N] - trajectory.get_reference(index + N));
            MatrixXd Vxx = Qf;

            std::vector<VectorXd> k_control_list(N, VectorXd::Zero(u_dim));  // 控制增量
            std::vector<MatrixXd> K_feedback_list(N, MatrixXd::Zero(u_dim, x_dim));  // 反馈矩阵

            for (int i = N - 1; i >= 0; --i) {
                VectorXd x_ref = trajectory.get_reference(index + i);
                std::pair<MatrixXd, MatrixXd> linearized = linearize_dynamics(xs[i], us[i], dt);
                MatrixXd fx = linearized.first;
                MatrixXd fu = linearized.second;

                // 计算代价梯度和Hessian矩阵
                VectorXd lx = Q * (xs[i] - x_ref);
                VectorXd lu = R * us[i];
                MatrixXd lxx = Q;
                MatrixXd luu = R;
                MatrixXd lux = MatrixXd::Zero(u_dim, x_dim);

                // 计算Q函数的二次近似
                VectorXd Qx = lx + fx.transpose() * Vx;
                VectorXd Qu = lu + fu.transpose() * Vx;
                MatrixXd Qxx = lxx + fx.transpose() * Vxx * fx;
                MatrixXd Quu = luu + fu.transpose() * Vxx * fu;
                MatrixXd Qux = lux + fu.transpose() * Vxx * fx;

                // 计算控制增量和反馈矩阵
                MatrixXd Quu_inv = Quu.inverse();
                VectorXd k_control = -Quu_inv * Qu;
                MatrixXd K_feedback = -Quu_inv * Qux;

                // 更新价值函数
                Vx = Qx + K_feedback.transpose() * Quu * k_control + K_feedback.transpose() * Qu + Qux.transpose() * k_control;
                Vxx = Qxx + K_feedback.transpose() * Quu * K_feedback + K_feedback.transpose() * Qux + Qux.transpose() * K_feedback;

                k_control_list[i] = k_control;
                K_feedback_list[i] = K_feedback;
            }

            // 更新控制序列并进行前向模拟
            std::vector<VectorXd> xs_new(N + 1, xs[0]);
            std::vector<VectorXd> us_new;

            for (int i = 0; i < N; ++i) {
                VectorXd du = k_control_list[i] + K_feedback_list[i] * (xs_new[i] - xs[i]);
                us_new.push_back(us[i] + du);
                xs_new[i + 1] = dynamics(xs_new[i], us_new.back(), dt);
            }
            xs = xs_new;
            us = us_new;

            // 判断是否收敛
            double cost = compute_total_cost(xs, us, trajectory, index);
            std::cout << "Iteration " << iter << ", Cost: " << cost << std::endl;
            if (cost < 1e-6) {
                break;
            }
        }
        return us[0];  // 返回当前时刻的最优控制输入
    }

    // 车辆动力学模型
    VectorXd dynamics(const VectorXd &x, const VectorXd &u, double dt) {
        VectorXd x_next = x;
        x_next[0] += x[3] * cos(x[2]) * dt;  // 更新x坐标
        x_next[1] += x[3] * sin(x[2]) * dt;  // 更新y坐标
        x_next[2] += u[1] * dt;              // 更新theta
        x_next[3] += u[0] * dt;              // 更新速度v
        return x_next;
    }

    // 线性化车辆动力学模型
    std::pair<MatrixXd, MatrixXd> linearize_dynamics(const VectorXd &x, const VectorXd &u, double dt) {
        MatrixXd fx = MatrixXd::Identity(4, 4);
        fx(0, 2) = -x[3] * sin(x[2]) * dt;
        fx(0, 3) = cos(x[2]) * dt;
        fx(1, 2) = x[3] * cos(x[2]) * dt;
        fx(1, 3) = sin(x[2]) * dt;

        MatrixXd fu = MatrixXd::Zero(4, 2);
        fu(2, 1) = dt;
        fu(3, 0) = dt;

        return {fx, fu};
    }

    // 计算总成本
    double compute_total_cost(const std::vector<VectorXd> &xs, const std::vector<VectorXd> &us, const Trajectory &trajectory, size_t index) {
        double cost = 0.0;
        for (int i = 0; i < N; ++i) {
            VectorXd dx = xs[i] - trajectory.get_reference(index + i);
            cost += (dx.transpose() * Q * dx)(0, 0) + (us[i].transpose() * R * us[i])(0, 0);
        }
        VectorXd dx_terminal = xs[N] - trajectory.get_reference(index + N);
        cost += (dx_terminal.transpose() * Qf * dx_terminal)(0, 0);
        return cost;
    }
};

// 主函数
int main() {
    Vehicle vehicle;  // 初始化车辆
    Trajectory trajectory;  // 初始化轨迹
    iLQRController controller(10, 10, 0.1);  // 初始化iLQR控制器
    double dt = 0.1;
    std::vector<double> x_history, y_history;  // 记录车辆轨迹

    for (size_t t = 0; t < trajectory.cx.size() - controller.N - 1; ++t) {
        VectorXd u_opt = controller.ilqr(vehicle, trajectory, t);  // 获取最优控制输入
        vehicle.update(u_opt[0], u_opt[1], dt);  // 更新车辆状态
        x_history.push_back(vehicle.x);  // 记录x坐标
        y_history.push_back(vehicle.y);  // 记录y坐标

        // 绘制车辆轨迹和参考轨迹
        plt::clf();
        plt::named_plot("Reference Trajectory", trajectory.cx, trajectory.cy, "-r");
        plt::named_plot("Vehicle Trajectory", x_history, y_history, "-b");
        plt::legend();
        plt::xlim(0, 50);
        plt::ylim(-20, 25);
        plt::title("iLQR Trajectory Tracking");
        plt::xlabel("x [m]");
        plt::ylabel("y [m]");
        plt::grid(true);
        plt::pause(0.001);  // 短暂暂停以动态展示
    }

    plt::show();  // 展示最终图形
    return 0;
}

 6. 后续优化

加入障碍物回避:在代价函数中加入障碍物相关的约束项,直接进行规控一体优化;

采用全DDP算法:考虑系统动态中的二阶项,通过全DDP算法提升优化精度;

噪声处理:考虑车辆运动中的过程噪声和观测噪声,用iLQG使得算法更加鲁棒。


http://www.kler.cn/a/319063.html

相关文章:

  • 陪诊问诊APP开发实战:基于互联网医院系统源码的搭建详解
  • 【大数据测试HBase数据库 — 详细教程(含实例与监控调优)】
  • HarmonyOS Next星河版笔记--界面开发(4)
  • ubuntu ros 解决建完图后 保存的地图非常小的问题
  • Vim9 语法高亮syntax 在指定的缓冲区和窗口执行命令
  • GitLab 如何跨版本升级?
  • 精益六西格玛管理实践中如何保证小组成员的稳定性?
  • Spring定时任务 - @Scheduled注解详解
  • IDEA相关设置总结
  • (11)iptables-仅开放指定ip访问指定端口
  • 飞腾平台perf工具PMU事件集成指南
  • 一分钟掌握 Java15 新特性
  • StringReader 使用 JAXB自动将 XML 数据映射到 Java 对象
  • Nginx 限流实战教程和技巧
  • Vue3 Day7-全局组件、指令以及pinia
  • uniapp app 端通过webview引入外部 js , webview 与 app 通信
  • spring-boot-maven-plugin插件打包和java -jar命令执行原理
  • [研发工具箱] 系列3.机电类常用的分类网站
  • Android开发拍身份证带人像框和国徽框效果
  • Spring 全家桶使用教程
  • 问题:机器字长为n位的二进制数可以用补码来表示()个不同的有符号定点整数。
  • oracle 数据库中的异常和游标管理
  • SpringBoot开发——实现WORD文件的导入导出
  • ElasticJob个人总结
  • Python(爬虫)正则表达式
  • python-比较月亮大小/数组下标/人见人爱a+b