碰撞检测问题在虚拟现实、计算机辅助设计与制造、游戏及机器人等领域有着广泛的应用,甚至成为关键技术。而包围盒算法是进行碰撞干涉初步检测的重要方法之一。包围盒算法是一种求解离散点集最优包围空间的方法。基本思想是用体积稍大且特性简单的几何体(称为包围盒)来近似地代替复杂的几何对象。为物体添加包围体的目的是快速的进行碰撞检测或者进行精确的碰撞检测之前进行过滤(即当包围体碰撞,才进行精确碰撞检测和处理)。包围体类型包括球体、轴对齐包围盒(AABB/Axis-aligned bounding box)、有向包围盒(OBB/Oriented bounding box)以及凸壳/凸包(Convex Hull)等。

使用PCA方法创建OBB(oriented bounding-box)包围盒

  AABB是应用最早的包围盒。它被定义为包含该对象,且边平行于坐标轴的最小六面体。故描述一个AABB,仅需六个标量。AABB构造比较简单,存储空间小,但紧密性差,尤其对不规则几何形体,冗余空间很大,当对象旋转时,无法对其进行相应的旋转。包围球被定义为包含该对象的最小的球体。确定包围球,首先需分别计算组成对象的基本几何元素集合中所有元素的顶点的x,y,z坐标的均值以确定包围球的球心,再由球心与三个最大值坐标所确定的点间的距离确定半径r。包围球的碰撞检测主要是比较两球间半径和与球心距离的大小。OBB是较为常用的包围盒类型。它是包含该对象且相对于坐标轴方向任意的最小的长方体。OBB最大特点是它的方向的任意性,这使得它可以根据被包围对象的形状特点尽可能紧密的包围对象,但同时也使得它的相交测试变得复杂。OBB包围盒比AABB包围盒和包围球更加紧密地逼近物体,能比较显著地减少包围体的个数,从而避免了大量包围体之间的相交检测。但OBB之间的相交检测比AABB或包围球体之间的相交检测更费时。

  OBB包围盒的生成思路简单来说就是根据一系列坐标点,通过PCA(主成分分析)获得特征向量,即OBB的主轴,推导过程可参考下面的链接。已知平面上点的坐标计算其OBB包围框四个角点以及中心的Python代码如下:

import numpy as np
from scipy.spatial import ConvexHull


def get_obb_from_points(points, calcconvexhull=True):
    """ given a set of points, calculate the oriented bounding box. 
    
    Parameters:
    points: numpy array of point coordinates with shape (n,2)
            where n is the number of points
    calcconvexhull: boolean, calculate the convex hull of the 
            points before calculating the bounding box. You typically
            want to do that unless you know you are passing in a convex
            point set
    Output:
        tuple of corners, centre
    """

    if calcconvexhull:
        _ch = ConvexHull(points)
        points = _ch.points[_ch.vertices]

    cov_points = np.cov(points,y = None,rowvar = 0,bias = 1)
    v, vect = np.linalg.eig(cov_points)
    tvect = np.transpose(vect)

    # use the inverse of the eigenvectors as a rotation matrix and
    # rotate the points so they align with the x and y axes
    points_rotated = np.dot(points,np.linalg.inv(tvect))
    # get the minimum and maximum x and y 
    mina = np.min(points_rotated,axis=0)
    maxa = np.max(points_rotated,axis=0)
    diff = (maxa - mina)*0.5
    # the center is just half way between the min and max xy
    center = mina + diff

    # get the corners by subtracting and adding half the bounding boxes height and width to the center
    corners = np.array([center+[-diff[0],-diff[1]],center+[diff[0],-diff[1]],center+[diff[0],diff[1]],center+[-diff[0],diff[1]],center+[-diff[0],-diff[1]]])
    # use the the eigenvectors as a rotation matrix and
    # rotate the corners and the center back
    corners = np.dot(corners,tvect)
    center = np.dot(center,tvect)

    return corners, center

   或者可以用下面的形式:

import numpy as np
from scipy.spatial import ConvexHull


def get_obb_from_points(points, calcconvexhull=True):
    """ given a set of points, calculate the oriented bounding box. 
    
    Parameters:
    points: numpy array of point coordinates with shape (n,2)
            where n is the number of points
    calcconvexhull: boolean, calculate the convex hull of the 
            points before calculating the bounding box. You typically
            want to do that unless you know you are passing in a convex
            point set
    Output:
        tuple of corners, centre
    """

    if calcconvexhull:
        _ch = ConvexHull(points)
        points = _ch.points[_ch.vertices]

    cov_points = np.cov(points,y = None,rowvar = 0,bias = 1)
    v, vect = np.linalg.eig(cov_points)
    tvect = np.transpose(vect)

    # use the eigenvectors as a rotation matrix and
    # rotate the points so they align with the x and y axes
    points_rotated = np.dot(tvect, points.T)

    # get the minimum and maximum x and y 
    mina = np.min(points_rotated, axis=1)
    maxa = np.max(points_rotated, axis=1)
    diff = (maxa - mina)*0.5
    # the center is just half way between the min and max xy
    center = mina + diff

    # get the corners by subtracting and adding half the bounding boxes height and width to the center
    corners = np.array([center+[-diff[0],-diff[1]],center+[diff[0],-diff[1]],center+[diff[0],diff[1]],center+[-diff[0],diff[1]],center+[-diff[0],-diff[1]]])
    # use the inverse of the eigenvectors as a rotation matrix 
    # and rotate the corners and the center back
    corners = np.dot(np.linalg.inv(tvect), corners.T)
    center = np.dot(np.linalg.inv(tvect), center)

    return corners, center
View Code

相关文章:

  • 2021-08-07
  • 2022-12-23
  • 2021-11-18
  • 2021-10-24
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-11-07
猜你喜欢
  • 2021-05-02
  • 2021-12-05
  • 2022-12-23
  • 2022-12-23
  • 2021-06-28
  • 2022-12-23
相关资源
相似解决方案