capsule-approximation.py
Go to the documentation of this file.
1 """
2 Copyright (c) 2020 INRIA
3 Inspired from Antonio El Khoury PhD: https://tel.archives-ouvertes.fr/file/index/docid/833019/filename/thesis.pdf
4 Section 3.8.1 Computing minimum bounding capsules
5 """
6 
7 import numpy as np
8 import scipy.optimize as optimize
9 
10 import hppfcl
11 
12 """
13 Capsule definition
14 a, b: the two extremities of the capsule segment
15 r: radius of the capsule
16 """
17 
18 EPSILON = 1e-8
19 CONSTRAINT_INFLATION_RATIO = 5e-3
20 
21 
22 def capsule_volume(a, b, r):
23  return np.linalg.norm(b - a) * np.pi * r**2 + 4 / 3 * np.pi * r**3
24 
25 
27  ap = p - a
28  ab = b - a
29  t = ap.dot(ab) / ab.dot(ab)
30  t = np.clip(t, 0, 1)
31  p_witness = a[None, :] + (b - a)[None, :] * t[:, None]
32  dist = np.linalg.norm(p - p_witness, axis=1).max()
33  return dist
34 
35 
36 def pca_approximation(vertices):
37  mean = vertices.mean(axis=0)
38  vertices -= mean
39  u, s, vh = np.linalg.svd(vertices, full_matrices=True)
40  components = vh
41  pca_proj = vertices.dot(components.T)
42  vertices += mean
43 
44  a0 = mean + components[0] * (pca_proj[:, 0].min() - EPSILON)
45  b0 = mean + components[0] * (pca_proj[:, 0].max() + EPSILON)
46  radius = np.linalg.norm(pca_proj[:, 1:], axis=1).max()
47  return a0, b0, radius
48 
49 
50 def capsule_approximation(vertices):
51  a0, b0, r0 = pca_approximation(vertices)
52  constraint_inflation = CONSTRAINT_INFLATION_RATIO * r0
53  x0 = np.array(list(a0) + list(b0) + [r0])
54  constraint_cap = lambda x: distance_points_segment(vertices, x[:3], x[3:6]) - x[6]
55  capsule_vol = lambda x: capsule_volume(x[:3], x[3:6], x[6])
56  constraint = optimize.NonlinearConstraint(
57  constraint_cap, lb=-np.inf, ub=-constraint_inflation
58  )
59  res = optimize.minimize(capsule_vol, x0, constraints=constraint)
60  res_constraint = constraint_cap(res.x)
61  assert (
62  res_constraint <= 1e-4
63  ), "The computed solution is invalid, a vertex is at a distance {:.5f} of the capsule.".format(
64  res_constraint
65  )
66  a, b, r = res.x[:3], res.x[3:6], res.x[6]
67  return a, b, r
68 
69 
70 def approximate_mesh(filename, lMg):
71  mesh_loader = hppfcl.MeshLoader()
72  mesh = mesh_loader.load(filename, np.ones(3))
73  vertices = np.array([lMg * mesh.vertices(i) for i in range(mesh.num_vertices)])
74  assert vertices.shape == (mesh.num_vertices, 3)
75  a, b, r = capsule_approximation(vertices)
76  return a, b, r
77 
78 
79 def parse_urdf(infile, outfile):
80  from lxml import etree
81 
82  tree = etree.parse(infile)
83 
84  def get_path(fn):
85  if fn.startswith("package://"):
86  relpath = fn[len("package://") :]
87  import os
88 
89  for rospath in os.environ["ROS_PACKAGE_PATH"].split(":"):
90  abspath = os.path.join(rospath, relpath)
91  if os.path.isfile(abspath):
92  return abspath
93  raise ValueError("Could not find " + fn)
94  return fn
95 
96  def get_transform(origin):
97  from pinocchio import SE3, rpy
98 
99  _xyz = [float(v) for v in origin.attrib.get("xyz", "0 0 0").split(" ")]
100  _rpy = [float(v) for v in origin.attrib.get("rpy", "0 0 0").split(" ")]
101  return SE3(rpy.rpyToMatrix(*_rpy), np.array(_xyz))
102 
103  def set_transform(origin, a, b):
104  from pinocchio import rpy, Quaternion
105 
106  length = np.linalg.norm(b - a)
107  z = (b - a) / length
108  R = Quaternion.FromTwoVectors(np.array([0, 0, 1]), z).matrix()
109  origin.attrib["xyz"] = " ".join([str(v) for v in ((a + b) / 2)])
110  origin.attrib["rpy"] = " ".join([str(v) for v in rpy.matrixToRpy(R)])
111 
112  from tqdm import tqdm
113 
114  for mesh in tqdm(
115  tree.xpath("/robot/link/collision/geometry/mesh"), desc="Generating capsules"
116  ):
117  geom = mesh.getparent()
118  coll = geom.getparent()
119  link = coll.getparent()
120  if coll.find("origin") is None:
121  o = etree.Element("origin")
122  o.tail = geom.tail
123  coll.insert(0, o)
124  origin = coll.find("origin")
125  lMg = get_transform(origin)
126 
127  meshfile = get_path(mesh.attrib["filename"])
128  import os
129 
130  name = os.path.basename(meshfile)
131  # Generate capsule
132  a, b, radius = approximate_mesh(meshfile, lMg)
133  length = np.linalg.norm(b - a)
134 
135  set_transform(origin, a, b)
136 
137  mesh.tag = "cylinder"
138  mesh.attrib.pop("filename")
139  mesh.attrib["radius"] = str(radius)
140  mesh.attrib["length"] = str(length)
141  coll.attrib["name"] = name
142 
143  if link.find("collision_checking") is None:
144  link.append(etree.Element("collision_checking"))
145  collision_checking = link.find("collision_checking")
146  collision_checking.append(etree.Element("capsule"))
147  collision_checking[-1].attrib["name"] = name
148 
149  tree.write(outfile)
150 
151 
152 if __name__ == "__main__":
153  # Example for a single capsule
154  # filename = "mesh.obj"
155  # mesh_loader = hppfcl.MeshLoader()
156  # mesh = mesh_loader.load(filename, np.ones(3))
157  # vertices = mesh.vertices()
158  # a, b, r = capsule_approximation(vertices)
159 
160  # Example for a whole URDF model
161  # This path refers to Pinocchio source code but you can define your own directory here.
162  pinocchio_model_dir = join(dirname(dirname(str(abspath(__file__)))), "models")
163  urdf_filename = (
164  pinocchio_model_dir
165  + "models/example-robot-data/robots/ur_description/urdf/ur5_gripper.urdf"
166  )
167  parse_urdf(urdf_filename, "ur5_gripper_with_capsules.urdf")
optimize
capsule-approximation.parse_urdf
def parse_urdf(infile, outfile)
Definition: capsule-approximation.py:79
capsule-approximation.distance_points_segment
def distance_points_segment(p, a, b)
Definition: capsule-approximation.py:26
capsule-approximation.capsule_approximation
def capsule_approximation(vertices)
Definition: capsule-approximation.py:50
capsule-approximation.approximate_mesh
def approximate_mesh(filename, lMg)
Definition: capsule-approximation.py:70
pinocchio::cholesky::min
JointCollectionTpl const DataTpl< Scalar, Options, JointCollectionTpl > const Eigen::MatrixBase< Mat > & min
Definition: cholesky.hpp:91
capsule-approximation.pca_approximation
def pca_approximation(vertices)
Definition: capsule-approximation.py:36
capsule-approximation.capsule_volume
def capsule_volume(a, b, r)
Definition: capsule-approximation.py:22
pinocchio::python::context::SE3
SE3Tpl< Scalar, Options > SE3
Definition: bindings/python/context/generic.hpp:53
CppAD::max
AD< Scalar > max(const AD< Scalar > &x, const AD< Scalar > &y)
Definition: autodiff/cppad.hpp:180


pinocchio
Author(s):
autogenerated on Sat Jun 1 2024 02:40:31