matlab-3D装箱代码

3D装箱:给定装载的四个约束:长,宽,高,限重,若干待装载货箱的信息:长,宽,高,重量,求满足约束的情况下,最佳的装载方式(或是达到最高载重,或是达到最大装载体积),以货物的装载顺序和在卡车中的位置表示。(注:货物放置在另外一个货物的上面时,需要有至少80%的支撑面积)
求解思路:该算法的思路很简单,可以理解为先穷举,像一个树生出很多分叉,每一个装箱方式是从树根到枝头的路径,每一个结点是一个要装的箱子。再剪枝,待装箱方式的数量达到N时,按照一定的筛选规则,留下M个装箱方式(M<N)。然后M个装箱方式,继续增加结点,增加路径,待装箱方式再次达到N时又进行剪枝,循环直到装完所有箱子。额外的细节操作是:先把尺寸统一的货箱打包成合适的尺寸,以降低装载的复杂度。其次,设置策略为每个货箱选择合适的落脚点。最后,对多种装箱方式进行挑选,只对若干优秀的方式继续装填,舍弃劣解。

代码和简单的装箱数据放在如下网盘中:
链接:https://pan.baidu.com/s/1Jk7C8wcd1aShf17bE5jQsw
提取码:sukw
注:matlab版本需要大于等于2018,否则可能会报错

另再附代码,方便讨论:

  • 主程序 main (展示使用方法)
  • 主装箱算法 final_zhuangxiang (整体框架)
  • 对箱子进行分类打包 classification
  • 对打包后的箱子及没打包的箱子进行装箱 zhuangxiang1

运行结果如图所示:
在这里插入图片描述

load data.mat
PATH={};
truck=[18000,2600,2600,23000]; % 长,宽,高,载重
[real_PATH,objective,surplus_box]=final_zhuangxiang(PATH,box,truck);
show(real_PATH,truck)
function [real_PATH,objective,surplus_box]=final_zhuangxiang(PATH,box,truck)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数名称:主装箱算法 final_zhuangxiang
%%入口参数:已装车的货物 PATH(cell格式,5行(左下坐标+信息+名称+名称+旋转方向)多列)  待装箱的货物信息 box(cell格式,第一行为长宽高重,第二行为货物的名称,第三行同第二行,第四行是旋转方向)  货车信息 truck
%%出口参数:装车的货物 real_PATH(格式同上述PATH) 当前车辆的优化目标 objective(max(v/V,w/W))   未能装车的剩余货物 surplus_box
%%函数功能说明:
    %%输入已装车的货物,未装车的货物,truck进行装车,步骤如下:
    %%步骤1:打包
    %%步骤2:对打包后的箱子+未打包的箱子进行装车
    %%步骤3:解包
    %%步骤4:评价
%%注意:
%%by SebastianLi, At ZhengZhou, 25th February, 2021
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% 如果PATH不为空,并以剩余的空间作为约束进行打包,以防止打包过大
truck1=truck;
if isempty(PATH)==0
    t=zeros(size(PATH,1),1);
    num_PATH=1;
    angles=[];
    w=0;
    for j=1:size(PATH{num_PATH, 1},1)
        s=eightangle(PATH{num_PATH, 1}{j, 1},PATH{num_PATH, 1}{j, 2});
        angles(end+1:end+8,:)=s;
        w=w+PATH{num_PATH, 1}{j,2}(1,4);
    end
    truck1=[truck(1,1)-max(angles(:,1)),truck(1,2),truck(1,3),truck(1,4)-w];  % 只考虑x方向的剩余空间
end

%% 对于两种方向的箱子进行打包,取打包后箱子数小的打包方式作为最终打包
s2={};
for i=1:size(box,2)  
    s=box{1, i};
    s1=[s(1, 2),s(1, 1),s(1, 3),s(1, 4)];
    s2(1,i)={s1};
    s2(2,i)=box(2, i);
end
s2(3,:)=s2(2, :);
s2(4,:)={0};
[Allbox1,~,~]=classification(s2,truck1);  % 对全部旋转后的箱子进行分类打包
Allbox1(4,:)={1};

[Allbox2,~,~]=classification(box,truck1); % 对未旋转的箱子进行分类打包
Allbox2(4,:)={0};
if size(Allbox1,2)>=size(Allbox2,2)       %  取打包后箱子数小的打包方式作为最终打包
    Allbox=Allbox2;
    y1=1;
else
    Allbox=Allbox1;
    y1=0;
end

%% 对打包后的箱子+未打包的箱子进行装车
s2={};       % 旋转后的Allbox
for i=1:size(Allbox,2)  
    s=Allbox{1, i};
    s1=[s(1, 2),s(1, 1),s(1, 3),s(1, 4)];
    s2(1,i)={s1};
    s2(2,i)=Allbox(2, i);
    s2(3,i)=Allbox(3, i);
    s2(4,i)={y1};
    if strcmp(Allbox{2,i}(1:3),'bag')==1
        for j=1:size(s2{3,i},1)
            s2{3,i}{j,1}=[s2{3, i}{j, 1}(1,2),s2{3, i}{j, 1}(1,1),s2{3, i}{j, 1}(1,3)];
            s2{3,i}{j,2}=[s2{3, i}{j, 2}(1,2),s2{3, i}{j, 2}(1,1),s2{3, i}{j, 2}(1,3),s2{3, i}{j, 2}(1,4)];
        end
    end
end
 Allbox=[Allbox,s2];  % 把旋转前后的箱子都放在一起
[final_PATH,~,surplus_box]=zhuangxiang1(PATH,Allbox,truck);
% show(final_PATH)
%% 把surplus_box中的bag拆分出来
if isempty(surplus_box)==0
    ss={};
    for i=1:size(surplus_box,2)
        if strcmp(surplus_box{2,i}(1:3),'bag')==0   % 如果不是bag
            if surplus_box{4,i}==0
                ss(:,end+1)=[surplus_box(1,i);surplus_box(2,i)];
            else
                ss(:,end+1)=[{[surplus_box{1, i}(1,2),surplus_box{1, i}(1,1),surplus_box{1, i}(1,3),surplus_box{1, i}(1,4)]};surplus_box(2,i)];
            end
        else
            if surplus_box{4,i}==0
                for j=1:size(surplus_box{3,i},1)
                    ss(:,end+1)=[surplus_box{3,i}(j,2);surplus_box{3,i}(j,3)];
                end    
            else
                    for j=1:size(surplus_box{3,i},1)
                    ss(:,end+1)=[{[surplus_box{3,i}{j,2}(1,2),surplus_box{3,i}{j,2}(1,1),surplus_box{3,i}{j,2}(1,3),surplus_box{3,i}{j,2}(1,4)]};surplus_box{3,i}(j,3)];
                    end
                    
            end
        end
    end
    surplus_box=ss; 
    surplus_box(3,:)=surplus_box(2,:);
    surplus_box(4,:)={0};
end

%% 拆分bag
if isempty(final_PATH)==1  % 如果所有的箱子都不能装车
    real_PATH={};
    objective=0;
else
    real_PATH={};  % 把bag再拆分成箱子
    for i=1:size(final_PATH{1, 1},1)
        if strcmp(final_PATH{1, 1}{i, 3}(1:3),'bag')==0
            real_PATH(end+1,:)=final_PATH{1, 1}(i,:);
        end
        if strcmp(final_PATH{1, 1}{i, 3}(1:3),'bag')==1
                s=final_PATH{1, 1}{i, 4};
                for j=1:size(final_PATH{1, 1}{i, 4},1)
                    s{j,1}=final_PATH{1, 1}{i, 4}{j, 1}+final_PATH{1, 1}{i, 1};
                    s{j,4}=s{j,3};
                    s{j,5}=0;
                end
                real_PATH(end+1:end+size(s,1),:)=s;
        end
    end  
%% 评估货车的装载情况
    v=0;
    w=0;
    for i=1:size(real_PATH,1)
        v=v+real_PATH{i, 2}(1,1)*real_PATH{i, 2}(1,2)*real_PATH{i, 2}(1,3);
        w=w+real_PATH{i, 2}(1,4);
    end

    V=truck(1,1)*truck(1,2)*truck(1,3);
    W=truck(1,4);
objective=max(v/V,w/W);
end
function [Allbox,Bag,surplus_box]=classification(box,truck)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数名称:分类打包程序 classification
%%入口参数:货物信息 box(cell格式,第一行为长宽高重,第二行为货物的名称,第三行同第二行,第四行是旋转方向)  货车信息 truck
%%出口参数:打包后的箱子 Allbox  打包的货物 Bag 未能处理的剩余货物 surplus_box
%%函数功能说明:
    %%按照尺寸进行分类打包,步骤如下:
    %%步骤1:对可能出现的超高箱子进行处理
    %%步骤2:按照尺寸进行分类,如果x,y,z相同则为同一类
    %%步骤3:对分类结果按照每类的个数大于1或等于1进行分离
    %%步骤4:对个数大于1的种类,调用同类装箱算法进行打包
    %%步骤5:对优化目标t~=1的种类,进行补充之后,再次进行打包
%%注意:
%%by SebastianLi, At ZhengZhou, 24th February, 2021
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 如果存在过高的箱子,将该类箱子储存到tt1中,并在最后补充到surplus_box中
s=[];
for i=1:size(box,2)
    if box{1,i}(1,3)>truck(1,3)
        s(end+1,1)=i;
    end
end
tt1=box(:,s);
box(:,s)=[];

%% 按照尺寸进行分类 分类结果为s        
s={};
for i=1:size(box,2)
    s1=box{1, i}(1,1:3);
    s3=[];
    for j=1:size(box,2)
        s2=box{1, j}(1,1:3);
        if sum(s1-s2)==0 && s1(1,3)==s2(1,3)   % x,y,z相同
            s3(end+1,:)=j;
        end
    end
    s(i,1)={s3};
end

s5=[];
for i=1:size(s,1)
    s4=0;
    
    for j=1:size(s,1)
        if  i<j && size(s{i,1},1)==size(s{j,1},1) && sum(abs(s{i,1}-s{j,1}))==0
            s4=j;
        end
    end
    if s4~=0
       s5(end+1,1)=i;
    end
end
s(s5,:)=[];   % s 所有的分类结果

%% 对分类结果进行挑选:每类箱子的数目大于1为smallplat  每类箱子只有一个为s3  
s2=[];
for i=1:size(s,1)
    if size(s{i,1},1)==1
        s2(end+1,1)=i;
    end
end
s(s2,:)=[];  % s 每类的数目大于1的结果

s2=[];
for i=1:size(s,1)
    s2=[s2;s{i,1}];
end
s3=box;
s3(:,s2)=[];  % s3 每类箱子只有一个
surplus_box=s3;

smallplat={};
for i=1:size(s,1)
    smallplat(i,1)={box(:,s{i,1})};
end

%% 对同类箱子进行打包 输出结果为s2
s2={};        % s2 对同类的箱子进行装箱
s4=[];
surplus_box2={};
for i=1:size(smallplat,1)
     [PATH,t,surplus_box1]=tong_zhuangxiang(smallplat{i,1},truck);
     if isempty(PATH)==0
         PATH{1,1}(:,4)=PATH{1,1}(:,3);
         PATH{1,1}(:,5)=smallplat{i,1}(4,1);
         s2(i,1)=PATH;
     else
         s2(i,1)={0};
     end
     s4(i,1)=t;
      if isempty(surplus_box1)==0
          surplus_box2=[surplus_box2,surplus_box1];  
      end
end
if isempty(surplus_box2)==0
%     surplus_box2(:,1)=[];
    surplus_box=[surplus_box,surplus_box2];
end
s2(s4==0,:)=[];
s4(s4==0,:)=[];
%% 对t~=1的打包进行再补充      
m1=s2(s4>0 & s4<=0.9,:);
m2=s2(s4>0.9 & s4<1,:);
s2((s4>0.9 & s4<1)|(s4>0 & s4<=0.9),:)=[];

% 将t<=0.9的进行删减,直到t=1
t1=m1;
t2={};   % t2 保存新的打包
t3={};   % t3 保存被删掉的箱子
t4=t1;
for i=1:size(t1,1) 
    t=0;
    k=0;
    while t~=1
        t1{i,1}(end,:)=[];
        [~,~,t]=select1(t1(i,1));
        k=k+1;
    end
    t2(i,:)=t1(i,1);
    t3(i,:)={t4{i,1}(end-k+1:end,:)};
end
s2=[s2;t2];

% 将t3进行补充到surplus_box
t4={};
t5={};
for i=1:size(t3,1)
        for j=1:size(t3{i,1},1)
            surplus_box(1,end+1)=t3{i,1}(j,2);
            surplus_box(2,end)=t3{i,1}(j,3);
            surplus_box(3,end)=t3{i,1}(j,3);
            surplus_box(4,end)={0};
        end
end

% 对t大于0.9的打包进行补给
t3=[];
t4={};
 t1=m2; 
for i=1:size(t1,1) 
    angles=[];
    t2=t1{i,1}(:,1);  
    for j=1:size(t2,1)
        s=eightangle(t2{j, 1},t1{i,1}{1, 2});
        angles(end+1:end+8,:)=s;
    end
    [newPATH,t,surplus_box]=zhuangxiang1(t1(i,1),surplus_box,[max(angles(:,1)),max(angles(:,2)),max(angles(:,3)),18000]);
    t3(i,1)=t;
    t4(i,1)=newPATH;     % 更新后的路径
end
s2=[s2;t4];
bag=s2;

%% 对bag进行打包
Bag={};
for i=1:size(bag,1)
    s=bag{i,1};
    angles=[];
    for j=1:size(s,1)
        s1=eightangle(s{j, 1},s{j, 2});
        angles(end+1:end+8,:)=s1;
    end
    s2=0;
    for j=1:size(s,1)
        s2=s2+s{j, 2}(1,4);
    end
    Bag(1,i)={[max(angles(:,1)),max(angles(:,2)),max(angles(:,3)),s2]};
    Bag(2,i)={['bag' num2str(i)]};
    Bag(3,i)={s};
    Bag(4,i)={0};
end

%% 最终输出

if isempty(surplus_box)==1
    surplus_box=tt1;
    Allbox=Bag;
else
    surplus_box(3,:)=surplus_box(2,:);
    if isempty(tt1)==0
        surplus_box=[surplus_box,tt1];
        Allbox=[Bag,surplus_box];
    else
        Allbox=[Bag,surplus_box];
    end
end
function  [final_PATH,t,surplus_box]=zhuangxiang1(PATH,surplus_box,truck)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数名称:3D装箱程序 zhuangxiang
%%入口参数:已装车的货物 PATH(cell格式,5行(左下坐标+信息+名称+名称+旋转方向)多列)  待装箱的货物信息 surplus_box(cell格式,第一行为长宽高重,第二行为货物的名称,第三行同第二行,第四行是旋转方向)  货车信息 truck
%%出口参数:装车的货物 final_PATH(格式同上述PATH) 当前车辆的优化目标 t(max(v/V,w/W)) 未能装车的剩余货物 surplus_box
%%函数功能说明:
    %%输入已装车的货物,未装车的货物,truck进行装车,步骤如下:
    %%步骤1:筛选,除去不满足约束的箱子,将该箱子在最后放入surplus_box
    %%步骤2:如果PATH为空,设置初始PATH,并判断货物的高度是否超出货车的高度
    %%步骤3:解包
    %%步骤4:评价
%%注意:surplus_box应该是4行多列
%%by SebastianLi, At ZhengZhou, 25th February, 2021
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% 统计PATH的体积和w,筛选不能装车的箱子
ss1={};
if isempty(PATH)==0
    v=0;
    w=0;
    for i=1:size(PATH{1, 1},1)
        v=v+PATH{1, 1}{i, 2}(1,1)*PATH{1, 1}{i, 2}(1,2)*PATH{1, 1}{i, 2}(1,3);
        w=w+PATH{1, 1}{i, 2}(1,4);
    end
    V=truck(1,1)*truck(1,2)*truck(1,3)-v;  % V 卡车剩余的体积
    W=truck(1,4)-2;                        % W 卡车剩余的载重
    s=[];
    for i=1:size(surplus_box,2)
        v=surplus_box{1,i}(1,1)*surplus_box{1,i}(1,2)*surplus_box{1,i}(1,3);
        w=surplus_box{1,i}(1,4);
        if v>V || w>W
            s(end+1,1)=i;
        end
    end
    ss1=surplus_box(:,s);              % 会导致surplus中出现重复的箱子编号,原箱子和旋转之后的箱子
    surplus_box(:,s)=[];
end
%% 如果surplus_box中存在长度或是宽度超出truck的box,直接置空,认定是旋转导致
s=[];
for i=1:size(surplus_box,2)
    if surplus_box{1,i}(1,1)>truck(1,1) || surplus_box{1,i}(1,2)>truck(1,2)
        s(end+1,1)=i;
    end
end
surplus_box(:,s)=[];
%% 如果PATH为空,设置初始PATH,并判断货物的高度是否超出货车的高度
tt1={};
if isempty(PATH)==1
    s=[];
    for i=1:size(surplus_box,2)
        if surplus_box{1,i}(1,3)>truck(1,3)
            s(end+1,1)=i;
        end
    end
    tt1=surplus_box(:,s);
    surplus_box(:,s)=[];
    START=[0,0,0];   % 起始坐标
    if size(surplus_box,1)==4
        for i=1:size(surplus_box,2)
            box_size=surplus_box{1, i};
            PATH{i,1}(1,:)={START,box_size,surplus_box{2, i},surplus_box{3, i},surplus_box{4, i}};   % PATH 待增添的装箱次序
        end
    elseif size(surplus_box,1)==2
        surplus_box=[surplus_box;surplus_box(2,:)];
        surplus_box(end+1,:)={0};
        for i=1:size(surplus_box,2)
            box_size=surplus_box{1, i};
            PATH{i,1}(1,:)={START,box_size,surplus_box{2, i},surplus_box{3, i},surplus_box{4, i}};   % PATH 待增添的装箱次序
        end
    end
end
%% 防止surplus_box是两行
% if  size(surplus_box,1)==2
%         surplus_box=[surplus_box;surplus_box(2,:)];
%         surplus_box(end+1,:)={0};
% end
%% 装车
box=surplus_box;
if size(PATH,1)==0  % 若当前所有的箱子高度都超过货车高度,则直接输出
    t=0;
    final_PATH={};
    surplus_box=tt1;
else
  ss=0;
  Allpath={};
  while  size(PATH,2)~=0 % 当车被装满或是货被装完,停止

        % 对当前的装箱进行挑选
       if (ss>2 && mod(ss,3)==0 && size(PATH,1)>30) || size(PATH,1)>=300  
           [~,b,~]=select(PATH); 
            PATH=PATH(b(1:30,:),:);
       end
       new_path={};          % new_path 加上一个新的箱子后的总箱子
       for i=1:size(PATH,1)  % i 对每一种待增添的次序
           angles=[];

           % surplus_box 统计未装车的箱子
           s=PATH{i,1};
           s1=[];
           for m=1:size(s,1)
               for n=1:size(box,2)
                   if strcmp(s{m,3},box{2,n})==1
                       s1(end+1,1)=n;
                   end
               end
           end
           surplus_box=box;
           surplus_box(:,s1)=[];        

           % angle 统计下一步可行的落脚点
           for j=1:size(PATH{i,1},1)
               s=eightangle(PATH{i, 1}{j, 1},PATH{i, 1}{j, 2});
               angles(end+1:end+8,:)=s;  % angles 所有货物的所有顶点
           end
           angle=choose_angle1(angles);

           % vertex 统计所有货物的上顶点,用于判断支撑面积
           vertex=[];  
           for o=1:size(PATH{i, 1},1)
               s=eightangle(PATH{i, 1}{o,1},PATH{i, 1}{o,2});
               vertex(end+1:end+4,:)=s(5:8,:);
           end 

            w_path=0;
           for q=1:size(PATH{i,1},1)
               w_path=w_path+PATH{i,1}{q,2}(1,4);
           end
               
               
           s6={};
           for k=1:size(angle,1)                        % k 对每一个落脚点
               s=angle(k,:);                            % s 当前的落脚点

               for l=1:size(surplus_box,2)              % l 对每一个未装车的箱子
                   s1=surplus_box{1, l};      % s1 box的长宽高重
                   s2=s+s1(:,1:3);
                   if isempty(find((truck(:,1:3)-s2)<0))==1 && s1(:,end)+w_path<truck(:,end) && s(:,3)~=0 % 如果体积和载重满足要求,且当落脚点不是地面
                      s3=vertex(find(vertex(:,3)==s(:,end)),:);   % s3 相同z值的所有上顶点
                      s4=eightangle(s,s1);
                      s4=s4(1:4,:);                      % s4 box的底面下顶点
                      s5=0;                              % s5 支撑面积
                      for p=1:size(s3,1)/4 
                        s5=s5+rectint([s3((p-1)*4+1,1:2),s3((p-1)*4+2,1)-s3((p-1)*4+1,1),s3((p-1)*4+3,2)-s3((p-1)*4+2,2)],[s4(1,1:2),s4(2,1)-s4(1,1),s4(3,2)-s4(2,2)]);
                      end
                      if s5>=0.8*s1(1,1)*s1(1,2)   % 如果支撑面积超过底面积的80%
                          s6=PATH{i,1};
                          s6(end+1,:)={s,s1,surplus_box{2,l},surplus_box{3,l},surplus_box{4,l}};  % s6 保存新的箱子
                          new_path(end+1,1)={s6};
                      end
                   elseif isempty(find((truck(:,1:3)-s2)<0))==1 && s1(:,end)+w_path<truck(:,end) && s(:,3)==0 % 如果体积和载重满足要求,且当前落脚点是地面
                           s6=PATH{i,1};
                           s6(end+1,:)={s,s1,surplus_box{2,l},surplus_box{3,l},surplus_box{4,l}};  % s6 保存新的箱子
                           new_path(end+1,:)={s6};
                   end
               end
           end
            if isempty(s6)==1
               Allpath(end+1,1)=PATH(i,1);  %把每一个不能再更新的路径保存
%                Allpath(end,2)=PATH(i,2);
            end
       end

       % unique摆放的位置和箱子型号都相同的摆放方式
        t=[]; 
        for i=1:size(new_path,1)-1
            s1=new_path{i, 1}(:,1);
            s11=new_path{i, 1}(:,2);
            s2=new_path{i+1, 1}(:,1);
            s22=new_path{i+1, 1}(:,2);
            if size(find((cell2mat(s1)-cell2mat(s1))~=0),1)==0 &&  size(find((cell2mat(s11)-cell2mat(s22))~=0),1)==0
                t(end+1,1)=i+1;
            end
        end
        new_path(t,:)=[];
        PATH=new_path;
        ss=ss+1;
  end
%% 输出 
  [~,b,t]=select2(Allpath,truck);
  final_PATH=Allpath(b(1,1),:);
  t=t(b(1,1),:);

%% 统计剩余的箱子 
% s=box;
% s2=0;
% for i=1:size(s,2)
%     s1=0;
%     for j=i:size(s,2)
%         if strcmp(s{2,i},s{2,j})==1
%             s1=s1+1;
%         end
%     end
%     if s1==1
%         s2=s2+1;    % s2是box中箱子的个数,不含重复
%     end
% end
%   
% surplus_box 统计未装车的箱子  
    s=final_PATH{1,1};
    s1=[];
    for m=1:size(s,1)
       for n=1:size(box,2)
           if strcmp(s{m,3},box{2,n})==1
               s1(end+1,1)=n;
           end
       end
    end
    surplus_box=box;
    surplus_box(:,s1)=[];    
  
if isempty(surplus_box)==1
    surplus_box=tt1;
else
    surplus_box=[surplus_box,tt1];
end

if isempty(surplus_box)==1
    surplus_box=ss1;
else
    surplus_box=[surplus_box,ss1];
end

s1={};
if isempty(surplus_box)==0 && size(surplus_box,2)>1
    for i=1:size(surplus_box,2)
        s=0;
        for j=i:size(surplus_box,2)
            if strcmp(surplus_box{2,i},surplus_box{2,j})==1
                s=s+1;
                i
                j
            end
        end
        if s==1
            s1(:,end+1)=surplus_box(:,i);
        end
    end
    surplus_box=s1;  
end
  
end

function  [PATH,t,surplus_box]=tong_zhuangxiang(box,truck)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数名称:同类装箱程序 tong_zhuangxiang
%%入口参数:货物信息 box(cell格式,第一行为长宽高重,第二行为货物的名称,第三行同第二行,第四行是旋转方向)  货车信息 truck
%%出口参数:规划的装箱路径 PATH  优化目标 t(v/max(x*y*z)) 剩余的货物 surplus_box
%%函数功能说明:
    %%对同类进行打包,步骤如下:
    %%步骤1:判断总箱子是否超重,如果超重,只对限重范围内的箱子进行装车
    %%步骤2:将箱子进行装车(按照先z,再y,后z的方向)
    %%装车:预估可以箱子可以排成多少行列,按照不同的情况,进行装车。
%%注意:box至少包含两个箱子
%%by SebastianLi, At ZhengZhou, 24th February, 2021
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
w=0;  
num_box=size(box,2);   % 货物的个数
for i=1:num_box
   w=w+box{1,i}(1,4);  % 货物的总重量
end
%% 判断总箱子是否超重,如果超重,只对限重范围内的箱子进行装箱
if w>truck(1,4)   
    w=0;
   for i=1:num_box
       w=w+box{1,i}(1,4);
       if w>truck(1,4)
           break
       end
   end
   if i>1       % 如果在限重范围内,还存在部分箱子可以装车,则该些箱子进行装车
        uu1=box(:,i:end);
        box=box(:,1:i-1);
        num_box=size(box,2);
        x=box{1, 1}(1,1);      % 货物尺寸
        y=box{1, 1}(1,2);
        z=box{1, 1}(1,3);

        truck_x=truck(1,1);    % 卡车尺寸
        truck_y=truck(1,2);
        truck_z=truck(1,3);

        num_x=fix(truck_x/x);  % 在x方向上最多能装num_x个
        num_y=fix(truck_y/y);  % 在y方向上最多能装num_y个
        num_z=fix(truck_z/z);  % 在z方向上最多能装num_y个

        START=[0,0,0];         % 起始坐标
        if x<=truck_x && y<=truck_y && z<=truck_z         % 如果长宽高都满足约束
            PATH{1,1}(1,:)={START,box{1, 1},box{2, 1}};   % PATH 待增添的装箱次序
            
                if num_box<=num_z                         % 如果全部货物可以排成一柱,即底面积为x*y的高矩形
                    for i=2:num_box
                        PATH{1,1}(i,:)={[0,0,(i-1)*z],box{1, i},box{2, i}};
                    end
                end

                if num_box>num_z && num_box<=num_y*num_z   % 如果全部货物可以排成一排,像底面积为x*truck_y的高矩形
                    if num_z==1
                        for i=2:num_box
                            PATH{1,1}(i,:)={[0,(i-1)*y,0],box{1, i},box{2, i}};
                        end 
                    elseif num_z>1
                           t2=zeros(num_z,1);
                            for j=1:num_y-1
                                t3=ones(num_z,1)*j;
                                t2=[t2;t3];           % t2 对于y
                            end
                            while size(t2,1)<num_box
                                t2=[t2;t2];
                            end
                              t4=(0:num_z-1)'; 
                              while size(t4,1)<num_box
                                  t4=[t4;t4];
                              end                      % t4 对于z  
                             for i=2:num_box
                                 PATH{1,1}(i,:)={[0,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};  % (fix((i+1)/num_z)-1)*y    mod(i+1,num_z)*z
                             end 
                    end
                end

                if num_box>num_y*num_z && num_box<=num_x*num_y*num_z   % 如果全部货物可以装成多排,并且可以全部装进车厢里
                    t2=zeros(num_z,1);
                    for j=1:num_y-1
                        t3=ones(num_z,1)*j;
                        t2=[t2;t3];           % t2 对于y
                    end
                    while size(t2,1)<num_box
                        t2=[t2;t2];
                    end
                      t4=(0:num_z-1)'; 
                      while size(t4,1)<num_box
                          t4=[t4;t4];
                      end                      % t4 对于z  
                    for i=2:num_box  
                        PATH{1,1}(i,:)={[fix((i-1)/(num_y*num_z))*x,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};
                    end
                end
                surplus_box={};
                if num_box>num_x*num_y*num_z  % 如果货物不能全部装进车厢里,按照多排进行排放,并统计剩余箱子
                    surplus_box=box(:,1:num_box-num_x*num_y*num_z);
                    box(:,1:num_box-num_x*num_y*num_z)=[];
                    PATH{1,1}(1,:)={START,box{1, 1},box{2, 1}};
                    num_box=num_x*num_y*num_z;
                    t2=zeros(num_z,1);
                    for j=1:num_y-1
                        t3=ones(num_z,1)*j;
                        t2=[t2;t3];           % t2 对于y
                    end
                    while size(t2,1)<num_box
                        t2=[t2;t2];
                    end
                      t4=(0:num_z-1)'; 
                      while size(t4,1)<num_box
                          t4=[t4;t4];
                      end                      % t4 对于z  
                    for i=2:num_box  
                        PATH{1,1}(i,:)={[fix((i-1)/(num_y*num_z))*x,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};
                    end
                end
                t=zeros(size(PATH,1),1);
                for num_PATH=1:size(PATH,1)   
                    angles=[];
                    for j=1:size(PATH{num_PATH, 1},1)
                        s=eightangle(PATH{num_PATH, 1}{j, 1},PATH{num_PATH, 1}{j, 2});
                        angles(end+1:end+8,:)=s;
                    end
                end
                place=max(angles(:,1))*max(angles(:,2))*max(angles(:,3));  
                s1=0;  
                for i=1:size(PATH{num_PATH, 1},1)
                    s1=s1+PATH{num_PATH, 1}{i, 2}(1,1)*PATH{num_PATH, 1}{i, 2}(1,2)*PATH{num_PATH, 1}{i, 2}(1,3);
                end
                t(num_PATH,1)=s1/place;   % t 目标值
        else            % 如果长宽高不满足约束,输出空集
            PATH={};
            t=0;
            surplus_box=box;
        end
        if isempty(surplus_box)==0
            surplus_box=[surplus_box,uu1];
        else
            surplus_box=uu1;
        end
   else     % 如果只有一个箱子,且超重,直接输出
       PATH={};
       t=0;
       surplus_box=box;
   end
else
        x=box{1, 1}(1,1);     % 货物尺寸
        y=box{1, 1}(1,2);
        z=box{1, 1}(1,3);

        truck_x=truck(1,1);   % 卡车尺寸
        truck_y=truck(1,2);
        truck_z=truck(1,3);

        num_x=fix(truck_x/x);  % 在x方向上最多能装num_x个
        num_y=fix(truck_y/y);
        num_z=fix(truck_z/z);

        START=[0,0,0];   % 起始坐标
        if x<=truck_x && y<=truck_y && z<=truck_z
            PATH{1,1}(1,:)={START,box{1, 1},box{2, 1}};   % PATH 待增添的装箱次序
                if num_box<=num_z   % 如果全部货物可以排成底面积为x*y的高矩形
                    for i=2:num_box
                        PATH{1,1}(i,:)={[0,0,(i-1)*z],box{1, i},box{2, i}};
                    end
                end

                if num_box>num_z && num_box<=num_y*num_z % 如果全部货物可以排在底面积为x*truck_y的高矩形
                    if num_z==1
                        for i=2:num_box
                            PATH{1,1}(i,:)={[0,(i-1)*y,0],box{1, i},box{2, i}};
                        end 
                    elseif num_z>1
                           t2=zeros(num_z,1);
                            for j=1:num_y-1
                                t3=ones(num_z,1)*j;
                                t2=[t2;t3];           % t2 对于y
                            end
                            while size(t2,1)<num_box
                                t2=[t2;t2];
                            end
                              t4=(0:num_z-1)'; 
                              while size(t4,1)<num_box
                                  t4=[t4;t4];
                              end                      % t4 对于z  
                             for i=2:num_box
                                 PATH{1,1}(i,:)={[0,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};  % (fix((i+1)/num_z)-1)*y    mod(i+1,num_z)*z
                             end 
                    end
                end

                if num_box>num_y*num_z && num_box<=num_x*num_y*num_z   % 如果全部货物可以装进车厢里
                    t2=zeros(num_z,1);
                    for j=1:num_y-1
                        t3=ones(num_z,1)*j;
                        t2=[t2;t3];           % t2 对于y
                    end
                    while size(t2,1)<num_box
                        t2=[t2;t2];
                    end
                      t4=(0:num_z-1)'; 
                      while size(t4,1)<num_box
                          t4=[t4;t4];
                      end                      % t4 对于z  
                    for i=2:num_box  
                        PATH{1,1}(i,:)={[fix((i-1)/(num_y*num_z))*x,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};
                    end
                end
                surplus_box={};
                if num_box>num_x*num_y*num_z  % 如果全部货物太多,不能全部装进车厢里
                    surplus_box=box(:,1:num_box-num_x*num_y*num_z);
                    box(:,1:num_box-num_x*num_y*num_z)=[];
                    PATH{1,1}(1,:)={START,box{1, 1},box{2, 1}};
                    num_box=num_x*num_y*num_z;
                    t2=zeros(num_z,1);
                    for j=1:num_y-1
                        t3=ones(num_z,1)*j;
                        t2=[t2;t3];           % t2 对于y
                    end
                    while size(t2,1)<num_box
                        t2=[t2;t2];
                    end
                      t4=(0:num_z-1)'; 
                      while size(t4,1)<num_box
                          t4=[t4;t4];
                      end                      % t4 对于z  
                    for i=2:num_box  
                        PATH{1,1}(i,:)={[fix((i-1)/(num_y*num_z))*x,t2(i,1)*y,t4(i,1)*z],box{1, i},box{2, i}};
                    end
                end
                t=zeros(size(PATH,1),1);
                for num_PATH=1:size(PATH,1)
                    angles=[];
                    for j=1:size(PATH{num_PATH, 1},1)
                        s=eightangle(PATH{num_PATH, 1}{j, 1},PATH{num_PATH, 1}{j, 2});
                        angles(end+1:end+8,:)=s;
                    end
                end
                place=max(angles(:,1))*max(angles(:,2))*max(angles(:,3));  
                s1=0;  
                for i=1:size(PATH{num_PATH, 1},1)
                    s1=s1+PATH{num_PATH, 1}{i, 2}(1,1)*PATH{num_PATH, 1}{i, 2}(1,2)*PATH{num_PATH, 1}{i, 2}(1,3);
                end
                t(num_PATH,1)=s1/place;
        else
            PATH={};
            t=0;
            surplus_box=box;
end
end
end

% 摆放点选择
% 
% 功能:从所有的顶点中选择出合适的摆放点
% Sebastian. ZZU. 13/7/2021
% 目录
% 步骤1: 保留每个箱子的(x, 0, 0) (0, y, 0) (0, 0, z)三个顶点
% 步骤2: 找到所有的不重复点
% 步骤3: 求步骤一和步骤二的交点
% 步骤4: 求最前沿的这些点
% 步骤5: 加补(max(x), 0, 0)和(max(y), 0, 0)

function angle=choose_angle1(angles)
% 步骤1: 保留每个箱子的(x, 0, 0) (0, y, 0) (0, 0, z)三个顶点
    s=zeros(size(angles,1)/8*3,3);  
    for i=1:size(angles,1)/8
        y1(i,:)=angles((i-1)*8+1,:);
        s((i-1)*3+1,:)=angles((i-1)*8+2,:);
        s((i-1)*3+2,:)=angles((i-1)*8+3,:);
        s((i-1)*3+3,:)=angles((i-1)*8+5,:);
    end
    s2=s;

    % 补充一种特殊情况,增添到最后 
    y2=[];
    s=unique(s2,'rows');
    for i = 1:size(s,1)
       ind = ismember(s2,s(i,:),'rows');
       if size(find(ind==1),1)==3 && size(intersect(y1,s(i,:),'rows'),1)==0
           y2(end+1,:)=s(i,:);
       end
    end
% 步骤2: 找到所有的不重复点
    angles(1,:)=[];  % 除去[0,0,0]
    K=[];
    for i=1:size(angles,1)
        k=0;
        for j=1:size(angles,1)
            if (abs(angles(i,1)-angles(j,1))+abs(angles(i,2)-angles(j,2))+abs(angles(i,3)-angles(j,3)))==0
                k=k+1;
            end
        end
        if k==1
            K(end+1,1)=i;
        end
    end
    s1=angles(K,:);
% 步骤3: 求步骤一和步骤二的交点
    s=intersect(s1,s2,'rows');
% 步骤4: 求最前沿的这些点
    K=[];
    for i=1:size(s,1)
        k=0;
        for j=1:size(angles,1)
            if s(i,1)-angles(j,1)<0 && s(i,2)-angles(j,2)<0 && s(i,3)-angles(j,3)<0 && abs(s(i,1)-angles(j,1))+ abs(s(i,2)-angles(j,2))+ abs(s(i,3)-angles(j,3))~=0
                k=k+1;
            end
        end
        if k~=0
            K(end+1,1)=i;
        end
    end
    s(K,:)=[];
    angle=[s;y2];
% 步骤5: 加补(max(x), 0, 0)和(max(y), 0, 0)
    xx=[max(angles(:,1)),0,0;0,max(angles(:,2)),0];
    angle=[angle;xx];
    angle=unique(angle,'rows');
end

function eight_angle=eightangle(START,box_size)

% 函数功能:   已知:起点,箱子的长宽高,求:各顶点的坐标。
% 2021.2.2,郑州,Sebastian

eight_angle=zeros(8,3);

eight_angle(1,:)=[0,0,0]+START;
eight_angle(2,:)=[box_size(1,1),0,0]+START;
eight_angle(3,:)=[0,box_size(1,2),0]+START;
eight_angle(4,:)=[box_size(1,1),box_size(1,2),0]+START;
eight_angle(5,:)=[0,0,box_size(1,3)]+START;
eight_angle(6,:)=[box_size(1,1),0,box_size(1,3)]+START;
eight_angle(7,:)=[0,box_size(1,2),box_size(1,3)]+START;
eight_angle(8,:)=[box_size(1,1),box_size(1,2),box_size(1,3)]+START;


function [sorted_based_on_front]=Pareto_front_rank(x,n_obj)
% Input  x      需要pareto前沿排序的点集合,一行代表一个点,一列代表点的一个维
%        n_obj  表示点坐标的维度,也即是目标的个数
% Output sorted_based_on_front      返回x的第n_obj+1列就是pareto前沿排序的序号
% sorted_based_on_front 倒数第二列是pareto前沿排序,倒数第一列是原索引

N=size(x,1);%计算要排序点的个数
front = 1;
F(front).f = [];
individual = [];
for i = 1 : N
    % Number of individuals that dominate this individual
    individual(i).n = 0;
    % Individuals which this individual dominate
    individual(i).p = [];
    for j = 1 : N
        dom_less = 0;
        dom_equal = 0;
        dom_more = 0;
        for k = 1 : n_obj
            if (x(i,k) < x(j,k))
                dom_less = dom_less + 1;
            elseif (x(i,k) == x(j,k))
                dom_equal = dom_equal + 1;
            else
                dom_more = dom_more + 1;
            end
        end
        if dom_less == 0 && dom_equal ~= n_obj
            individual(i).n = individual(i).n + 1;
        elseif dom_more == 0 && dom_equal ~= n_obj
            individual(i).p = [individual(i).p j];
        end
    end   
    if individual(i).n == 0
        x(i,n_obj + 1) = 1;
        F(front).f = [F(front).f i];
    end
end
% Find the subsequent fronts
while ~isempty(F(front).f)
   Q = [];
   for i = 1 : length(F(front).f)
       if ~isempty(individual(F(front).f(i)).p)
        	for j = 1 : length(individual(F(front).f(i)).p)
            	individual(individual(F(front).f(i)).p(j)).n = ...
                	individual(individual(F(front).f(i)).p(j)).n - 1;
        	   	if individual(individual(F(front).f(i)).p(j)).n == 0
               		x(individual(F(front).f(i)).p(j),n_obj  + 1) = ...
                        front + 1;
                    Q = [Q individual(F(front).f(i)).p(j)];
                end
            end
       end
   end
   front =  front + 1;
   F(front).f = Q;
end

x(:,end+1)=(1:size(x,1))';
sorted_based_on_front=sortrows(x,4);
% [temp,index_of_fronts] = sort(x(:,n_obj  + 1));
% for i = 1 : length(index_of_fronts)
%     sorted_based_on_front(i,:) = x(index_of_fronts(i),:);
% end

end
function [a,b,t]=select1(PATH)
% 作为classification的子函数,挑选的准则只考虑s1/place
t=zeros(size(PATH,1),1);
for num_PATH=1:size(PATH,1)
    angles=[];
    for j=1:size(PATH{num_PATH, 1},1)
        s=eightangle(PATH{num_PATH, 1}{j, 1},PATH{num_PATH, 1}{j, 2});
        angles(end+1:end+8,:)=s;
    end
    place=max(angles(:,1))*max(angles(:,2))*max(angles(:,3));  % zui\\

    s1=0;  
    for i=1:size(PATH{num_PATH, 1},1)
        s1=s1+PATH{num_PATH, 1}{i, 2}(1,1)*PATH{num_PATH, 1}{i, 2}(1,2)*PATH{num_PATH, 1}{i, 2}(1,3);
    end
    t(num_PATH,1)=s1/place;
end
[a,b]=sortrows(t,-1);

function [a,b,t]=select2(PATH,truck)

V=truck(1,1)*truck(1,2)*truck(1,3);
W=truck(1,4);
for i=1:size(PATH,1)
    v=0;
    w=0;
    for j=1:size(PATH{i,1},1)
        v=v+PATH{i,1}{j,2}(1,1)*PATH{i,1}{j,2}(1,2)*PATH{i,1}{j,2}(1,3);
        w=w+PATH{i,1}{j,2}(1,4);
    end
    t(i,1)=max(v/V,w/W);
end
[a,b]=sortrows(t,-1);

% s=[];
% if a(1,1)==a(2,1)
%     path=PATH(b(find(a==a(1,1)),:),:);
%     for i=1:size(path,1)
%         v=0;
%         w=0;
%         for j=1:round(size(path{i,1},1)/2)
%             v=v+path{i,1}{j,2}(1,1)*path{i,1}{j,2}(1,2)*path{i,1}{j,2}(1,3);
%             w=w+path{i,1}{j,2}(1,3);
%         end
%         s(i,1)=max(v/V,w/W);
%     end
%     [a1,b1]=sortrows(s,-1);
%     s1=b(find(a==a(1,1)),:);
%     a=a1;
%     b=s1(b1,:);
%     t=s;
% end


function []=show(PATH,truck)

%% 自动全屏
h = figure(1);				% 创建图形窗口
% warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');	% 关闭警告
jFrame = get(h,'JavaFrame');
pause(0.01);					
set(jFrame,'Maximized',1);	
pause(0.01);					
% warning('on','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');	

%% 显示卡车
l=truck(1,1);
w=truck(1,2);
h=truck(1,3);
X(1,:)=[0,0,0];
X(2,:)=[0,0,h];
X(3,:)=[0,w,0];
X(4,:)=[0,w,h];
X(5,:)=[l,0,0];
X(6,:)=[l,0,h];
X(7,:)=[l,w,0];
X(8,:)=[l,w,h];
d=[1 2 4 3 1 5 6 8 7 5 6 2 4 8 7 3];
plot3(X(d,1),X(d,2),X(d,3));
hold on;
xlabel('x');ylabel('y');zlabel('z');

%% 显示货物
for i=1:size(PATH,1)
     showbox(PATH{i, 1},PATH{i, 2})
end

```c
function showbox(SATRT,box_size)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%函数名称:显示箱子 showbox
%%入口参数:起点 SATRT([x,y,z])  箱子尺寸 box_size([length,width,height]%%出口参数:可视化图像
%%函数功能说明:
    %%可视化, visualization.
%%by SebastianLi, At ZZ, 24th January, 2021
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

hold on

l=box_size(1,1);
w=box_size(1,2);
h=box_size(1,3);


X(1,:)=[0,0,0];
X(2,:)=[0,0,h];
X(3,:)=[0,w,0];
X(4,:)=[0,w,h];
X(5,:)=[l,0,0];
X(6,:)=[l,0,h];
X(7,:)=[l,w,0];
X(8,:)=[l,w,h];

X(:,1)=X(:,1)+SATRT(1,1);
X(:,2)=X(:,2)+SATRT(1,2);
X(:,3)=X(:,3)+SATRT(1,3);

d=[1 2 4 3 1 5 6 8 7 5 6 2 4 8 7 3];

plot3(X(d,1),X(d,2),X(d,3));
评论 64
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值