如图所示为差速机器人模型,描述小车的速度一般为小车质心的线速度 v v v和角速度 ω \omega ω。小车在笛卡尔坐标系下的坐标为 ( x , y ) (x,y) (x,y),航向角为 θ \theta θ。差速车的运动模型为:
{ x ˙ = v cos ( θ ) y ˙ = v sin ( θ ) θ ˙ = ω \left\{ \begin{array}{rcl} \dot x &=&v \cos(\theta) \\ \dot y &=& v \sin(\theta)\\ \dot \theta &=& \omega \end{array} \right. ⎩⎨⎧x˙y˙θ˙===vcos(θ)vsin(θ)ω
在ROS框架下,小车的控制指令一般为质心线速度 v v v和角速度 ω \omega ω,但是小车的底层控制以控制两轮速度为主,所以需要对速度进行反解算。假设小车左右轮线速度分别为 v l v_{l} vl和 v r v_{r} vr,轮间距为 L L L,因为小车转动时两轮是同轴转动(角速度相同: ω l \omega_{l} ωl= ω r \omega_{r} ωr= ω \omega ω),所以可以得出:
L = v l ω l − v r ω r ω = v l − v r L L=\frac{v_{l}}{\omega_{l}}-\frac{v_{r}}{\omega_{r}}\\ \omega = \frac{v_{l}-v_{r}}{L} L=ωlvl−ωrvrω=Lvl−vr
质心线速度和两轮线速度的关系为:
v = v l + v r 2 v = \frac{v_{l}+v_{r}}{2} v=2vl+vr
由此,可以得出两轮线速度为:
v l = v + ω l 2 v r = v − ω l 2 v_l = v+\frac{\omega l}{2} \\ v_r = v-\frac{\omega l}{2} vl=v+2ωlvr=v−2ωl
NLGL算法是在纯跟踪算法的基础上推导得出的,纯跟踪算法的基本原理可以参照:https://zhuanlan.zhihu.com/p/112065159
https://zhuanlan.zhihu.com/p/377036466
NLGL算法的控制律为:
a S c m d = 2 v 2 L 1 s i n ( η ) a_{Scmd} = 2\frac{v^2}{L_1}sin(\eta) aScmd=2L1v2sin(η)
其中, a S c m d a_{Scmd} aScmd为运动体的横向加速度,也可以视为向心加速度, L 1 L_1 L1为运动体的前视距离(需要自己设定), v v v为运动体的质心线速度, η \eta η为前视距离 L 1 L_1 L1与质心线速度 v v v的夹角。
由纯跟踪算法可知,运动体的实时旋转半径 R = L 1 2 2 x R = \frac{L_1^2}{2x} R=2xL12, x x x为在车体坐标系下目标点到运动体质心的横向偏差, x = L 1 s i n ( η ) x=L_1sin(\eta) x=L1sin(η),由此可得 R = L 1 2 s i n ( η ) R = \frac{L_1}{2sin(\eta)} R=2sin(η)L1。已知运动体的质心线速度 v v v,则向心加速度 a S c m d = v 2 R a_{Scmd} = \frac{v^2}{R} aScmd=Rv2,由此得到NLGL算法的控制律。
不管是在纯跟踪算法还是NLGL算法中,前视距离的选择关系到小车的路径跟踪效果,选择的前视距离太短导致无法与路径相交,就无法获取到目标点,导致小车运动在短时间内振荡;选择的前视距离太长,虽然跟踪路径会比较平滑,但是会产生较大的跟踪误差。因此,前视距离需要根据小车的运动情况和路径的变化不断做出调整。具体的前视距离的选择方法主要参考:pure pursuit预瞄距离的改进思路方法介绍 1。
具体的代码如下所示:
%% 参数设定:确定前视距离的方法 最简单的比例关系
clear; clc ;
global Lf; %前视距离
Lf = 0.04;
eta = 0;
kp = 0.8; %速度增益
dt = 0.01; %[s] 时间间隔
T = 100; %最大仿真时间
%% 参考路径 圆 闭合曲线
global r;
aplha=0:pi/100:2*pi;
r=0.4;
cx=r*cos(aplha);
cy=r*sin(aplha);
global max_Index;
max_Index = length(cx);
%% 初始状态设定
i = 1;
j = 1;
x = zeros(1,10);
y = zeros(1,10);
yaw = zeros(1,10); %航向角
v = zeros(1,10); %速度
%初始位置设定
x(1) = 0.4;
y(1) = 0;
yaw(1) = 1.5;
global speed;
speed = 0.1; %速度设定
t = zeros(size(cx)); %仿真时间
index = zeros(size(cx)); %索引值
global oldnearestp_ind; % 存储上一时刻临近点的索引
oldnearestp_ind = -1;
global target_ind; % 目标点索引
target_ind = 0;
%% 运行主体
[target_ind]= calc_target_index(x(1),y(1),cx,cy);
index(1) = target_ind;
while ((T >= t(i)) && (max_Index >= target_ind))
if(target_ind==max_Index) % 判断是否到达路径终点
dis = calc_distance(x(i),y(i),cx(target_ind),cy(target_ind));
if(dis<=0.02)
v(i+1) = 0; w = 0;
break;
end
end
[w] = pure_pursuit_control(x(i),y(i),yaw(i),v(i),cx,cy,target_ind);
index(j+1) = target_ind;
[a] = speedControl(kp,v(i));
[x(i+1),y(i+1),yaw(i+1),v(i+1)] = updatestate(x(i),y(i),yaw(i),v(i),a,w,dt);
t(j+1) = t(j)+dt;
i = i+1;
j = j+1;
end
%% 绘图
figure;
plot(cx,cy,'b'); hold on;
plot(x,y,'r','Linewidth',1);
legend('ref','fact');
function [distance] = calc_distance(x,y,cx,cy)
distance = sqrt((cx-x)^2+(cy-y)^2);
end
function [ind] = calc_nearest_index(x,y,cx,cy)
global oldnearestp_ind;
global max_Index;
N = max_Index;
if oldnearestp_ind == -1 % 初始时刻搜寻最近路径点
Distance = zeros(1,N);
for i = 1:N
Distance(i) = sqrt((cx(i)-x)^2 + (cy(i)-y)^2);
end
[~,ind] = min(Distance);
oldnearestp_ind = ind;
else
ind = oldnearestp_ind;
distance_this_index = calc_distance(x,y,cx(ind),cy(ind));
while (ind+1) <= N %判断索引值是否超过路径点总体长度
distance_next_index = calc_distance(x,y,cx(ind+1),cy(ind+1));
if distance_this_index < distance_next_index
break;
end
ind = ind + 1;
distance_this_index = distance_next_index;
end
oldnearestp_ind = ind;
end
end
function [ind] = calc_target_index(x,y,cx,cy)
global Lf;
[ind] = calc_nearest_index(x,y, cx,cy);
L = calc_distance(x,y,cx(ind),cy(ind));
while Lf>=L && (ind+1)<=length(cx)
ind = ind+1;
L = calc_distance(x,y,cx(ind),cy(ind));
end
end
function [w, ind] = pure_pursuit_control(x,y,yaw,v,cx,cy,pind)
global Lf;
global target_ind;
global max_index;
global eta;
[ind] = calc_target_index(x,y,cx,cy);
if pind >= ind
target_ind = pind;
else
target_ind = ind;
end
if target_ind <= length(cx)
tx = cx(target_ind);
ty = cy(target_ind);
else
tx = cx(length(max_index));
ty = cy(length(max_index));
end
theta = atan2((ty-y), (tx-x)); %目标点与质心坐标的向量与x轴的夹角
eta = theta-yaw;
w = (2*v*sin(eta))/Lf;
end