3D装箱:给定装载的四个约束:长,宽,高,限重,若干待装载货箱的信息:长,宽,高,重量,求满足约束的情况下,最佳的装载方式(或是达到最高载重,或是达到最大装载体积),以货物的装载顺序和在卡车中的位置表示。(注:货物放置在另外一个货物的上面时,需要有至少80%的支撑面积)
求解思路:该算法的思路很简单,可以理解为先穷举,像一个树生出很多分叉,每一个装箱方式是从树根到枝头的路径,每一个结点是一个要装的箱子。再剪枝,待装箱方式的数量达到N时,按照一定的筛选规则,留下M个装箱方式(M
代码和简单的装箱数据放在如下网盘中:
链接:https://pan.baidu.com/s/1Jk7C8wcd1aShf17bE5jQsw
提取码:sukw
注:matlab版本需要大于等于2018,否则可能会报错
另再附代码,方便讨论:
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));