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


pinocchio
Author(s):
autogenerated on Fri Nov 1 2024 02:41:39