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

news/2024/9/19 11:48:00/ 标签: 自动驾驶, 人工智能, 机器学习

最优化理论与自动驾驶(四):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 = xself.y = yself.theta = theta  # 航向角self.v = v          # 速度def update(self, a, omega, dt):"""更新车辆状态a: 加速度omega: 角速度dt: 时间步长"""self.x += self.v * np.cos(self.theta) * dtself.y += self.v * np.sin(self.theta) * dtself.theta += omega * dtself.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/sdef 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.Qfk_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 @ uklxx = self.Qluu = self.Rlux = np.zeros((u_dim, x_dim))# 计算Q函数的二次近似Qx = lx + fx.T @ VxQu = lu + fu.T @ VxQxx = lxx + fx.T @ Vxx @ fxQuu = luu + fu.T @ Vxx @ fuQux = lux + fu.T @ Vxx @ fx# 计算控制增量Quu_inv = np.linalg.inv(Quu + np.eye(u_dim) * 1e-6)  # 加小值防止矩阵奇异k = -Quu_inv @ QuK = -Quu_inv @ Qux# 更新V函数Vx = Qx + K.T @ Quu @ k + K.T @ Qu + Qux.T @ kVxx = Qxx + K.T @ Quu @ K + K.T @ Qux + Qux.T @ K# 存储k和Kk_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:breakreturn 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  # xx_next[1] = x[1] + x[3] * np.sin(x[2]) * dt  # yx_next[2] = x[2] + u[1] * dt                 # thetax_next[3] = x[3] + u[0] * dt                 # vreturn x_nextdef linearize_dynamics(self, x, u, dt):"""线性化动力学模型,返回状态和控制的雅可比矩阵"""fx = np.eye(4)fx[0, 2] = -x[3] * np.sin(x[2]) * dtfx[0, 3] = np.cos(x[2]) * dtfx[1, 2] = x[3] * np.cos(x[2]) * dtfx[1, 3] = np.sin(x[2]) * dtfx[2, 2] = 1.0fx[3, 3] = 1.0fu = np.zeros((4, 2))fu[2, 1] = dt  # theta 对 omega 的偏导fu[3, 0] = dt  # v 对 a 的偏导return fx, fudef compute_total_cost(self, xs, us, trajectory, index):"""计算总的代价函数"""cost = 0.0for k in range(self.N):xk = xs[k]uk = us[k]x_ref = trajectory.get_reference(index + k)dx = xk - x_refcost += dx.T @ self.Q @ dx + uk.T @ self.R @ uk# 终端代价dx = xs[-1] - trajectory.get_reference(index + self.N)cost += dx.T @ self.Qf @ dxreturn cost# 主函数
def main():vehicle = Vehicle()trajectory = Trajectory()controller = iLQRController(N=50, max_iter=10, dt=0.1)dt = 0.1x_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)# 保存为GIFimageio.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 = xself.y = yself.theta = theta  # 航向角self.v = v          # 速度def update(self, a, omega, dt):"""更新车辆状态a: 加速度omega: 角速度dt: 时间步长"""self.x += self.v * np.cos(self.theta) * dtself.y += self.v * np.sin(self.theta) * dtself.theta += omega * dtself.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/sdef 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.Qfk_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 @ uklxx = self.Qluu = self.Rlux = np.zeros((u_dim, x_dim))# 计算Q函数的二次近似Qx = lx + fx.T @ VxQu = lu + fu.T @ VxQxx = lxx + fx.T @ Vxx @ fxQuu = luu + fu.T @ Vxx @ fuQux = lux + fu.T @ Vxx @ fx# 计算控制增量Quu_inv = np.linalg.inv(Quu + np.eye(u_dim) * 1e-6)  # 加小值防止矩阵奇异k = -Quu_inv @ QuK = -Quu_inv @ Qux# 更新V函数Vx = Qx + K.T @ Quu @ k + K.T @ Qu + Qux.T @ kVxx = Qxx + K.T @ Quu @ K + K.T @ Qux + Qux.T @ K# 存储k和Kk_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:breakreturn 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  # xx_next[1] = x[1] + x[3] * np.sin(x[2]) * dt  # yx_next[2] = x[2] + u[1] * dt                 # thetax_next[3] = x[3] + u[0] * dt                 # vreturn x_nextdef linearize_dynamics(self, x, u, dt):"""线性化动力学模型,返回状态和控制的雅可比矩阵"""fx = np.eye(4)fx[0, 2] = -x[3] * np.sin(x[2]) * dtfx[0, 3] = np.cos(x[2]) * dtfx[1, 2] = x[3] * np.cos(x[2]) * dtfx[1, 3] = np.sin(x[2]) * dtfx[2, 2] = 1.0fx[3, 3] = 1.0fu = np.zeros((4, 2))fu[2, 1] = dt  # theta 对 omega 的偏导fu[3, 0] = dt  # v 对 a 的偏导return fx, fudef compute_total_cost(self, xs, us, trajectory, index):"""计算总的代价函数"""cost = 0.0for k in range(self.N):xk = xs[k]uk = us[k]x_ref = trajectory.get_reference(index + k)dx = xk - x_refcost += dx.T @ self.Q @ dx + uk.T @ self.R @ uk# 终端代价dx = xs[-1] - trajectory.get_reference(index + self.N)cost += dx.T @ self.Qf @ dxreturn 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;         // 更新航向角thetav += 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);}// 计算每个轨迹点的航向角thetafor (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设置为0v.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,终端状态代价矩阵QfQ = 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;              // 更新thetax_next[3] += u[0] * dt;              // 更新速度vreturn 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.ppmy.cn/news/1527626.html

相关文章

使用阿里OCR身份证识别

1、开通服务 免费试用 2、获取accesskay AccessKeyId和AccessKeySecret 要同时复制保存下来 因为后面好像看不AccessKeySecret了 3.Api 参考 https://help.aliyun.com/zh/ocr/developer-reference/api-ocr-api-2021-07-07-recognizeidcard?spma2c4g.11186623.0.0.7a9f4b1e5C…

STM32快速复习(十二)FLASH闪存的读写

文章目录 一、FLASH是什么&#xff1f;FLASH的结构&#xff1f;二、使用步骤1.标准库函数2.示例函数 总结 一、FLASH是什么&#xff1f;FLASH的结构&#xff1f; 1、FLASH简介 &#xff08;1&#xff09;STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&…

AcWing算法基础课-789数的范围-Java题解

大家好&#xff0c;我是何未来&#xff0c;本篇文章给大家讲解《AcWing算法基础课》789 题——数的范围。本文详细解析了一个基于二分查找的算法题&#xff0c;题目要求在有序数组中查找特定元素的首次和最后一次出现的位置。通过使用两个二分查找函数&#xff0c;程序能够高效…

随机规划及其MATLAB实现

目录 引言 随机规划的基本模型 随机动态规划 随机动态规划建模实例​(随机动态规划)&#xff1a; MATLAB中的随机规划实现 示例&#xff1a;两阶段随机规划 表格总结&#xff1a;随机规划求解方法与适用场景 结论 引言 随机规划&#xff08;Stochastic Programming&…

VulhubDC-4靶机详解

项目地址 https://download.vulnhub.com/dc/DC-4.zip实验过程 将下载好的靶机导入到VMware中&#xff0c;设置网络模式为NAT模式&#xff0c;然后开启靶机虚拟机 使用nmap进行主机发现&#xff0c;获取靶机IP地址 nmap 192.168.47.1-254根据对比可知DC-4的一个ip地址为192.1…

C++——多态的原理

多态的原理 多态的原理引入虚函数表 多态的原理 引入 如下代码的输出结果为&#xff08;&#xff09; A.编译报错 B.运行报错 C.8 D.12 上⾯题⽬运⾏结果12bytes&#xff0c;除了_b和_ch成员&#xff0c;还多⼀个__vfptr放对象的前⾯(注意有些平台可能会放到对象的最后⾯&am…

web基础—dvwa靶场(七)SQL Injection

SQL Injection&#xff08;SQL注入&#xff09; SQL Injection&#xff08;SQL注入&#xff09;&#xff0c;是指攻击者通过注入恶意的SQL命令&#xff0c;破坏SQL查询语句的结构&#xff0c;从而达到执行恶意SQL语句的目的。SQL注入漏洞的危害是巨大的&#xff0c;常常会导致…

AI绘画:科技赋能艺术的崭新时代

&#x1f4af;AI绘画&#xff1a;走进艺术创新的新时代 人工智能在改变世界的过程中&#xff0c;AI绘画工具逐渐成为创新的典范。 本文将为您揭示AI绘画背后的技术秘密、潜在的应用场景&#xff0c;并为您推荐几款出色的AI绘画工具&#xff0c;助您领略这一技术带来的艺术新体…

git bash中执行java命令乱码问题处理

Git Bash中执行java命令显示乱码 购机自带windows字符集为gbk&#xff0c;git bash默认为utf8&#xff0c;导致中文字符显示乱码 处理方案如下 顶部右键点击Options 选择Text&#xff0c;更换字符集即可

彩蛋岛 销冠大模型案例

彩蛋岛 销冠大模型案例 任务&#xff1a; https://kkgithub.com/InternLM/Tutorial/tree/camp3/docs/EasterEgg/StreamerSales 视频 https://www.bilibili.com/video/BV1f1421b7Du/?vd_source4ffecd6d839338c9390829e56a43ca8d 项目git地址&#xff1a; https://kkgithu…

VideoPlayer插件的用法

文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取文件类型"相关的内容&#xff0c;本章回中将介绍如何播放视频.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用…

[深度学习]Pytorch框架

1 深度学习简介 应用领域&#xff1a;语音交互、文本处理、计算机视觉、深度学习、人机交互、知识图谱、分析处理、问题求解 2 发展历史 1956年人工智能元年2016年国内开始关注深度学习2017年出现Transformer框架2018年Bert和GPT出现2022年&#xff0c;chatGPT出现&#xff0…

C++11新特性学习

C11 1. C11新特性 自动类型推导&#xff08;auto&#xff09;智能指针&#xff08;提供更安全和更高效的内存管理&#xff09;移动语义和右值引用 (move语义 &&&#xff0c;使得对象移动而非拷贝&#xff0c;在处理大量数据时提高程序性能)Lambda 表达式&#xff08;…

idea连接docker 自动化部署

进入Linux服务器 vim /lib/systemd/system/docker.service将 ExecStart/usr/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock 替换为 ExecStart/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock新建文件 Dockerfile配置Dockerfile文…

CTF比赛中的Git相关题目解题思路

在CTF比赛中&#xff0c;涉及Git相关的题目通常会考察参赛者对Git仓库的了解&#xff0c;尤其是如何利用公开或不完整的Git仓库来恢复源代码或获取敏感信息。本文将结合一些常见的工具和步骤&#xff0c;详细介绍如何解决这类题目。 背景 Git是一种分布式版本控制系统&#…

中国农业银行——开源软件一体化管理平台

【金融机构开源技术应用创新成果案例 第十二期】 中国农业银行——开源软件一体化管理平台 申报单位:中国农业银行股份有限公司 技术领域:开源软件管理 技术产品:Git、Gitea、Spring-boot、Mybatis 业务场景:开源软件准入、使用、安全治理、目录发布、内外部开源等 应…

Go第三方框架--gin框架(三)

5. net/http框架源码-- 多路复用的实现 这块核心功能对应 1.3 的圆圈2&#xff0c;所属代码如下图&#xff1a; run代码涉及的操作不是gin框架的核心&#xff0c;还记的我说过gin是在net/http的基础上操作的吗&#xff0c;我们来看下gin和net/http包的关联关系。 gin: 主要建…

Java重修笔记 第五十六天 坦克大战(六)多线程基础 - 线程同步、死锁

多线程同步机制 多线程编程中&#xff0c;一些敏感数据可能会被多个线程同时访问造成数据混乱&#xff08;例如票数&#xff09;&#xff0c;使用线程同步机制&#xff0c;通过锁对象&#xff08;对象实例或类实例&#xff09;的方式来保证该段代码在任意时刻&#xff0c;最多…

ram和rom的种类迭代和介绍

RAM&#xff08;随机存取存储器&#xff09;在计算机和电子设备中扮演着至关重要的角色。随着技术的发展&#xff0c;RAM有多个迭代和种类&#xff0c;每个种类在速度、功耗和使用场景上有所不同。以下是RAM的详细迭代种类及介绍。 1. SRAM&#xff08;Static RAM&#xff0c;…

linux-软件包管理-包管理工具(Debian 系)

Linux 软件包管理概述 在Linux系统中&#xff0c;软件包管理是系统维护的核心部分之一。通过软件包管理器&#xff0c;用户可以方便地安装、更新、删除和查询系统中的软件包。每个Linux发行版通常都有自己专属的包管理工具&#xff0c;这些工具基于不同的包格式。例如&#xf…