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

news2024/10/7 10:22:38

目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

史上第一款AOSP开发的IDE (支持Java/Kotlin/C++/Jni/Native/Shell/Python)

ASFP Study 史上第一款AOSP开发的IDE (支持Java/Kotlin/C/Jni/Native/Shell/Python) 类似于Android Studio,可用于开发Android系统源码。 Android studio for platform,简称asfp(爱上富婆)。 背景&下载&使用 背景 由…

8.4 矢量图层点要素分类(Categorized)渲染使用

文章目录 前言分类(Categorized)渲染QGis代码实现 总结 前言 前面几章介绍了矢量-点要素-单一符号的各种用法所谓单一符号是指点要素的符号在图层显示时只有一种形式下面介绍的分类(Categorized)渲染说明:文章中的示例…

【Python爬虫库】pytube使用方法

一、pytube库简介 pytube库是一个python第三方库,用于youtube视频的抓取和其他相关操作。官方文档:pytube 二、基本操作 1、显示视频标题 from pytube import YouTube yt YouTube(https://youtube.com/watch?vIAJsZWhj6GI) print(yt.title)说明&am…

自建网盘平台搭建(源码+教程)

为什么要自己搭建网盘,现在许多大厂的网盘,文件都添加了许多限制,有好多文件会遭到和谐,而且大部分网盘也都会限速,不开通VIP是很难用的!这是一套可以运营的网盘,代码无加密可以进行二次开发。下…

dos命令bat结合任务计划程序自动执行py文件

效果 bat文件 run.bat @echo off call C:\ProgramData\Anaconda3\Scripts\activate.bat pytorch C:\ProgramData\Anaconda3\envs\pytorch\python.exe E:\Gerapy_py\gerapy\projects\xmtv\xmtv\start_urls.py下面这个bat文件可以用来判断py文件是否执行成功 @echo off call C…

【Java】Netty创建网络服务端客户端(TCP/UDP)

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Netty创建网络服务端客户端示例。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更…

MySQL模糊查询/模式匹配(Pattern Match)

使用SQL查询数据时,时常会遇到这种情况,我们并不需要精确的匹配,而是要查找具有某类特点的数据。这种场景我们就要用到模糊查询。MySQL中常用的模糊查询方法有2种: like语句模糊查询regexp正则表达式模式匹配 目录 一、使用like模…

基于SSM的社区生鲜电商平台

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

Java / Android 多线程和 synchroized 锁

s AsyncTask 在Android R中标注了废弃 synchronized 同步 Thread: thread.start() public synchronized void start() {/*** This method is not invoked for the main method thread or "system"* group threads created/set up by the VM. Any new functionali…

Visual Interpretability for Deep Learning: a Survey

Visual Interpretability for Deep Learning: a Survey----《深度学习的视觉可解释性:综述》 摘要 本文回顾了最近在理解神经网络表示以及学习具有可解释性/解耦的中间层表示的神经网络方面的研究。尽管深度神经网络在各种任务中表现出了优越的性能,但可解释性始终…

基于SpringBoot+Vue+uniapp微信小程序实验室预约管理平台详细设计和实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

getid3 获取视频时长

1、首先,我们需要先下载一份PHP类—getid3https://codeload.github.com/JamesHeinrich/getID3/zip/master 2.我在laravel6.0 中使用 需要在composer.json 自动加载 否则系统访问不到 在命令行 执行 composer dump-autoload $getID3 new \getID3();//视频文件需要放…

【PostgreSql本地备份为dump文件与恢复】使用脚本一键备份为dump文件

环境:windows数据库:postgresql 1.准备脚本 backUpDb.bat 脚本为备份脚本,双击运行,右键可以选择编辑;restoreDb.bat 脚本为恢复脚本,双击运行,右键选择编辑; 1.1 脚本介绍 如上图…

数据结构:串(定义,基本操作,存储结构)

目录 1.串的定义2.串的基本操作3.字符集编码4.串的存储结构1.顺序存储2.链式存储 1.串的定义 串,即字符串( String)是由零个或多个字符组成的有限序列。 一般记为s ‘a1a2……an’ (n ≥0) 其中,S是串名,单引号括起来的字符序列是…

企业清算有哪些类型?在哪里可以查看相关公告?

企业清算是什么? 企业清算指企业按章程规定解散以及由于破产或其他原因宣布终止经营后,对企业的财产、债权、债务进行全面清查,并进行收取债权,清偿债务和分配剩余财产的经济活动。 企业清算给分为破产清算,非破产清…

华为云交换数据空间 EDS:“可信、可控、可证”能力实现你的数据你做主

文章目录 前言一、数据安全流通价值的必要性和紧迫性1.1、交换数据空间(EDS)背景1.2、《数字中国建设整体布局规划》1.3、数据流通成为制约数据要素价值释放的瓶颈 二、华为云 EDS 解决方案介绍2.1、构建可控数据交换空间2.2、可控的数据交换框架2.3、定…

互联网医院|线上医疗引领行业发展

您是否曾经遇到过这样的问题:在忙碌的工作中,突然感到身体不适,但却又不想浪费时间和金钱去实体医院?或者是因为疫情的限制,出门看病变得困难重重?那么,今天我要向您介绍的,正是解决…

中断 NVIC的概念和原理

1.什么是中断 中断: 由于中断源的触发,常规程序被打断, CPU转 而运行中断响应函数,而后又回到常规程序的执行, 这一过程叫做中断。 中断优先级的概念 中断的意义和作用 中断处理的过程和术语 STM32 GPIO外部中断简…

【java:牛客每日三十题总结-3】

java:牛客每日三十题总结 总结如下 总结如下 集合相关知识点 Collection主要的子接口: List:可以存放重复内容 Set:不能存放重复内容,所有重复的内容靠hashCode()和equals()两个方法区分 Queue:队列接口 SortedSet:可以对集合中的数据进行排序 Map没有继承Collection接口&…

最新AI系统ChatGPT源码+AI绘画系统源码+支持GPT4.0+Midjourney绘画+搭建部署教程+附源码

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…