为了实现通过用户给的3D模型实现避障功能的路径规划,我们采取首先对模型进行切片,然后再通过修改路径长度和两点之间路径获取的函数完成
对于meshcut
库的使用
可以实现3D建模处理的库有很多,但如果仅仅是想要一个切面就将vtk
搬出来未免就有一点大材小用了,通过寻找,我找到了一个meshcut
库,其可以将stl
文件和定义的平面求得交线.
meshcut库的开发主页
下面是使用meshcut
库实现对一个3D模型切割的效果图
而其所使用的源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import stl import matplotlib.pyplot as plt import numpy as np import meshcut
def create_mesh(m:stl.mesh.Mesh): verts = m.vectors.reshape(-1, 3) faces = np.arange(len(verts)).reshape(-1, 3)
verts, faces = meshcut.merge_close_vertices(verts, faces) return meshcut.TriangleMesh(verts,faces)
if __name__=="__main__": m=stl.mesh.Mesh.from_file("up.stl") mesh=create_mesh(m)
i=1 z=1 while(i!=0): plane_origin=np.array([0,0,z]) plane_n=np.array([0,0,1]) plane=meshcut.Plane(plane_origin,plane_n)
p=meshcut.cross_section_mesh(mesh,plane) x=p[0][:,0] x=np.hstack([x,np.array(x[0])]) y=p[0][:,1] y=np.hstack([y,np.array(y[0])]) plt.plot(x,y) plt.savefig("{}.png".format(z)) z=z+1
|
判断一条线是否与多边形相交
算法
接下来我们来讨论一下关于碰撞检测这一部分的内容.
多边形是由有限个点首尾相连而成的,那么假如一条线段与多边形相交,那么其必然与多边形的至少一条边相交.因此问题就转化为判断一条线段是否与一个线段集中线段相交.
而对于判断两线段是否相交的方法,记第一条线段两点为$A_1,A_2$,第二条线段两点为$B_1,B_2$,如果从线段一指向线段二某点(通常使用某个端点)的两个矢量与线段二的叉乘异号,表明$A_1,A_2$分别位于第二条线段两侧.同理,可以判断$B_1,B_2$是否位于第一条线段两侧.如果两个条件同时满足,那么就可以说明线段相交.
这一手段参考资料
特殊情况,判断点是否在多边形内,可以用相似的方法,通过过该点做一条射线,然后统计相交点数,如果相交点数为偶数,则点在多边形内,否则在多边形外
实现
在实现这个问题的同时,需要考虑到Python在处理大量的for
循环时效率非常低下,因此需要对操作进行向量化处理.
假定$A_x,A_y,B_x,B_y$为$n,n,m,m$维列向量,定义$C_{ij}$的值等于$A_{i}\times B_{j}$为行列式的值,根据线性代数相关知识,我们可以得到$C=A_xB^T_y-A_yB_x^T$
如此操作时需要注意对array
进行reshape
下面展示了实现矢量集叉乘和判断线段集是否相交的代码
1 2 3 4 5 6 7
| def calc_cross(a: np.array, b: np.array): ax = a[:, 0].reshape(-1, 1) ay = a[:, 1].reshape(-1, 1) bx = b[:, 0].reshape(1, -1) by = b[:, 1].reshape(1, -1) return np.dot(ax, by)-np.dot(ay, bx)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| def check_crossing(lines1: np.array, lines2: np.array): begs1 = lines1[:, 0, :] begs2 = lines2[:, 0, :] ends1 = lines1[:, 1, :] ends2 = lines2[:, 1, :] l1 = ends1-begs1 l2 = ends2-begs2
v1 = begs2-begs1 v2 = begs2-ends1 c1 = calc_cross(v1, l2) c2 = calc_cross(v2, l2) c = c1*c2 bca = c < 0
v1 = begs1-begs2 v2 = begs1-ends2 c1 = calc_cross(v1, l1) c2 = calc_cross(v2, l1) c = c1*c2 bcb = c < 0
bc = np.all([bca, bcb], axis=0) return bc
|
这个时候我意识到了一个问题:在实际的移动过程中,z轴的移动无法控制,因此需要在路径规划的过程中对z轴进行单独处理
因此:在每一次移动时,首先移动z轴,然后就可以理解为平面内运动