本算法衍生自欧姆龙杯大赛智能物料排布赛项
简介
最终能实现给出一个在一个指定的平面区域内,不同形状、大小、数量的平面物料的最节省空间的排布方式,且具有实时显示
功能
问题描述:
- 排布区域
平面区域为40×50(X方向×Y方向)的矩形。 - 物料描述
1、包含矩形和三角形两种形状。
2、矩形和三角形的总数为25个,每种形状至少有一个。
3、矩形和三角形的面积、数量随机。 - 排布规则
1、所有物料都必须使用
2、所有物料之间不能叠放
3、物料可以进行任意角度的旋转
4、物料不可进行镜像翻转
5、所有物料的排布位置不能超过排布区域
程序界面:

(注:此图还没排完,处在暂停当中)

- 显示框内红色线为此桢的排列的高度最高峰;灰色线为100%利用率高度线
- 主线程
mywin
使用pyqt5实现界面的显示和刷新。在程序接收到物块坐标数据,并点击开始之后,线程Calculator
开始对坐标数据进行计算,同时,mywin
将以100hz的频率刷新显示。
源码链接:
https://github.com/Ralphson/2D-binning_omron
突然有了契机,来回忆一下程序的实现
运算框架

- 其中排矩形是一个类贪心算法,保障底层排序时的空隙尽量小
- 排三角形的逻辑是在矩形表面处的点上做遍历,在一个点上取重心低的姿态存储,一个三角形取所有位置低的姿态为最终姿态
- 回溯则是在所有图形都排完后,对结果按照从高到低的顺序依次重新寻找位置:如示例界面中所示,红色的为当前最高峰、灰色为100%线,故数据集的回溯为从最高峰开始依次剔除100%线以上的所有图形并重新排列
重要功能的实现
- 点在图形内检测:
如何判断一个点是否在图形内(三角形、矩形甚至任意多边形),我用的方法是判断点与角的关系,即判断点是否在该角内,再重复所有角达到判断是否在图形内的目的。
其中两组向量叉乘:
O P ⃗ × O A ⃗ 和 O P ⃗ × O B ⃗ \vec {OP} \times\vec {OA}和\vec {OP} \times \vec {OB} OP×OA和OP×OB
反映了向量OP
与向量OA
、OB
的位置关系:如果点P在角AOB内,则结果异号;若在边上,则结果包含0;若在外面,则结果同号。判断一个角如上,判断所有角同理。
def judgePointInner(x, y, location):
'''
判断点在多边形内
进行区域规整的快速判断
:param x: 判断点x
:param y: 判断点y
:param location:待检测区域。必须是按照边的顺序,连着给的点; 图形坐标:[[], [], [], []]
:return:在里面为-1,在外面为1,在边上0
'''
# 判断是否在包络外
x_set = [i[0] for i in location]
y_set = [i[1] for i in location]
x_min = min(x_set)
x_max = max(x_set)
y_min = min(y_set)
y_max = max(y_set)
if x < x_min or x > x_max:
return 1 # 在外面
if y < y_min or y > y_max:
return 1 # 在外面
flag = -1 # -1在里面;0在边上;1在外面
for i in range(len(location)):
point = location[i]
if i == 0:
point_next = location[i + 1]
point_bef = location[-1]
elif i == len(location) - 1:
point_next = location[0]
point_bef = location[i - 1]
else:
point_next = location[i + 1]
point_bef = location[i - 1]
v0 = [x - point[0], y - point[1]]
v1 = [point_next[0] - point[0], point_next[1] - point[1]]
v2 = [point_bef[0] - point[0], point_bef[1] - point[1]]
# 叉乘之积
answer = (v0[0]*v1[1] - v1[0]*v0[1]) * (v0[0]*v2[1] - v2[0]*v0[1])
if answer > 0: # 在外面
flag = 1
return flag
if answer == 0: # 在边上
flag = 0
return flag # 在里面
- 图形的重叠检测:
在有了点是否在图形内的判断的基础上,就容易实现图形的重叠检测:
(这套判断适用于任意多边形之间的判断,对于三角形和矩形的情景应该有简化的方案)
现有图形A、B
判断:
1、A是否在B的矩形包络内;
2、A的重心是否在B内,B的重心是否在A内;
3、A的所有点是否在B内;
4、A的所有边是否与B相交;
def judgeCoin(self, Xc, Yc, location):
'''
待排图形与已排图形的重叠检测
根据已排图形来
location:待检查图形, [[], [], []]
Xc:形心x
Yc:形心y
:return: 重叠T/不重叠F
'''
#判断是否在包络外
x_list = [i[0] for i in location]
y_list = [i[1] for i in location]
x_min = min(x_list) # 带派图形的x最低值
x_max = max(x_list)
y_min = min(y_list)
y_max = max(y_list)
# 遍历已经排放图形的顶点信息
for Point in self.settledPoints: # [[Yc, y_max, Xc, gender, location, num, s], ..]
settledGraph = Point[4] # [[], [], [], []] # 以排图形
x_list_set = [i[0] for i in settledGraph]
y_list_set = [i[1] for i in settledGraph]
x_min_set = min(x_list_set) # 已派图形的x最低值
x_max_set = max(x_list_set)
y_min_set = min(y_list_set)
y_max_set = max(y_list_set)
# 离得太远的直接跳过
if x_max<x_min_set or x_min>x_max_set or y_max<y_min_set or y_min>y_max_set:
continue
# 检查重心
exist0 = self.judgePointInner(Xc, Yc, settledGraph)
if exist0 == -1 or exist0 == 0: # 形心不能在里面或边上
return True # 形心在里面
# 检查各个点
for i in range(len(location)):
x = location[i][0]
y = location[i][1]
exist1 = self.judgePointInner(x, y, settledGraph) # 图形的顶点
if exist1 == -1: # 顶点可以在边上但不能在里面
return True #形心在里面
# 检查边界线香蕉
line_already = [] # 已排图形的线
if len(settledGraph) == 3: # 三角形
l = [[settledGraph[0], settledGraph[1]], # 边线1
[settledGraph[1], settledGraph[2]], # 边线2
[settledGraph[2], settledGraph[0]], # 边线3
# [settledGraph[0], [(settledGraph[1][0] + settledGraph[2][0])/2, (settledGraph[1][1] + settledGraph[2][1])/2]] , # 中线1
# [settledGraph[1], [(settledGraph[0][0] + settledGraph[2][0])/2, (settledGraph[0][1] + settledGraph[2][1])/2]] # 中线2
]
line_already.extend(l)
else: # 矩形
l = [[settledGraph[0], settledGraph[1]], # 边线1
[settledGraph[1], settledGraph[2]], # 边线2
[settledGraph[2], settledGraph[3]], # 边线3
[settledGraph[3], settledGraph[0]], # 边线4
# [settledGraph[0], settledGraph[2]], # 中线1
# [settledGraph[1], settledGraph[3]] # 中线2
]
line_already.extend(l)
line_noready = [] # 未排图形的线
if len(location) == 3:
l = [[location[0], location[1]], # 边线1
[location[1], location[2]], # 边线2
[location[2], location[0]], # 边线3
[location[0], [(location[1][0] + location[2][0]) / 2, (location[1][1] + location[2][1]) / 2]], # 中线1
[location[1], [(location[0][0] + location[2][0]) / 2, (location[0][1] + location[2][1]) / 2]] # 中线2
]
line_noready.extend(l)
else: # 矩形
l = [[location[0], location[1]], # 边线1
[location[1], location[2]], # 边线2
[location[2], location[3]], # 边线3
[location[3], location[0]], # 边线4
[location[0], location[2]], # 中线1
[location[1], location[3]] # 中线2
]
line_noready.extend(l)
for line0 in line_already:
for line1 in line_noready:
exist = self.judgeLineCross(line1, line0) # 检查线段
if exist:
return True # 出现香蕉
return False # 检查中没有发现重叠的情况
- 待补充
另:
python有现成的图形检测的包shapely
,嫌自己开发麻烦的话直接pip install
即可,但是在效率上貌似不如自己写逻辑来的好