【第4期-智能驾驶汽车系列术语概念解析】第6节:基于三次样条插值的路径规划算法

目录

一、分段三次样条插值曲线推导

1. 邻接点的函数值相等

2. 邻接点的一阶导相等

3. 邻接点的二阶导相等

4. 端点边界条件

二、案例精讲与MATLAB仿真

1. 场景介绍

2. MATLAB程序

3. 仿真结果


一、分段三次样条插值曲线推导

图1 分段三次样条曲线示意图

         如图 1所示,设有n个离散点 ,我们希望用一条光滑的曲线依次经过这n个离散点。上文讲到,若用一条高阶多项式曲线进行插值,会出现端点振荡的龙格现象。

        因此我们设想用若干段低阶的多项式曲线分别插值相邻两个点,然后将其首位相连构成一条光滑的完整曲线。回到图1,n个离散点对应n-1个区间段,也就是说我们的目标转化为求解n-1段三次曲线,每段三次曲线的表达式如下:

式1 

 上式中, fi(x)代表第i段三次曲线函数, ai,j 代表第i段三次曲线的j次项系数。

        由式1可知,每段三次曲线有4个待定系数,那么n-1段三次曲线总共有4(n-1)个待定系数,故需要构造4(n-1)个独立方程才能获得唯一解。换言之,我们需要设定4(n-1)个独立的边界条件,构造边界条件的过程其实也是探索多段三次曲线的等式约束过程,可以从如下几个方面考虑。

1. 邻接点的函数值相等

        显然,我们希望每一段的三次曲线的首末两个点函数值等于离散点,表达式如下:

式2

         改用矩阵表达:

式3

 

        式中,xi和yi分别代表离散点 的横坐标和纵坐标。因此,根据邻接点的函数值相等这一等式约束可以构造2(n-1)个边界条件。

2. 邻接点的一阶导相等

        为了保证曲线在邻接点的导数连续,需要保证邻接点的一阶导相等:

式4

         改用矩阵表达:

式5

         因此,根据邻接点的一阶导相等这一等式约束可以构造(n-2)个边界条件。

3. 邻接点的二阶导相等

        为了保证曲率连续,邻接点的二阶导也需要相等:

式6

         改用矩阵表达:

式7

         根据邻接点的二阶导相等这一等式约束可以构造(n-2)个边界条件。

4. 端点边界条件

        端点边界条件分为自然边界、固定边界、扭结边界。针对自然边界,指定端点的二阶导数为0,即

式8

         改用矩阵表达:

式9

         根据端点条件这一等式约束可以构造2边界条件。

        综上,我们可以将上述的4(n-1)个等式约束全部用矩阵表达式统一表达,最后构成一个线型矩阵方程,因此可以用MATLAB进行矩阵计算求解得到4(n-1)个待定系数。

二、案例精讲与MATLAB仿真

1. 场景介绍

图2 局部路径规划示意图

       图2是一个典型的局部路径规划示意图, 蓝车作为障碍车,黄车需要换道避障。我们设A(0,-1.75),B(10,-1.75) , C(20,-1.75) D(30,1.75) E(40,1.75) F(50,1.75) 一共6个离散点,可以利用三次样条曲线进行路径规划。

        MATLAB的分段三次 Hermite 插值多项式库函数pchip,该函数可以直接计算分段三次样条,返回每一段多项式的4个系数,然后根据此系数调用ppval库函数,生成一系列的插值点,从而得到三次样条曲线

2. MATLAB程序

主程序如下:
% 基于三次样条曲线的换道路径规划
clc
clear
close all

%% 数据定义
d = 3.5;
k = 4;                                   
P = [0,-d/2; 10,-d/2; 20,-d/2; 30,d/2; 40,d/2; 50,d/2]';

%% 调用pchip函数,生成三次样条曲线
x_seq = P(1,:);
y_seq = P(2,:);
cs = pchip(x_seq,y_seq);
X_seq = linspace(0,50,100);
Y_seq = ppval(cs,X_seq);
path = [X_seq', Y_seq'];

%% 计算长度和曲率
x = path(:,1)';
y = path(:,2)';
diffX = diff(path(:,1));
diffY = diff(path(:,2));
cumLength = cumsum(sqrt(diffX.^2 + diffY.^2));   %长度
heading = atan2(diffY, diffX);
for i = 1:length(x)-2
    cur(i) = getCur(x(i:i+2)',y(i:i+2)');
end
cur(end+1) = cur(end);



%% 画曲率图
figure
hold on
grid on
% 主体图形绘制
plot(cumLength,cur,'LineWidth', 3,  'Color', 'k');
% 坐标轴
set(gca,'LineWidth',2.5,'FontName', 'Times New Roman')
hXLabel = xlabel('路径长度/m');
hYLabel = ylabel('曲率/m^-^1');
% 修改刻度标签字体和字号
set(gca, 'FontSize', 16),...
    set([hXLabel, hYLabel], 'FontName',  'simsun')
set([hXLabel, hYLabel], 'FontSize', 16)


% 画航向角图
figure
hold on
grid on
% 主体图形绘制
plot(cumLength, heading,'LineWidth', 3,  'Color', 'b');
% 坐标轴
set(gca,'LineWidth',2.5,'FontName', 'Times New Roman')
hXLabel = xlabel('路径长度/m');
hYLabel = ylabel('航向角/rad');
% 修改刻度标签字体和字号
set(gca, 'FontSize', 16),...
set([hXLabel, hYLabel], 'FontName',  'simsun')
set([hXLabel, hYLabel], 'FontSize', 16)


%% 画图
d = 3.5;               % 道路标准宽度
W = 1.8;               % 汽车宽度
L = 4.7;               % 车长
figure
len_line = 50;

% 画灰色路面图
GreyZone = [-5,-d-0.5; -5,d+0.5; len_line,d+0.5; len_line,-d-0.5];
fill(GreyZone(:,1),GreyZone(:,2),[0.5 0.5 0.5]);
hold on
fill([P(1,1),P(1,1),P(1,1)-L,P(1,1)-L],[-d/2-W/2,-d/2+W/2,-d/2+W/2,-d/2-W/2],'y')
fill([35,35,35-L,35-L],[-d/2-W/2,-d/2+W/2,-d/2+W/2,-d/2-W/2],'b')

% 画分界线
plot([-5, len_line],[0, 0], 'w--', 'linewidth',2);  %分界线
plot([-5,len_line],[d,d],'w','linewidth',2);     %左边界线
plot([-5,len_line],[-d,-d],'w','linewidth',2);  %左边界线

% 设置坐标轴显示范围
axis equal
set(gca, 'XLim',[-5 len_line]);
set(gca, 'YLim',[-4 4]);

% 绘制路径
scatter(P(1,:),P(2,:),100,'r.')
plot(P(1,:),P(2,:),'r');%路径点
plot(path(:,1),path(:,2), 'y','linewidth',2);%路径点

 上面的主程序使用了求解散点曲率的函数,函数体程序如下

function k=getCur(x,y)
ta=sqrt((x(2)-x(1))^2+(y(2)-y(1))^2);
tb=sqrt((x(3)-x(2))^2+(y(3)-y(2))^2);
M=[1,-ta,ta^2;
    1,0,0;
    1,tb,tb^2];
A=M\x;
B=M\y;
k=(2*(A(2)*B(3)-A(3)*B(2)))/((A(2)^2+B(2)^2)^1.5+1e-10);
end

3. 仿真结果

         执行上述代码,将场景图和规划路径一并绘制,如图3所示。 

图3 仿真场景图
图4 规划路径的航向角变化图
图5 规划路径的曲率变化图

        可以看出,三次样条曲线的曲率变化率较大,这与控制点的选取有较大关系。


                
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
1. 前言 在机器人领域中,路径规划是一项非常重要的任务。路径规划的目的是找到一条从起点到终点的路径,使机器人能够安全、高效地到达目的地。路径规划可以分为全局路径规划和局部路径规划两种,其中局部路径规划指的是机器人在运动中根据环境实时调整自身位置和速度的一种规划方式。 本文将介绍一种基于梯度下降的DWA算法的局部路径规划示例,旨在通过实例来讲解DWA算法的原理、流程和应用。 2. DWA算法简介 DWA算法是一种常见的基于梯度下降的局部路径规划算法,其全称为Dynamic Window Approach(动态窗口法)。该算法的主要思想是利用机器人当前的状态信息和周围环境信息,在运动空间中搜索合适的运动轨迹,从而实现对机器人局部路径的规划。 DWA算法的基本流程如下: 1. 机器人获取当前状态信息(包括位置、速度、朝向等)和周围环境信息(包括障碍物位置、速度等)。 2. 基于当前状态信息和周围环境信息,利用动态窗口法在运动空间中搜索合适的运动轨迹。 3. 利用代价函数评估搜索得到的轨迹,并选择代价最小的轨迹作为机器人的运动轨迹。 4. 根据选择的运动轨迹,实时调整机器人的位置和速度,从而使机器人能够安全、高效地到达目的地。 3. DWA算法实现 在介绍DWA算法的实现之前,需要先明确一些术语和定义: - 运动模型:描述机器人在不同状态下的运动规律,例如在某个速度下机器人的加速度、最大速度等。 - 运动控制器:根据当前状态信息和搜索得到的运动轨迹,计算机器人的速度和转向角度。 - 运动空间:描述机器人可能的运动轨迹,通常用速度和转向角度两个自由度来描述。 - 动态窗口:运动空间中机器人可行运动轨迹的一个子集,其大小和位置随着机器人运动状态的变化而变化。 - 代价函数:衡量机器人运动轨迹的优劣程度的函数,通常包括机器人与障碍物的距离、机器人的运动速度等因素。 基于上述定义,我们可以开始实现DWA算法。下面以Python代码为例进行说明。 首先,我们需要定义机器人的运动模型。此处我们假设机器人在匀加速的情况下行驶,因此可以定义一个简单的运动模型如下: ```python class RobotMotionModel(): def __init__(self, max_speed, max_acceleration, max_yaw_rate, max_yaw_acceleration): self.max_speed = max_speed self.max_acceleration = max_acceleration self.max_yaw_rate = max_yaw_rate self.max_yaw_acceleration = max_yaw_acceleration def predict(self, x, u, dt): # x: 机器人状态信息,包括位置、速度、朝向等 # u: 机器人运动控制指令,包括线速度和角速度 # dt: 时间步长 x[2] += u[1] * dt x[0] += u[0] * np.cos(x[2]) * dt x[1] += u[0] * np.sin(x[2]) * dt x[3] = u[0] # 机器人速度等于控制指令 # 对机器人速度进行限制 if x[3] > self.max_speed: x[3] = self.max_speed elif x[3] < -self.max_speed: x[3] = -self.max_speed # 对机器人朝向角进行限制 if x[2] > np.pi: x[2] -= 2 * np.pi elif x[2] < -np.pi: x[2] += 2 * np.pi return x ``` 接下来,我们需要定义一个动态窗口,用于限制机器人的运动轨迹。具体来说,我们可以根据当前状态信息和运动模型,计算出机器人能够到达的最大速度和最大角速度,然后根据这些限制条件,在运动空间中生成一个动态窗口。代码如下: ```python class DynamicWindow(): def __init__(self, max_speed, max_yaw_rate, max_acceleration, max_yaw_acceleration): self.max_speed = max_speed self.max_yaw_rate = max_yaw_rate self.max_acceleration = max_acceleration self.max_yaw_acceleration = max_yaw_acceleration def calculate(self, x): # x: 机器人状态信息,包括位置、速度、朝向等 # 计算机器人能够到达的最大速度和最大角速度 v_max = min(self.max_speed, np.sqrt(self.max_acceleration ** 2 + (self.max_yaw_acceleration * x[3]) ** 2) / np.abs(self.max_yaw_acceleration)) w_max = min(self.max_yaw_rate, self.max_yaw_acceleration * x[3]) # 根据最大速度和最大角速度,生成动态窗口 v_min = max(0, x[3] - self.max_acceleration * dt) w_min = max(-self.max_yaw_rate, x[4] - self.max_yaw_acceleration * dt) v_max = min(v_max, x[3] + self.max_acceleration * dt) w_max = min(w_max, x[4] + self.max_yaw_acceleration * dt) return (v_min, v_max, w_min, w_max) ``` 下一步,我们需要根据动态窗口在运动空间中搜索合适的运动轨迹。具体来说,我们需要在速度和角速度的范围内,以一定的步长进行搜索,然后计算每一条搜索得到的轨迹的代价函数值,最终选择代价最小的轨迹作为机器人的运动轨迹。代码如下: ```python class DWAPlanner(): def __init__(self, max_speed, max_yaw_rate, max_acceleration, max_yaw_acceleration, dt): self.motion_model = RobotMotionModel(max_speed, max_acceleration, max_yaw_rate, max_yaw_acceleration) self.dynamic_window = DynamicWindow(max_speed, max_yaw_rate, max_acceleration, max_yaw_acceleration) self.dt = dt def plan(self, x, obstacles): # x: 机器人状态信息,包括位置、速度、朝向等 # obstacles: 障碍物信息,包括位置、速度等 # 根据当前状态信息和运动模型,预测下一时刻机器人的状态信息 x_pred = self.motion_model.predict(x.copy(), u, self.dt) # 根据预测的机器人状态信息和周围环境信息,计算动态窗口 dw = self.dynamic_window.calculate(x_pred) # 在动态窗口范围内,以一定的步长搜索运动轨迹,并计算每条轨迹的代价函数值 min_cost = float('inf') best_u = [0, 0] for v in np.arange(dw[0], dw[1], self.motion_model.max_acceleration * self.dt): for w in np.arange(dw[2], dw[3], self.motion_model.max_yaw_acceleration * self.dt): u = [v, w] x_new = self.motion_model.predict(x.copy(), u, self.dt) cost = self.calculate_cost(x_new, obstacles) if cost < min_cost: min_cost = cost best_u = u return best_u def calculate_cost(self, x_new, obstacles): # 计算机器人运动轨迹的代价函数值 cost = 0 for o in obstacles: d = np.sqrt((x_new[0] - o[0]) ** 2 + (x_new[1] - o[1]) ** 2) cost += d return cost ``` 最后,我们可以根据DWA算法实现一个简单的机器人路径规划示例。具体来说,我们可以定义一个机器人类,包括机器人的状态信息和运动控制器。然后在一个二维空间中模拟机器人的运动,每隔一段时间调用DWA算法来规划机器人的运动轨迹,并将机器人的状态信息更新到新的状态。代码如下: ```python class Robot(): def __init__(self, x, u, max_speed, max_yaw_rate, max_acceleration, max_yaw_acceleration, dt): self.x = x # 机器人状态信息,包括位置、速度、朝向等 self.u = u # 机器人运动控制指令,包括线速度和角速度 self.motion_model = RobotMotionModel(max_speed, max_acceleration, max_yaw_rate, max_yaw_acceleration) self.dwa_planner = DWAPlanner(max_speed, max_yaw_rate, max_acceleration, max_yaw_acceleration, dt) def update(self, obstacles): # 根据当前机器人状态和运动控制指令,计算机器人下一个状态 self.x = self.motion_model.predict(self.x.copy(), self.u, dt) # 根据DWA算法规划机器人运动轨迹 self.u = self.dwa_planner.plan(self.x, obstacles) # 初始化机器人状态 x = np.array([0, 0, 0, 0]) u = [0, 0] # 初始化障碍物信息 obstacles = [(5, 5), (6, 6), (7, 7)] # 初始化机器人对象 robot = Robot(x, u, max_speed=1, max_yaw_rate=np.pi/4, max_acceleration=0.1, max_yaw_acceleration=np.pi/8, dt=0.1) # 模拟机器人的运动 for i in range(100): # 更新机器人状态 robot.update(obstacles) # 绘制机器人和障碍物 plt.clf() plt.plot(robot.x[0], robot.x[1], 'bo') for o in obstacles: plt.plot(o[0], o[1], 'ro') plt.xlim(-10, 10) plt.ylim(-10, 10) plt.pause(0.1) ``` 4. 总结 DWA算法是一种常见的基于梯度下降的局部路径规划算法,其主要思想是利用机器人当前的状态信息和周围环境信息,在运动空间中搜索合适的运动轨迹,从而实现对机器人局部路径的规划。本文通过一个简单的机器人路径规划示例来讲解DWA算法的原理、流程和应用,旨在帮助读者更好地理解该算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黎的Ally

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值