Trapezoidal Decomposition梯形分解算法(TCD)

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
TODO:写完再整理

文章目录


前言

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!

本文先对**Trapezoidal Decomposition梯形分解算法(TCD)**做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章


提示:以下是本篇文章正文内容

Trapezoidal Decomposition梯形分解算法(TCD)

原理

Trapezoidal Decomposition是最简单的精确细胞分解方法。该方法先使机器人沿着空间的边缘行走一圈,构建整个区域地图,然后用一条竖直的切割线从左至右扫描过整个区域,当切割线与多边形障碍物的顶点相遇时,就会分解出一个子区域,这样整个自由空间就被分解为诸多子区域,每个子区域都是梯形。梯形分解法的时间复杂度通常为O(n log n)。机器人在每个子区域中进行往复运动对子区域进行覆盖。

(1)第一种原理

(2)第二种原理

梯形分解方法假设一条垂直线段(称为slice)从左到右扫过由多边形障碍物填充的有界环境。单元格cell是通过一系列打开和关闭操作形成的

事件类型

当slice遇到一个事件时,有三种类型的事件:IN、OUT和MIDDLE。

1、在IN事件中,当前单元格关闭(从而完成其构造),两个新单元格打开(从而启动其构造)(图2)。

2、OUT事件正好相反:两个单元格关闭,一个新单元格打开(图3)。

IN事件可以看作是一个单元格分解成两个单元格,而OUT事件是两个单元格合并成一个单元格。

3、在MIDDLE事件中,当前单元格关闭,一个新单元格形成。这些操作的结果是自由空间被分解成梯形单元格。

(3)第三种原理



通过cell的面积最大化来确定cell的真正边界

代码实现

cpp 复制代码
# Find a path avoiding obstacles using Vertical Cell Decomposition
# Author -- Shikhar Dev Gupta

import sys
from helpers.graph import *
from helpers.geometry import *;
import matplotlib.pyplot as plt

# Check for empty lines
file_handler = open("input_file","r");
raw_data = file_handler.read();
raw_data = raw_data.split("\n");

if(len(raw_data) <2):
	print("Incorrect format of the input file");
	exit;

def parse_input_line(line):
	temp2 = [];
	line = [i.strip() for i in line.split(",")];
	vertex = [];
	for index,i in enumerate(line):
		if(i[0] == "("):
			i = i[1:];
		if(i[len(i)-1] == ")"):
			i= i[:-1];
		vertex.append(int(i));
		if(index%2 != 0):
			temp2.append(vertex);
			vertex = [];
	return temp2;	

# Draw the obstacles and point the source and the destination----------------------------------------------
def draw_problem():
	bnd_x = [i.x for i in boundary];
	bnd_x.append(boundary[0].x);
	bnd_y = [i.y for i in boundary];
	bnd_y.append(boundary[0].y);
	poly_x = [];
	poly_y = []

	# Draw the boundary
	plt.plot(bnd_x, bnd_y);
	
	for index, i in enumerate(obstacles):
		poly_x.append([p[0] for p in i]);
		poly_y.append([p[1] for p in i]);
	
		plt.fill( poly_x[index], poly_y[index], color="#512DA8");
	
	plt.plot(source.x, source.y, marker="o");
	plt.plot(dest.x, dest.y, marker="o");
	plt.annotate('Source', xy=(source.x, source.y), xytext=(source.x+5, source.y-6) );
	plt.annotate('Destination', xy=(dest.x, dest.y), xytext=(dest.x-4, dest.y-10) );


# Extract vertices----------------------------------------------
temp = parse_input_line(raw_data[0]);
boundary = [point(i[0], i[1]) for i in temp];

# Extract source and dest
temp = parse_input_line(raw_data[len(raw_data)-1]);
source = point(temp[0][0], temp[0][1]);
dest = point(temp[1][0], temp[1][1]);

# Extract obstacles
obstacles = [];
for i in raw_data[1:len(raw_data)-1]:
	obstacles.append(parse_input_line(i) );

#sort by x-values
sorted_vertices = [];
for index,i in enumerate(obstacles):
	for j in i:
		j.append(index);
		sorted_vertices.append(j);
sorted_vertices.sort(key=lambda x: x[0]);

# Draw the problem
draw_problem();

new_sorted_vertices = [];

for i in sorted_vertices:
	temp = point(i[0], i[1], i[2]);
	new_sorted_vertices.append(temp);

new_obstacles = [];
for index, i in enumerate(obstacles):
	temp_obs = [];
	for j in i:
		temp = point(j[0], j[1], index);
		temp_obs.append(temp);
	new_obstacles.append(temp_obs);	


#-----------------------------------------------------------
# Find vertical lines
open_line_segments = [];

y_limit_lower = boundary[0].y;
y_limit_upper = boundary[2].y;

for pt in new_sorted_vertices:
	curr_line_segment = [ point(pt.x, y_limit_lower), point(pt.x, y_limit_upper) ]; 
	lower_obs_pt = curr_line_segment[0];
	upper_obs_pt = curr_line_segment[1];
	upper_gone = False;
	lower_gone = False;
	break_now = False;

	# Find intersection points with the vertical proposed lines. the intersection function returns false if segments are same, so no need to worry about same segment checking
	for index,obs in enumerate(new_obstacles):
		# Add the first point again for the last line segment of a polygon.
		
		obs.append( obs[0] );
		for vertex_index in range(len(obs)-1 ):
			res = segment_intersection( curr_line_segment[0], curr_line_segment[1], obs[vertex_index],  obs[vertex_index+1]);
			if (res!=-1):
				if ( index == pt.obstacle ):
					if pt.equals( res ) == False:
						if ( res.y > pt.y ):
							upper_gone = True;
						elif ( res.y < pt.y ):
							lower_gone = True;	
				else:	
					if pt.equals( res ) == False:
						if ( upper_gone is False ):
							if ( (res.y > pt.y) and res.y < (upper_obs_pt.y) ):
								upper_obs_pt = res;
						if ( lower_gone is False ):
							if ( (res.y < pt.y) and (res.y > lower_obs_pt.y) ):
								lower_obs_pt = res;
			if( upper_gone is True and lower_gone is True ):
				break_now = True;

		#No need to check for current point anymore...completely blocked
		if(break_now is True):
			break;		

	# Draw the vertical cell lines
	if(lower_gone is False):
		plt.plot( [lower_obs_pt.x, pt.x],  [lower_obs_pt.y, pt.y] );
		
	if(upper_gone is False):
		plt.plot( [pt.x, upper_obs_pt.x],  [pt.y, upper_obs_pt.y] );

	# Add to the global segment list
	if (lower_gone and upper_gone):
		open_line_segments.append([None, None]);
	elif (lower_gone):
		open_line_segments.append([None, upper_obs_pt]);
	elif (upper_gone):
		open_line_segments.append([lower_obs_pt, None]);
	else:
		open_line_segments.append([lower_obs_pt, upper_obs_pt]);


#------------------------------------------------------
# Find Polygon cells naiively. Will improve next. 
cells = [];

for index1 in range(len(open_line_segments) ):
	curr_segment = open_line_segments[index1];
	curr_vertex = new_sorted_vertices[index1];
	break_now = False;
	done = [False, False, True];
	if( curr_segment[0] is None ):
		done[0] = True; 
	if( curr_segment[1] is None ):
		done[1] = True;	
	if( curr_segment[1] is None and open_line_segments[index1][0] is None):
		done[2] = False;	

	for index2 in range(index1+1,  len(open_line_segments) ):
		next_segment = open_line_segments[index2];
		next_vertex = new_sorted_vertices[index2];			
		
		double_index1 = -2;
		double_index2 = -2;
		lines_to_check = [];
		trapezoids = [];
		double_check = False;

		if ( next_segment[0] is not None and next_segment[1] is not None ):
			double_check = True;

		if( done[0] is False ):
			if( double_check ):
				double_index1 = len(lines_to_check);
				lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[0], next_vertex]), 0]);
				lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[1], next_vertex]), 0]);
				trapezoids.append([ curr_segment[0], next_segment[0], next_vertex, curr_vertex ]);
				trapezoids.append([ curr_segment[0], next_vertex, next_segment[1], curr_vertex ]);
			elif ( next_segment[0] is not None ):
				lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[0], next_vertex]), 0]);
				trapezoids.append([ curr_segment[0], next_segment[0], next_vertex, curr_vertex ]);
			elif( next_segment[1] is not None ):
				lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), centroid([next_segment[1], next_vertex]), 0]);
				trapezoids.append([ curr_segment[0], next_vertex, next_segment[1], curr_vertex ]);
			else:
				lines_to_check.append( [centroid([curr_segment[0], curr_vertex]), next_vertex, 0]);
				trapezoids.append([ curr_segment[0], next_vertex, curr_vertex ]);

		if( done[1] is False ):
			if( double_check ):
				double_index2 = len(lines_to_check);
				lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[0], next_vertex]), 1]);
				lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[1], next_vertex]), 1]);
				trapezoids.append([ curr_vertex, next_segment[0], next_vertex , point(curr_segment[1].x, curr_segment[1].y,curr_segment[1].obstacle, 34)]);
				trapezoids.append([ curr_vertex, next_vertex, next_segment[1], curr_segment[1] ]);
			elif ( next_segment[1] is not None ):
				lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[1], next_vertex]), 1]);
				trapezoids.append([ curr_vertex, next_vertex, next_segment[1], curr_segment[1] ]);
			elif( next_segment[0] is not None ):
				lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), centroid([next_segment[0], next_vertex]), 1]);
				trapezoids.append([ curr_vertex, next_segment[0], next_vertex , curr_segment[1] ]);
			else:
				lines_to_check.append( [centroid([curr_segment[1], curr_vertex]), next_vertex, 1]);
				trapezoids.append([ curr_vertex, next_vertex, curr_segment[1] ]);
		
		if( done[2] is False ):
			if(double_check):
				double_index = len(lines_to_check);
				lines_to_check.append( [curr_vertex, centroid([next_segment[0], next_vertex]), 2]);
				trapezoids.append([ curr_vertex,next_segment[0], next_vertex ]);
				lines_to_check.append( [curr_vertex, centroid([next_segment[1], next_vertex]), 2]);
				trapezoids.append([ curr_vertex, next_vertex, next_segment[1] ]);
			elif ( next_segment[0] is not None ):
				lines_to_check.append( [curr_vertex, centroid([next_segment[0], next_vertex]), 2]);
				trapezoids.append([ curr_vertex,next_segment[0], next_vertex ]);
			elif( next_segment[1] is not None ):
				lines_to_check.append( [curr_vertex, centroid([next_segment[1], next_vertex]), 2]);
				trapezoids.append([ curr_vertex, next_vertex, next_segment[1] ]);
			# Will this ever occur though??
			else:
				lines_to_check.append( [curr_vertex, next_vertex, 2]);
				trapezoids.append([curr_vertex, next_vertex]);

		temp_to_remove = [];
		for index5,q in enumerate(lines_to_check): 
			ok = [True, True, True];
			for index3,obs in enumerate(new_obstacles):
				# Add the last line to make closed polygon
				obs.append( obs[0] );
				for index4 in range(len(obs)-1):
					if (segment_intersection( q[0], q[1],  obs[index4],  obs[index4+1]) != -1):
						ok[q[2]] = False;
						if(index5 not in temp_to_remove):
							temp_to_remove.append(index5);
						

			if (  ok[q[2]] is True ):
				done[q[2]] = True;

		for i in range(len(lines_to_check)):
			if i not in temp_to_remove:
				cells.append(trapezoids[i]);
		
		if( done[0] == True and done[1] == True and done[2] == True ):
			break;

to_draw =[];
for i in cells:
	i.append(i[0]);
	to_draw.append(i);


#-------------------------------------------------------
# Merge overlapping Polygons
quad_cells = [i for i in cells if len(i)>3];
tri_cells = [i for i in cells if len(i)==3];
others = [i for i in cells if len(i)<3];
quads_to_remove = [];
quads_to_add = [];

quads_to_remove = [];
quads_to_add = [];
for index_cell in range(len(quad_cells)):
	for index_cell2,cell in enumerate(quad_cells):
		if(index_cell != index_cell2):
			if(quad_cells[index_cell][0].x == cell[0].x and quad_cells[index_cell][1].x == cell[1].x):
					temp1 = list(quad_cells[index_cell]);
					temp1.append(temp1[0]);
					temp2 = list(cell);
					temp2.append(temp2[0]);
					area1 = polygon_area(temp1,4); area2 = polygon_area(temp2,4);
					new_quad=[];
					
					new_quad.append( point(temp1[0].x, min(temp1[0].y, temp2[0].y)) );
					new_quad.append( point(temp1[1].x, min(temp1[1].y, temp2[1].y)) );
					new_quad.append( point(temp1[1].x, max(temp1[2].y, temp2[2].y)) );
					new_quad.append( point(temp1[0].x, max(temp1[3].y, temp2[3].y)) );
					new_quad.append( point(temp1[0].x, min(temp1[0].y, temp2[0].y)) );
					area3 = polygon_area(new_quad, 4);
					if( area1 + area2 >= area3):
						#merge
						quads_to_remove.append(index_cell);
						quads_to_remove.append(index_cell2);
						
						quads_to_add.append(new_quad);

quads_to_remove = list(set(quads_to_remove));
for index in sorted(quads_to_remove, reverse=True):
    del quad_cells[index];

for i in quads_to_add:
	quad_cells.append(i);

# Remove duplicates
to_remove = [];
for index1 in range(len(quad_cells)):
	for index2 in range(index1+1, len(quad_cells)):
		duplicate = True;
		for k,m in zip(quad_cells[index1], quad_cells[index2]):
			if k.equals(m) is False:
				duplicate = False;
				break;
		if(duplicate is True):
			if index2 not in to_remove:
				to_remove.append(index2);		

for index in sorted(to_remove, reverse=True):
    del quad_cells[index];

# One more pass to remove extra quads generated because of cross - segments
quads_to_remove = [];
for index1 in range(len(quad_cells)):
	for index2 in range(len(quad_cells)):
		if(index1 != index2 and quad_cells[index1][0].x == quad_cells[index2][0].x and quad_cells[index1][1].x == quad_cells[index2][1].x):
			
			if( (quad_cells[index1][0].y<= quad_cells[index2][0].y) and  (quad_cells[index1][1].y<= quad_cells[index2][1].y)
				and (quad_cells[index1][2].y>= quad_cells[index2][2].y) and (quad_cells[index1][3].y >= quad_cells[index2][3].y)):			
				quads_to_remove.append(index2);


quads_to_remove = list(set(quads_to_remove) );
for index in sorted(quads_to_remove, reverse=True):
    del quad_cells[index];


#------------------------------------------------------
# Add boundary lines
if( boundary[0].x != new_sorted_vertices[0].x):
	quad_cells.append([boundary[0], point(new_sorted_vertices[0].x, y_limit_lower), point(new_sorted_vertices[0].x, y_limit_upper), boundary[3]]);
if( boundary[1].x != new_sorted_vertices[len(new_sorted_vertices)-1].x):
	quad_cells.append([point(new_sorted_vertices[len(new_sorted_vertices)-1].x ,y_limit_lower), boundary[1], boundary[2], point(new_sorted_vertices[len(new_sorted_vertices)-1].x, y_limit_upper) ]);

#-------------------------------------------------------
# Plot final cells
to_draw = quad_cells+tri_cells+others;
for i in to_draw:
	x = [j.x for j in i];
	y = [j.y for j in i];
	plt.plot(x, y);

#----------------------------------------------------------------------
# Get the graph
graph_vertices = [];
graph_edges = [];

for index1 in range(len(quad_cells)):
	same_boundary = [];
	for index2 in range(len(quad_cells)):
		if(index1 != index2):
			if( (quad_cells[index1][1].x == quad_cells[index2][0].x ) and ((quad_cells[index1][2].y in [quad_cells[index2][0].y, quad_cells[index2][3].y]) or (quad_cells[index1][1].y in [quad_cells[index2][0].y, quad_cells[index2][3].y]) ) ):
				same_boundary.append(index2);

	temp = quad_cells[index1][0:4];
	centroid_vertex = centroid(temp);
	place = centroid_vertex.find_point(graph_vertices)
	if( place == -1):
		graph_vertices.append(centroid_vertex);

	if(len(same_boundary)==1):
		temp_edge_middle = centroid([quad_cells[index1][1], quad_cells[index1][2]]);
		graph_vertices.append(temp_edge_middle);
		n = len(graph_vertices)-1;
		if(place != -1):
			graph_edges.append([place, n]);
		else:
			graph_edges.append([n-1, n]);
		temp = quad_cells[same_boundary[0]][0:4];
		curr_centroid_vertex = centroid(temp);
		place2 = curr_centroid_vertex.find_point(graph_vertices);
		if( place2 == -1 ):
			graph_vertices.append(curr_centroid_vertex);
			graph_edges.append([n, n+1]);
		else:
			graph_edges.append([n, place2]);

	elif(len(same_boundary)>1):
		n = len(graph_vertices)-1;
		if(place != -1):
			use = place;
		else:
			use = n;	
		for index, i in enumerate(same_boundary):
			temp = quad_cells[i][0:4];
			curr_centroid_vertex = centroid(temp);
			temp_edge_middle = centroid([quad_cells[i][0], quad_cells[i][3]]);
			graph_vertices.append(temp_edge_middle);
			pl1 =len(graph_vertices)-1;
			hmmm= curr_centroid_vertex.find_point(graph_vertices);
			if (hmmm == -1):
				graph_vertices.append(curr_centroid_vertex);
				pl2 =len(graph_vertices)-1;
			else:
				pl2 = hmmm;	
			graph_edges.append([use, pl1]);
			graph_edges.append([pl1, pl2]);		


# Add source and dest to graph
# Find the smallest distance vertex on graph and see if its clear to traverse
# Source------------------------------
min_ind = -1; min = 9999999;
for index, i in enumerate(graph_vertices):
	if( check_obstruction(new_obstacles, [source, i]) is True ):
		dist = find_dist(i, source);
		if( dist < min):
			min = dist;
			min_ind = index;

graph_vertices.append(source);
m = len(graph_vertices)-1;
graph_edges.append([min_ind, m]);	

# Destination------------------------------------
min_ind = -1; min = 9999999;
for index, i in enumerate(graph_vertices):
	if( check_obstruction(new_obstacles, [dest, i]) is True ):
		dist = find_dist(i, dest);
		if( dist < min):
			min = dist;
			min_ind = index;

graph_vertices.append(dest);
m = len(graph_vertices)-1;
graph_edges.append([min_ind, m]);

# Convert graph in adjacency list format
graph = [];
for j in range(len(graph_vertices)):
	graph.append([]);
	for i in graph_edges:
		if(i[0]==j):
			graph[j].append(i[1]);
		elif(i[1]==j):
			graph[j].append(i[0]);	

path = bfs(graph, len(graph_vertices)-2, len(graph_vertices)-1);

if(path is None):
	print "No path found. Sorry";
	sys.exit();
else:
	print "Path found."	;

# Draw everything--------------
for index,i in enumerate(graph_vertices):
	plt.annotate(str(index), xy=(i.x, i.y), xytext=(i.x+2, i.y-2) );
	# plt.plot(i.x,i.y, marker="x");

for i in graph_edges:
	temp_x = [graph_vertices[i[0]].x, graph_vertices[i[1]].x];
	temp_y = [graph_vertices[i[0]].y, graph_vertices[i[1]].y];
	plt.plot(temp_x,temp_y);


# draw path
temp_x = [graph_vertices[i].x for i in path];
temp_y = [graph_vertices[i].y for i in path];
plt.plot(temp_x,temp_y, color="#0F0F0F", linewidth=2);	


#----------------------------------------------------
# output into a file
file_output = open("vertical_cell_output", "w" );
str_to_write = "";
for index in range(len(graph_vertices)):

	str_to_write = str_to_write + ", "+str(index)+":"+"("+ str(int(graph_vertices[index].x) )+  ", "+ str(int(graph_vertices[index].y) ) + ")";

str_to_write = str_to_write[1:];

total_write = str_to_write+"\n";
str_to_write="";
for i in graph:
	if (i == []):
		continue;
	str_to_write = str_to_write + ",(";
	for j in i:
		str_to_write = str_to_write + str(j) + ",";
	str_to_write = str_to_write[:-1];
	str_to_write = str_to_write + ")";

str_to_write = str_to_write[1:];

total_write = total_write+ str_to_write + "\n";

str_to_write = "";
str_to_write =','.join(str(x) for x in path);

total_write = total_write + str_to_write;

file_output.write(total_write);
print "Output written to file.. Drawing the result";

plt.show();

优缺点

产生大量的子区域,造成了大量的路径冗余


相关推荐
2401_8572979119 分钟前
2025校招内推-招联金融
java·前端·算法·金融·求职招聘
小冉在学习39 分钟前
leetcode刷题day32|动态规划Part01(509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯)
算法·leetcode·动态规划
雷达学弱狗40 分钟前
孙论文——定标
算法
Mr_Xuhhh40 分钟前
vector
c语言·开发语言·数据结构·算法·链表·visualstudio
枫の准大一40 分钟前
空间复杂度&动态顺序表
数据结构·算法
black_blank1 小时前
众数问题,
算法
saberyydsicloud2 小时前
C++(9.25)
开发语言·c++·算法
sewinger2 小时前
关于贪心算法
算法·贪心算法
暴怒香菜统治世界2 小时前
c语言--结构体
c语言·开发语言·数据结构·笔记·算法
予早2 小时前
LeetCode 427. 建立四叉树
算法·leetcode