Пример #1
0
def merge_triangle_meshes(*items):
    merged_geom = Geometry3D()
    verts = []
    tris = []
    nverts = 0
    for i, item in enumerate(items):
        if isinstance(item, Geometry3D):
            xform, (iverts, itris) = numpy_convert.to_numpy(item)
        elif hasattr(item, 'geometry'):
            xform, (iverts, itris) = numpy_convert.to_numpy(item.geometry())
        else:
            raise ValueError(
                "Don't know how to merge trimesh from item of type " +
                item.__class__.__name__)
        verts.append(
            np.dot(np.hstack((iverts, np.ones((len(iverts), 1)))),
                   xform.T)[:, :3])
        tris.append(itris + nverts)
        nverts += len(iverts)
    verts = np.vstack(verts)
    tris = np.vstack(tris)
    for t in tris:
        assert all(v >= 0 and v < len(verts) for v in t)
    mesh = numpy_convert.from_numpy((verts, tris), 'TriangleMesh')
    merged_geom.setTriangleMesh(mesh)
    return merged_geom
Пример #2
0
 def __init__(self,
              env_obj,
              env_geom,
              gravity_coefficient=-9.8,
              gravity_direction=[0, 0, 1],
              mass=1.0):
     self.env_obj = env_obj
     self.env_geom = env_geom
     pointcloud = to_numpy(self.env_geom.pc.getPointCloud(),
                           type="PointCloud")
     R = to_numpy(self.env_geom.pc.getCurrentTransform()[0], 'Matrix3')
     t = to_numpy(self.env_geom.pc.getCurrentTransform()[1], 'Vector3')
     pointcloud[:, :3] = np.dot(R, pointcloud.T[:3, :]).T + t
     self.point_cloud_array = pointcloud[:, :3]
     self.point_cloud_tree = KDTree(self.point_cloud_array, leaf_size=100)
     self.gravity_coefficient = gravity_coefficient
     self.gravity_direction = gravity_direction
     self.mass = mass
Пример #3
0
    def init(self, scene, object, hints):
        """Needs object to contain a structured PointCloud."""
        if not isinstance(object, (RigidObjectModel, Geometry3D, PointCloud)):
            print(
                "Need to pass an object as a RigidObjectModel, Geometry3D, or PointCloud"
            )
            return False
        if isinstance(object, RigidObjectModel):
            return self.init(scene, object.geometry(), hints)
        pc = None
        xform = None
        if isinstance(object, Geometry3D):
            pc = object.getPointCloud()
            xform = object.getCurrentTransform()
        else:
            pc = object
            xform = se3.identity()
        self.pc = pc
        self.pc_xform = xform

        #now look through PC and find flat parts
        #do a spatial hash
        from collections import defaultdict
        estimation_knn = 6
        pts = numpy_convert.to_numpy(pc)
        N = pts.shape[0]
        positions = pts[:, :3]
        normals = np.zeros((N, 3))
        indices = (positions * (1.0 / self._gripper.opening_span)).astype(int)
        pt_hash = defaultdict(list)
        for i, (ind, p) in enumerate(zip(indices, positions)):
            pt_hash[ind].append((i, p))
        options = []
        for (ind, iplist) in pt_hash.items():
            if len(iplist) < estimation_knn:
                pass
            else:
                pindices = [ip[0] for ip in iplist]
                pts = [ip[1] for ip in iplist]
                c, n = fit_plane_centroid(pts)
                if n[2] < 0:
                    n = vectorops.mul(n, -1)
                verticality = self.vertical_penalty(math.acos(n[2]))
                var = sum(
                    vectorops.dot(vectorops.sub(p, c), n)**2 for p in pts)
                roughness = self.roughness_penalty(var)
                options.append((cn, n, verticality + roughness))
        if len(options) == 0:
            return False
        self.options = options.sorted(key=lambda x: -x[2])
        self.index = 0
        return True
Пример #4
0
 def get_geometry(self,robot,qfinger=None,type='Group'):
     """Returns a Geometry of the gripper frozen at its configuration.
     If qfinger = None, the current configuration is used.  Otherwise,
     qfinger is a finger configuration.
     
     type can be 'Group' (most general and fastest) or 'TriangleMesh'
     (compatible with Jupyter notebook visualizer.)
     """
     if qfinger is not None:
         q0 = robot.getConfig()
         robot.setConfig(self.set_finger_config(q0,qfinger))
     res = Geometry3D()
     gripper_links = self.gripper_links if self.gripper_links is not None else [self.base_link] + self.descendant_links(robot)
     if type == 'Group':
         res.setGroup()
         Tbase = robot.link(self.base_link).getTransform()
         for i,link in enumerate(gripper_links):
             Trel = se3.mul(se3.inv(Tbase),robot.link(link).getTransform())
             g = robot.link(link).geometry().clone()
             if not g.empty():
                 g.setCurrentTransform(*se3.identity())
                 g.transform(*Trel)
             else:
                 print("Uh... link",robot.link(link).getName(),"has empty geometry?")
             res.setElement(i,g)
         if qfinger is not None:
             robot.setConfig(q0)
         return res
     else:
         import numpy as np
         from klampt.io import numpy_convert
         #merge the gripper parts into a static geometry
         verts = []
         tris = []
         nverts = 0
         for i,link in enumerate(gripper_links):
             xform,(iverts,itris) = numpy_convert.to_numpy(robot.link(link).geometry())
             verts.append(np.dot(np.hstack((iverts,np.ones((len(iverts),1)))),xform.T)[:,:3])
             tris.append(itris+nverts)
             nverts += len(iverts)
         verts = np.vstack(verts)
         tris = np.vstack(tris)
         for t in tris:
             assert all(v >= 0 and v < len(verts) for v in t)
         mesh = numpy_convert.from_numpy((verts,tris),'TriangleMesh')
         res.setTriangleMesh(mesh)
         if qfinger is not None:
             robot.setConfig(q0)
         return res
Пример #5
0
def get_object_normals(obj):
    """General-purpose object normal getter"""
    if isinstance(obj, TriangleMesh):
        verts, tris = numpy_convert.to_numpy(obj)
        return get_triangle_normals(verts, tris)
    elif isinstance(obj, PointCloud):
        return geometry.point_cloud_normals(obj)
    elif isinstance(obj, Geometry3D):
        if obj.type() == 'TriangleMesh':
            return get_object_normals(obj.getTriangleMesh())
        elif obj.type() == 'PointCloud':
            return get_object_normals(obj.getPointCloud())
        else:
            raise ValueError(
                "Can only get normals for triangle mesh and point cloud")
    elif hasattr(obj, 'geometry'):
        return get_object_normals(obj.geometry())
    else:
        raise ValueError(
            "Invalid object sent to get_object_normals, can only be a TriangleMesh or PointCloud"
        )
Пример #6
0
def stable_faces(obj, com=None, stability_tol=0.0, merge_tol=0.0):
    """
    Returns a list of support polygons on the object that are
    likely to be stable on a planar surface.
    
    Args:
        obj (RigidObjectModel or Geometry3D): the object.
        com (3-list, optional): sets the local center of mass. If
            not given, the default RigidObjectModel's COM will be used,
            or (0,0,0) will be used for a Geometry3D.
        stability_tol (float,optional): if > 0, then only faces that
            are stable with all perturbed "up" directions (dx,dy,1) with
            ||(dx,dy)||<= normal_tol will be outputted (robust stability). 
            If < 0, then all faces that are stable from some "up" direction
            (dx,dy,1) with ||(dx,dy)||<= |normal_tol| will be outputted
            (non-robust stability)
        merge_tol (float, optional): if > 0, then adjacent faces with
            normals whose angles are within this tolerance (in rads) will
            be merged together.
    
    Returns:
        list of list of 3-vectors: The set of all polygons that could
        form stable sides. Each polygon is convex and listed in
        counterclockwise order (i.e., the outward normal can be obtained
        via:
        
            (poly[2]-poly[0]) x (poly[1]-poly[0])
        
    """
    if isinstance(obj, RigidObjectModel):
        geom = obj.geometry()
        if com is None:
            com = obj.getMass().getCom()
    else:
        geom = obj
        if com is None:
            com = (0, 0, 0)
    assert len(com) == 3, "Need to provide a 3D COM"
    ch_trimesh = geom.convert('ConvexHull').convert('TriangleMesh')
    xform, (verts, tris) = numpy_convert.to_numpy(ch_trimesh)
    trinormals = get_triangle_normals(verts, tris)

    edges = dict()
    tri_neighbors = np.full(tris.shape, -1, dtype=np.int32)
    for ti, tri in enumerate(tris):
        for ei, e in enumerate([(tri[0], tri[1]), (tri[1], tri[2]),
                                (tri[2], tri[0])]):
            if (e[1], e[0]) in edges:
                tn, tne = edges[(e[1], e[0])]
                if tri_neighbors[tn][tne] >= 0:
                    print("Warning, triangle", ti,
                          "neighbors two triangles on edge", tne, "?")
                tri_neighbors[ti][ei] = tn
                tri_neighbors[tn][tne] = ti
            else:
                edges[e] = ti, ei
    num_empty_edges = 0
    for ti, tri in enumerate(tris):
        for e in range(3):
            if tri_neighbors[tn][e] < 0:
                num_empty_edges += 1
    if num_empty_edges > 0:
        print("Info: boundary of mesh has", num_empty_edges, "edges")
    visited = [False] * len(tris)
    cohesive_faces = dict()
    for ti, tri in enumerate(tris):
        if visited[ti]:
            continue
        face = [ti]
        visited[ti] = True
        myvisit = set()
        myvisit.add(ti)
        q = deque()
        q.append(ti)
        while q:
            tvisit = q.popleft()
            for tn in tri_neighbors[tvisit]:
                if tn >= 0 and tn not in myvisit:
                    if math.acos(trinormals[ti].dot(
                            trinormals[tn])) <= merge_tol:
                        face.append(tn)
                        myvisit.add(tn)
                        q.append(tn)
        for t in myvisit:
            visited[t] = True
        cohesive_faces[ti] = face
    output = []
    for t, face in cohesive_faces.items():
        n = trinormals[t]
        R = so3.canonical(n)
        if len(face) > 1:
            #project face onto the canonical basis
            faceverts = set()
            for t in face:
                faceverts.add(tris[t][0])
                faceverts.add(tris[t][1])
                faceverts.add(tris[t][2])
            faceverts = list(faceverts)
            xypts = [so3.apply(so3.inv(R), verts[v])[1:3] for v in faceverts]
            try:
                ch = ConvexHull(xypts)
                face = [faceverts[v] for v in ch.vertices]
            except Exception:
                print("Error computing convex hull of", xypts)
                print("Vertex indices", faceverts)
                print("Vertices", [verts[v] for v in faceverts])
        else:
            face = tris[face[0]]
        comproj = np.array(so3.apply(so3.inv(R), com)[1:3])

        stable = True
        for vi in range(len(face)):
            vn = (vi + 1) % len(face)
            a, b = face[vi], face[vn]
            pa = np.array(so3.apply(so3.inv(R), verts[a])[1:3])
            pb = np.array(so3.apply(so3.inv(R), verts[b])[1:3])
            #check distance from com
            elen = np.linalg.norm(pb - pa)
            if elen == 0:
                continue
            sign = np.cross(pb - pa, comproj - pa) / elen
            if sign < stability_tol:
                stable = False
                break
        if stable:
            output.append([verts[i] for i in face])
    return output