自动驾驶算法(九):多项式轨迹与Minimun Snap原理与Matab代码详解

目录

1 为什么需要轨迹优化

2 代码解析

3 完整代码


1 为什么需要轨迹优化

        我们利用前八篇所学的博客可以利用RRT、A*、遗传算法等设计出一条折线轨迹,轨迹优化就是在路径优化的基础上将折线优化成曲线,这样更加有利于无人机的飞行。

        那么什么是多项式轨迹呢?

        每段轨迹的路程以t为变量的函数表示出来就是多项式轨迹。

        我们用三段轨迹表示这段路程(以时间t作为自变量):

        那么我们的任务就是求解参数向量p确定轨迹,t_0,t_1,t_2与路程成正比的因此我们可以提前算出。

        那么我们轨迹的要求是什么呢?

        优化目标为:

        这个优化的目的是使加速度尽可能的小:那么速度也是尽可能地小,对无人机动力要求就不那么大了。

        具体推导如下:

        其实我们就要算出来个Q矩阵,里面的r是rows的意思,c是cols的意思。

        其实就是一个二次规划问题?

        也就是求在Ap=b约束下最小的p^TQp

2 代码解析

        我们先来运行一下:

        所在位置为*的就是我们航行的路径点,不同颜色的就是几段。

        我们因此要先设置起点的速度和加速度、终点的速度和加速度、还有总时间、我们根据总时间还有路径的长度去分配时间:

function demo1_minimum_snap_simple()
    clear,clc;

    % condition
    % 航路点 起点、终点、中间的这些点
    waypts = [0,0;
              1,2;
              2,0;
              4,5;
              5,2]';
    % 起点、终点速度加速度      
    v0 = [0,0];
    a0 = [0,0];
    v1 = [0,0];
    a1 = [0,0];
    % 总时间
    T = 5; 
    % 计算每一段轨迹的时间
    % 输入参数为航路点 + 总时间
    ts = arrangeT(waypts,T);
    % 设置多项式的阶数
    n_order = 5;

        我们看一下怎么给各段路程分配时间的:

function ts = arrangeT(waypts,T)
    % 这一行代码计算了相邻路径点之间的差值。假设 waypts 是一个矩阵,每一列代表一个路径点的坐标,
    % 那么 waypts(:,2:end) 选择了除了第一个路径点之外的所有路径点,waypts(:,1:end-1) 选择了除了最后一个路径点之外的所有路径点,
    % 然后两者相减,得到了相邻路径点之间的位移。
    % waypts:
    % 0 1 2 4 5
    % 0 2 0 5 2
    % waypts(:,2:end)
    % 冒号表示取到了所有的行,取到了2到最后一列
    % 1 2 4 5
    % 2 0 5 2
    % 0 1 2 4  = 1  1  2  1
    % 0 2 0 5  = 2 -2  5 -3  其实就是相邻的点进行相减
    x = waypts(:,2:end) - waypts(:,1:end-1);
    % 计算了相邻路径点之间的欧几里得距离。x.^2 将 x 中的每个元素平方,然后 sum(x.^2,1) 对每列进行求和,
    % 最后 .^0.5 对每个和取平方根,得到相邻路径点之间的欧几里得距离。
    dist = sum(x.^2,1).^0.5;
    % 计算了一个缩放因子 k,它是总时间 T 与所有相邻路径点之间的距离之和的比值。
    k = T/sum(dist);
    % 构建了时间戳 ts。cumsum(dist*k) 计算了距离的累积和,然后在前面加了一个零,以确保起始时间戳为零。
    ts = [0 cumsum(dist*k)];
end

        我们指定了总时间T=5秒,这里做的就是根据路程进行线性分段。

    %% XY轴分开计算
    % 传入参数 第一行的所有列就是所有的x 每一段的时间 多项式阶 起点终点的加速度
    polys_x = minimum_snap_single_axis_simple(waypts(1,:),ts,n_order,v0(1),a0(1),v1(1),a1(1));
    polys_y = minimum_snap_single_axis_simple(waypts(2,:),ts,n_order,v0(2),a0(2),v1(2),a1(2));

        这里就是重头戏了,进行路径规划,包括求Q矩阵与约束的构建。

% 传入参数 第一行的所有列就是所有的x 每一段的时间 多项式阶 起点终点的加速度
function polys = minimum_snap_single_axis_simple(waypts,ts,n_order,v0,a0,ve,ae)
% 起点终点的位置
p0 = waypts(1);
pe = waypts(end);

% 多项式的段数:航路点-1
n_poly = length(waypts)-1;
% 参数个数 = 阶数+1
n_coef = n_order+1;

% 构建优化目标 优化目标的数量为多项式的段数
Q_all = [];
for i=1:n_poly
    % 构建对角矩阵(有几个多项式就有几个Q)
    % 在每次循环中,它调用了一个名为 computeQ 的函数
    % 传递了四个参数:n_order(多项式的阶数)、3(对jack 4阶导数进行优化)、ts(i)这一段的起始时间 和 ts(i+1)这一段的结束时间。
    % computeQ 函数的返回值是一个对角矩阵,这个对角矩阵将会被加入到 Q_all 中,通过 blkdiag 函数实现对 Q_all 的更新。
    Q_all = blkdiag(Q_all,computeQ(n_order,3,ts(i),ts(i+1)));

        这里我们传进来了起点和终点的x坐标。n_poly表示多项式的段数,也就是我们求的路程有几段,为航路点个数-1 = 4段。n_coef就是p矩阵的阶数,我们采用5阶(n_order)项+一个常数项的表示方法,即:

% 传入参数 个体的路径 地图 一行有多少个元素x
% 它告诉 MATLAB 这是一个名为 generate_continuous_path 的函数,它接受三个输入参数 single_pop,G 和 x,并且它会返回一个名为 single_new_pop 的变量。
function [single_new_pop] = generate_continuous_path(single_pop, G, x)
i = 1;
single_new_pop = single_pop;
% 这行代码调用了 size 函数来获取变量 single_new_pop 的大小,并将结果存储在了 single_path_num 变量中。
% 在这里,使用了波浪线 ~ 来表示忽略了 size 函数返回的第一个值(也就是行数),只保留了列数。
% single_new_pop 是一个 1 * 20的向量
% single_path_num 存储了路径经过的点的数量
[~, single_path_num] = size(single_new_pop);

% 遍历每一列 1-20
while i ~= single_path_num
    % 点i所在列 (从左到右编号)
    x_now = mod(single_new_pop(1, i), x) + 1; 
    % 点i所在行 (从上到下编号)
    y_now = fix(single_new_pop(1, i) / x) + 1;
    % 点i+1所在行 (从上到下编号)
    x_next = mod(single_new_pop(1, i + 1), x) + 1;
    y_next = fix(single_new_pop(1, i + 1) / x) + 1;
    
    % 初始化最大迭代次数
    max_iteration = 0;
    
    % 如果他们不相连的话
    while max(abs(x_next - x_now), abs(y_next - y_now)) > 1
        x_insert = floor((x_next + x_now) / 2);
        y_insert = floor((y_next + y_now) / 2);
        
        % 取得两者中点值
        if G(y_insert, x_insert) == 0  
            % 栅格值
            num_insert = (x_insert - 1) + (y_insert - 1) * x;
            % single_new_pop 是一个数组,这段代码的目的是将 num_insert 插入到 single_new_pop 的第 i 个位置之后。
            % single_new_pop(1, 1:i) 表示取 single_new_pop 中第1行,从第1列到第i列的元素。
            % single_new_pop(1, i+1:end) 表示取 single_new_pop 中第1行,从第i+1列到最后一列的元素。
            single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
            
        % 如果是障碍物的话
        else   
            % 它检查该点左面是否有路径 
            % (x_insert - 2) + (y_insert - 1) * x 是否不等于 single_new_pop(1, i)。
            if G(y_insert, x_insert - 1) == 0 && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i)) && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert - 1;
                % 索引号
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                % 插入
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                               
            elseif G(y_insert, x_insert + 1) == 0 && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i)) && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            elseif G(y_insert + 1, x_insert) == 0 && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i)) && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i+1))
                y_insert = y_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];

            elseif  G(y_insert - 1, x_insert) == 0 && ((x_insert - 1) + (y_insert - 2) * x ~= single_new_pop(1, i)) && ((x_insert - 1) + (y_insert-2) * x ~= single_new_pop(1, i+1))
                y_insert = y_insert - 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            % 左右上下都是占据的则删去路径
            else
                %break_pop = single_new_pop
                single_new_pop = [];
                break
            end    
        end
        
        x_next = x_insert;
        y_next = y_insert;
        max_iteration = max_iteration + 1;
        if max_iteration > 20000
            single_new_pop = [];
            break
        end
        
    end
    
    if isempty(single_new_pop)
        break
    end
    
    [~, single_path_num] = size(single_new_pop);
    i = i + 1;
end

        了解这些后,我们通过ComputeQ计算Q矩阵,并通过blkdiag合并一个个Q矩阵,最终组成如下形式:

Q = \begin{bmatrix} Q_1 & & & & \\ & Q_2& & & \\ & & ... & & \\ & & &... & \\ & & & & Q_k \end{bmatrix}

        其中k为多项式的段数,也就是说有几段路程,就有几个Q矩阵。

        我们看一下Q矩阵的计算方法:

% n:polynormial order
% r:derivertive order, 1:minimum vel 2:minimum acc 3:minimum jerk 4:minimum snap
% t1:start timestamp for polynormial
% t2:end timestap for polynormial
% n=5 五次多项式拟合路径 r=3 用jerk模拟 t1 t2 
function Q = computeQ(n,r,t1,t2)
% (n-r)*2+1 = (5-3)*2+1 = 5
T = zeros((n-r)*2+1,1);
for i = 1:(n-r)*2+1
    T(i) = t2^i-t1^i;
end
% 理论说过Q是n+1 * n+1的维度  也就是7*7的维度  r是3 表示用jerk进行优化
% 它的矩阵就右下角有值 
Q = zeros(n+1);
for i = r+1:n+1     % 行 从r+1开始,因为前r行都是0,在PPT中r=4。在我们的实例中r=3
    for j = i:n+1   % 列 从r+1开始
        k1 = i-r-1; %对应r-3  行-r-1
        k2 = j-r-1; %对应c-3  列-r-1
        k = k1+k2+1;% r-3+c-3+1
        % prod是连乘的意思  prod(k1+1:k1+r)= r-3+1 *...* r-3+3 =
        % (r-3)(r-2)(r-1)r  (c-3)(c-2)(c-1)c k就是参数 
        Q(i,j) = prod(k1+1:k1+r)*prod(k2+1:k2+r)/k*T(k);
        Q(j,i) = Q(i,j);
    end
end
%Q

        这里我们传来的n=5,即用五次项拟合曲线,r=4用三阶导数去求极值。

        T矩阵就是后面的那块:t_{i}^{r+c-7}-t_{i-1}^{r+c-7}

        这里是对应于4阶snap来说的,我们用三阶作为优化目标那么我们的T矩阵就应当是t_{i}^{r-3+c-3+1}-t_{i-1}^{r-3+c-3+1}。前三排前三列是0的,因此有值的就是第4行到第n+1=6行。因为是对角矩阵,我们只需算出其中的一半也就是6个值。如下Q矩阵:

      我们初始化Q矩阵为(n+1,n+1)的方阵,从r+1行也就是3+1=4行开始赋值,比如第四行,从第四列一直到第n+1列也就是第6列,再比如第五行,从第五列一直到第n+1列也就是第6列。

        首先计算r-3,也就是行-3,i表示遍历哪一行,j表示遍历哪一列,因此我们不妨想想当i与j都为4的时候,行-3对应4-3-1!=0,我们补足-1就是0了。k对应r-3+c-3+1。t_{i}^{r-3+c-3+1}-t_{i-1}^{r-3+c-3+1}就是T(k)。

        这里我们就理解T矩阵的赋值了,因为它所包含的最高项的次数为r-3+c-3+1的max r和c,r和cmax也就是n+1,因此它的最高项次数也就是 (n-3)*2 + 1。到此,我们的Q矩阵就构造完毕啦。

        我们打印一下Q矩阵:

        我们来建立约束方程:

% 传入参数 个体的路径 地图 一行有多少个元素x
% 它告诉 MATLAB 这是一个名为 generate_continuous_path 的函数,它接受三个输入参数 single_pop,G 和 x,并且它会返回一个名为 single_new_pop 的变量。
function [single_new_pop] = generate_continuous_path(single_pop, G, x)
i = 1;
single_new_pop = single_pop;
% 这行代码调用了 size 函数来获取变量 single_new_pop 的大小,并将结果存储在了 single_path_num 变量中。
% 在这里,使用了波浪线 ~ 来表示忽略了 size 函数返回的第一个值(也就是行数),只保留了列数。
% single_new_pop 是一个 1 * 20的向量
% single_path_num 存储了路径经过的点的数量
[~, single_path_num] = size(single_new_pop);

% 遍历每一列 1-20
while i ~= single_path_num
    % 点i所在列 (从左到右编号)
    x_now = mod(single_new_pop(1, i), x) + 1; 
    % 点i所在行 (从上到下编号)
    y_now = fix(single_new_pop(1, i) / x) + 1;
    % 点i+1所在行 (从上到下编号)
    x_next = mod(single_new_pop(1, i + 1), x) + 1;
    y_next = fix(single_new_pop(1, i + 1) / x) + 1;
    
    % 初始化最大迭代次数
    max_iteration = 0;
    
    % 如果他们不相连的话
    while max(abs(x_next - x_now), abs(y_next - y_now)) > 1
        x_insert = floor((x_next + x_now) / 2);
        y_insert = floor((y_next + y_now) / 2);
        
        % 取得两者中点值
        if G(y_insert, x_insert) == 0  
            % 栅格值
            num_insert = (x_insert - 1) + (y_insert - 1) * x;
            % single_new_pop 是一个数组,这段代码的目的是将 num_insert 插入到 single_new_pop 的第 i 个位置之后。
            % single_new_pop(1, 1:i) 表示取 single_new_pop 中第1行,从第1列到第i列的元素。
            % single_new_pop(1, i+1:end) 表示取 single_new_pop 中第1行,从第i+1列到最后一列的元素。
            single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
            
        % 如果是障碍物的话
        else   
            % 它检查该点左面是否有路径 
            % (x_insert - 2) + (y_insert - 1) * x 是否不等于 single_new_pop(1, i)。
            if G(y_insert, x_insert - 1) == 0 && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i)) && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert - 1;
                % 索引号
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                % 插入
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                               
            elseif G(y_insert, x_insert + 1) == 0 && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i)) && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            elseif G(y_insert + 1, x_insert) == 0 && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i)) && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i+1))
                y_insert = y_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];

            elseif  G(y_insert - 1, x_insert) == 0 && ((x_insert - 1) + (y_insert - 2) * x ~= single_new_pop(1, i)) && ((x_insert - 1) + (y_insert-2) * x ~= single_new_pop(1, i+1))
                y_insert = y_insert - 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            % 左右上下都是占据的则删去路径
            else
                %break_pop = single_new_pop
                single_new_pop = [];
                break
            end    
        end
        
        x_next = x_insert;
        y_next = y_insert;
        max_iteration = max_iteration + 1;
        if max_iteration > 20000
            single_new_pop = [];
            break
        end
        
    end
    
    if isempty(single_new_pop)
        break
    end
    
    [~, single_path_num] = size(single_new_pop);
    i = i + 1;
end

%求解F(不等式) 构建全是0的向量
b_all = zeros(size(Q_all,1),1);

% 构建等式约束 4K+2 多项式的个数*每个项的系数
Aeq = zeros(4*n_poly+2,n_coef*n_poly);
beq = zeros(4*n_poly+2,1);

% 起点/终点的位置速度和加速度  (6 equations)
% 1-3行 第一个参数到n_order+1=6 位置、速度、加速度
Aeq(1:3,1:n_coef) = [calc_tvec(ts(1),n_order,0);
                     calc_tvec(ts(1),n_order,1);
                     calc_tvec(ts(1),n_order,2)];
% 终点约束4-6
Aeq(4:6,n_coef*(n_poly-1)+1:n_coef*n_poly) = ...
                    [calc_tvec(ts(end),n_order,0);
                     calc_tvec(ts(end),n_order,1);
                     calc_tvec(ts(end),n_order,2)];
beq(1:6,1) = [p0,v0,a0,pe,ve,ae]';

% r=0:pos  1:vel  2:acc 3:jerk
% n_order 多项式的阶数
% t       第i段占据的时间
% r 0 1 2 对应 位置 速度 加速度
function tvec = calc_tvec(t,n_order,r)
    tvec = zeros(1,n_order+1);
    for i=r+1:n_order+1
        tvec(i) = prod(i-r:i-1)*t^(i-r-1);
    end
end

        回顾一下,我们n_poly,它的值为多项式的段数也就是航路点-1,本例中n_poly =4

        n_coef为多项式的次数+1,这里我们为6。

        先看一下它的形式吧:我们打印一下约束方程的Aeq

        它的行数为18(4*n_poly+2),列数为24(n_coef *n_poly )。我们看看它里面存储的是什么吧!

        1-6行我们加入起点和终点的约束(位移、速度、加速度):

% 起点/终点的位置速度和加速度  (6 equations)
% 1-3行 第一个参数到n_order+1=6 位置、速度、加速度
Aeq(1:3,1:n_coef) = [calc_tvec(ts(1),n_order,0);
                     calc_tvec(ts(1),n_order,1);
                     calc_tvec(ts(1),n_order,2)];
% 终点约束4-6
Aeq(4:6,n_coef*(n_poly-1)+1:n_coef*n_poly) = ...
                    [calc_tvec(ts(end),n_order,0);
                     calc_tvec(ts(end),n_order,1);
                     calc_tvec(ts(end),n_order,2)];

        起点约束放在起点的位置上,因为一个约束是有5阶+1个常数项的,因此一个约束占据6列。一共4段路线,是这么来的24行。

        从第七行开始固定中间的点。从第七行开始到第十行。

neq = 6;
% 从第七行开始
for i=1:n_poly-1
    neq=neq+1;
    Aeq(neq,n_coef*i+1:n_coef*(i+1)) = calc_tvec(ts(i+1),n_order,0);
    beq(neq) = waypts(i+1);
end

        位置为行数,一个占据自己的六个位置,如下:

        下面开始连续性约束:

% 段数-1 continuous constraints  ((n_poly-1)*3 equations)
for i=1:n_poly-1
    tvec_p = calc_tvec(ts(i+1),n_order,0);
    tvec_v = calc_tvec(ts(i+1),n_order,1);
    tvec_a = calc_tvec(ts(i+1),n_order,2);
    neq=neq+1;
    Aeq(neq,n_coef*(i-1)+1:n_coef*(i+1))=[tvec_p,-tvec_p];
    neq=neq+1;
    Aeq(neq,n_coef*(i-1)+1:n_coef*(i+1))=[tvec_v,-tvec_v];
    neq=neq+1;
    Aeq(neq,n_coef*(i-1)+1:n_coef*(i+1))=[tvec_a,-tvec_a];
end

        对位移、速度、加速度进行约束。n_coef*(i-1)+1:n_coef*(i+1)代表每一次处理一个点,如下:

        圈起来的就是一对邻接点的位移、速度、加速度。

        我们添加完约束之后,开始求解

Aieq = [];
bieq = [];

p = quadprog(Q_all,b_all,Aieq,bieq,Aeq,beq);

polys = reshape(p,n_coef,n_poly);

        我们这里没有不等式约束,故设置Q_all,b_all为空,我们求解的变量就是p。

        是不是很简单呢?

        看一下运行结果:

3 完整代码

% 传入参数 个体的路径 地图 一行有多少个元素x
% 它告诉 MATLAB 这是一个名为 generate_continuous_path 的函数,它接受三个输入参数 single_pop,G 和 x,并且它会返回一个名为 single_new_pop 的变量。
function [single_new_pop] = generate_continuous_path(single_pop, G, x)
i = 1;
single_new_pop = single_pop;
% 这行代码调用了 size 函数来获取变量 single_new_pop 的大小,并将结果存储在了 single_path_num 变量中。
% 在这里,使用了波浪线 ~ 来表示忽略了 size 函数返回的第一个值(也就是行数),只保留了列数。
% single_new_pop 是一个 1 * 20的向量
% single_path_num 存储了路径经过的点的数量
[~, single_path_num] = size(single_new_pop);

% 遍历每一列 1-20
while i ~= single_path_num
    % 点i所在列 (从左到右编号)
    x_now = mod(single_new_pop(1, i), x) + 1; 
    % 点i所在行 (从上到下编号)
    y_now = fix(single_new_pop(1, i) / x) + 1;
    % 点i+1所在行 (从上到下编号)
    x_next = mod(single_new_pop(1, i + 1), x) + 1;
    y_next = fix(single_new_pop(1, i + 1) / x) + 1;
    
    % 初始化最大迭代次数
    max_iteration = 0;
    
    % 如果他们不相连的话
    while max(abs(x_next - x_now), abs(y_next - y_now)) > 1
        x_insert = floor((x_next + x_now) / 2);
        y_insert = floor((y_next + y_now) / 2);
        
        % 取得两者中点值
        if G(y_insert, x_insert) == 0  
            % 栅格值
            num_insert = (x_insert - 1) + (y_insert - 1) * x;
            % single_new_pop 是一个数组,这段代码的目的是将 num_insert 插入到 single_new_pop 的第 i 个位置之后。
            % single_new_pop(1, 1:i) 表示取 single_new_pop 中第1行,从第1列到第i列的元素。
            % single_new_pop(1, i+1:end) 表示取 single_new_pop 中第1行,从第i+1列到最后一列的元素。
            single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
            
        % 如果是障碍物的话
        else   
            % 它检查该点左面是否有路径 
            % (x_insert - 2) + (y_insert - 1) * x 是否不等于 single_new_pop(1, i)。
            if G(y_insert, x_insert - 1) == 0 && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i)) && ((x_insert - 2) + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert - 1;
                % 索引号
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                % 插入
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                               
            elseif G(y_insert, x_insert + 1) == 0 && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i)) && (x_insert + (y_insert - 1) * x ~= single_new_pop(1, i+1))
                x_insert = x_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            elseif G(y_insert + 1, x_insert) == 0 && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i)) && ((x_insert - 1) + y_insert * x ~= single_new_pop(1, i+1))
                y_insert = y_insert + 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];

            elseif  G(y_insert - 1, x_insert) == 0 && ((x_insert - 1) + (y_insert - 2) * x ~= single_new_pop(1, i)) && ((x_insert - 1) + (y_insert-2) * x ~= single_new_pop(1, i+1))
                y_insert = y_insert - 1;
                num_insert = (x_insert - 1) + (y_insert - 1) * x;
                single_new_pop = [single_new_pop(1, 1:i), num_insert, single_new_pop(1, i+1:end)];
                
            % 左右上下都是占据的则删去路径
            else
                %break_pop = single_new_pop
                single_new_pop = [];
                break
            end    
        end
        
        x_next = x_insert;
        y_next = y_insert;
        max_iteration = max_iteration + 1;
        if max_iteration > 20000
            single_new_pop = [];
            break
        end
        
    end
    
    if isempty(single_new_pop)
        break
    end
    
    [~, single_path_num] = size(single_new_pop);
    i = i + 1;
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/122533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TikTok shop美国小店适合哪些人做?附常见运营问题解答

一、Tiktok shop小店分类 大家都知道,美国小店可以分为5 种: 美国本土个人店: 最灵活,有扶持政策;美国法人企业店:要求高,有扶持政策;美国公司中国人占股店 (ACCU店) : 权重相对低&#xff0c…

Google play的企业开发者账号比个人号上包成功率更高?

众所周知,Google play作为全球最大的Android应用市场,是开发者们推广应用的首选平台。Google play平台提供了两种账号类型:个人开发者和企业开发者,开发者们可以选择创建个人开发者账号或者企业开发者账号进行应用上架。 不过&am…

YOLO目标检测——汽车头部尾部检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用:用于训练自动驾驶系统中的车辆感知模块,以实现对周围车辆头部和尾部的准确检测和识别数据集说明:汽车头部尾部检测数据集,真实场景的高质量图片数据,数据场景丰富标签说明:使用lableimg标注软…

linux软链接和硬链接

1.硬链接(hard link) 每个文件的磁盘存储位置都有一个指针指向他这个指针称为inode,每创建一个硬链接都是指向这个inode指针,而不是指向这个文件的物理磁盘位置。 当有多个硬链接指向同一个inode,删除其中一个硬链接文件,他的物理…

键盘win键无法使用,win+r不生效、win键没反应、Windows键失灵解决方案(亲测可以解决)

最近几天发现自己笔记本的win键无法使用,win失灵了,但是外接键盘后则正常:。 这个问题困扰了我一周,我都以为自己的枪神坏了。 寻找了几个解决方法,网上看了好多好多稀里糊涂的办法,都是不管用的,这里给大…

面试分享 | 护网蓝队面试经验

关于蓝队面试经验 1.自我介绍能力 重要性 为什么将自我介绍能力放在第一位,实际上自我介绍才是面试中最重要的一点,因为护网面试并没有确定的题目,让面试官去提问 更多是的和面试官的一种 “交谈” ,面试的难易程度也自然就取决…

MySQL 8.0 Clone Plugin 详解

文章目录 前言1. 克隆插件安装2. 克隆插件的使用2.1 本地克隆2.2 远程克隆 3. 克隆任务监控4. 克隆插件实现4.1 Init 阶段4.2 File Copy4.3 Page Copy4.4 Redo Copy4.5 Done 5. 克隆插件的限制6. 克隆插件与 Xtrabackup 的异同7. 克隆插件相关参数 后记 前言 克隆插件&#xf…

JS点击图片指定对象变色两种方法

要求&#xff1a;点击上面的颜色实现下面的图像变成相同的颜色 难点&#xff1a;对于js函数的this对象不太清楚如何传递 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>changeColor</title>&l…

vite基础学习笔记:13.Dialog 对话框 (用户注册与登录)

说明&#xff1a;自学做的笔记和记录&#xff0c;如有错误请指正 1. Dialog 对话框组件 目标效果&#xff1a;点击“登录/注册”&#xff0c;弹框 &#xff08;1&#xff09;创建全局组件&#xff0c;在官网中查询代码粘贴 &#xff08;2&#xff09; 注册和使用全局组件 &a…

Zotero从安装到使用再到插件下载【适合小白,只看一篇就够!!!】

一、安装 1.安装Zotero 本人安装的是zotero6&#xff0c;全文是基于zotero6功能的介绍&#xff01; 下载地址&#xff1a;Zotero下载 选择 Custom&#xff0c;因为Standard是标准型会默认放置C盘&#xff0c;并且不能更改&#xff01; 选择自己的路径进行安装&#xff01;…

创建asp.net core mvc项目

一、安装vs2019 百度搜索【visual studio社区版下载】&#xff0c;进入微软官网 二、安装环境 运行vs2019,点击“工具”->“获取工具和功能” 三、打开vs2019 1.“文件”->“新建”->“项目”-> “ASP.Net Core Web 应用&#xff08;模型-视图-控制器&#xff09…

Python TCP服务端多线程接收RFID网络读卡器上传数据

本示例使用设备介绍&#xff1a;WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) #python通过缩进来表示代码块&#xff0c;不可以随意更改每行前面的空白&#xff0c;否则程序会运行错误&#xff01;&#xff01;&#xff01;如果缩进不…

Git->git简介,git的常用命令,git命令的常用理论

git简介git的常用命令git命令的常用理论 1.git简介 Git是什么&#xff1f; Git是一个开源的分布式&#xff0c;用于敏捷高效地处理任何或小或大的项目 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVSI…

2023-11 | 短视频批量下载/爬取某个用户的所有视频 | Python

这里以鞠婧祎的个人主页为demo https://www.douyin.com/user/MS4wLjABAAAACV5Em110SiusElwKlIpUd-MRSi8rBYyg0NfpPrqZmykHY8wLPQ8O4pv3wPL6A-oz 【2023-11-4 23:02:52 星期六】可能后面随着XX的调整, 方法不再适用, 请注意 找到接口 找到https://www.douyin.com/aweme/v1/web/…

蓝桥云课--1014 第 1 场算法双周赛

2-数树数【算法赛】&#xff08;找规律&#xff09; 一、题目要求 二、思路 由此可以推导出来&#xff0c;当s[i]L时&#xff0c;下一个编号当前编号*2-1&#xff1b;当s[i]R时&#xff0c;下一个编号当前编号*2&#xff1b; 三、代码 #include<bits/stdc.h> #define…

安卓三防手持终端 二维码扫描识别器 pda条码手持机

PDA条码手持机是一种快速的数据采集设备&#xff0c;具备多种数据采集功能并且可以进行二次开发&#xff0c;可以针对性的进行定制服务&#xff0c;满足各种业务需求。因其体积小&#xff0c;易操作、功能全、效率高深受物联网行业的青睐。 条码扫描是PDA重要的功能之一&#…

网络安全(黑客)小白自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

C++|前言

c|前言 一、什么是C二、C发展史三、C的重要性3.1语言的使用广泛度3.2工作领域3.3校招领域 四、如何学习C4.1别人怎么学4.2自己怎么学 一、什么是C 在上回书已经学习了C语言&#xff0c;我们知道C语言是面向过程语言&#xff0c;C语言是结构化和模块化的语言&#xff0c;适合处理…

你觉得哪个软件写verilog体验最好?

最近在媒体上看到一个热点问题&#xff0c;浏览量高达680,003。“你觉得哪个软件写verilog体验最好&#xff1f;”这个问题可以说是IC设计师们最想知道的问题&#xff0c;也是大家工作交流中比较常见的。今天移知教育小编就来为大家分享一下&#xff0c;我对于这个问题的解答。…

登陆认证权限控制(2)—— 基于Spring security 安全框架的权限管理 注解式权限控制 RABC模型

前言 登陆认证&#xff0c;权限控制是一个系统必不可少的部分&#xff0c;一个开放访问的系统能否在上线后稳定持续运行其实很大程度上取决于登陆认证和权限控制措施是否到位&#xff0c;不然可能系统刚刚上线就会夭折。 Spring Security 是一个能够为基于 Spring 的企业应用…