【python】欧姆龙智能物料排布之二维装箱算法实现

本算法衍生自欧姆龙杯大赛智能物料排布赛项

简介

最终能实现给出一个在一个指定的平面区域内,不同形状、大小、数量的平面物料的最节省空间的排布方式,且具有实时显示功能

问题描述:

  • 排布区域
    平面区域为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%线以上的所有图形并重新排列

重要功能的实现

  1. 点在图形内检测:
    如何判断一个点是否在图形内(三角形、矩形甚至任意多边形),我用的方法是判断点与角的关系,即判断点是否在该角内,再重复所有角达到判断是否在图形内的目的。

    其中两组向量叉乘:
    O P ⃗ × O A ⃗ 和 O P ⃗ × O B ⃗ \vec {OP} \times\vec {OA}和\vec {OP} \times \vec {OB} OP ×OA OP ×OB
    反映了向量OP与向量OAOB的位置关系:如果点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 # 在里面
  1. 图形的重叠检测:
    在有了点是否在图形内的判断的基础上,就容易实现图形的重叠检测:
    (这套判断适用于任意多边形之间的判断,对于三角形和矩形的情景应该有简化的方案)
现有图形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    # 检查中没有发现重叠的情况
  1. 待补充
另:

python有现成的图形检测的包shapely,嫌自己开发麻烦的话直接pip install即可,但是在效率上貌似不如自己写逻辑来的好

欢迎交流
  • 5
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值