meshcat_visualizer.py
Go to the documentation of this file.
1 from .. import pinocchio_pywrap as pin
2 from ..utils import npToTuple
3 
4 from . import BaseVisualizer
5 
6 import os
7 import warnings
8 import numpy as np
9 
10 try:
11  import hppfcl
12  WITH_HPP_FCL_BINDINGS = True
13 except:
14  WITH_HPP_FCL_BINDINGS = False
15 
16 
17 def loadBVH(bvh):
18  import meshcat.geometry as mg
19 
20  num_vertices = bvh.num_vertices
21  num_tris = bvh.num_tris
22  vertices = np.empty((num_vertices,3))
23  faces = np.empty((num_tris,3),dtype=int)
24 
25  for k in range(num_tris):
26  tri = bvh.tri_indices(k)
27  faces[k] = [tri[i] for i in range(3)]
28 
29  for k in range(num_vertices):
30  vert = bvh.vertices(k)
31  vertices[k] = vert
32 
33  vertices = vertices.astype(np.float32)
34  if num_tris > 0:
35  mesh = mg.TriangularMeshGeometry(vertices, faces)
36  else:
37  mesh = mg.Points(
38  mg.PointsGeometry(vertices.T, color=np.repeat(np.ones((3,1)),num_vertices,axis=1)),
39  mg.PointsMaterial(size=0.002))
40 
41  return mesh
42 
43 def createCapsule(length, radius, radial_resolution = 30, cap_resolution = 10):
44  nbv = np.array([max(radial_resolution, 4), max(cap_resolution, 4)])
45  h = length
46  r = radius
47  position = 0
48  vertices = np.zeros((nbv[0] * (2 * nbv[1]) + 2, 3))
49  for j in range(nbv[0]):
50  phi = (( 2 * np.pi * j) / nbv[0])
51  for i in range(nbv[1]):
52  theta = ((np.pi / 2 * i) / nbv[1])
53  vertices[position + i, :] = np.array([np.cos(theta) * np.cos(phi) * r,
54  np.cos(theta) * np.sin(phi) * r,
55  -h / 2 - np.sin(theta) * r])
56  vertices[position + i + nbv[1], :] = np.array([np.cos(theta) * np.cos(phi) * r,
57  np.cos(theta) * np.sin(phi) * r,
58  h / 2 + np.sin(theta) * r])
59  position += nbv[1] * 2
60  vertices[-2, :] = np.array([0, 0, -h / 2 - r])
61  vertices[-1, :] = np.array([0, 0, h / 2 + r])
62  indexes = np.zeros((nbv[0] * (4 * (nbv[1] - 1) + 4), 3))
63  index = 0
64  stride = nbv[1] * 2
65  last = nbv[0] * (2 * nbv[1]) + 1
66  for j in range(nbv[0]):
67  j_next = (j + 1) % nbv[0]
68  indexes[index + 0] = np.array([j_next * stride + nbv[1], j_next * stride, j * stride])
69  indexes[index + 1] = np.array([j * stride + nbv[1], j_next * stride + nbv[1], j * stride])
70  indexes[index + 2] = np.array([j * stride + nbv[1] - 1, j_next * stride + nbv[1] - 1, last - 1])
71  indexes[index + 3] = np.array([j_next * stride + 2 * nbv[1] - 1, j * stride + 2 * nbv[1] - 1, last])
72  for i in range(nbv[1]-1):
73  indexes[index + 4 + i * 4 + 0] = np.array([j_next * stride + i, j_next * stride + i + 1, j * stride + i])
74  indexes[index + 4 + i * 4 + 1] = np.array([j_next * stride + i + 1, j * stride + i + 1, j * stride + i])
75  indexes[index + 4 + i * 4 + 2] = np.array([j_next * stride + nbv[1] + i + 1, j_next * stride + nbv[1] + i, j * stride + nbv[1] + i])
76  indexes[index + 4 + i * 4 + 3] = np.array([j_next * stride + nbv[1] + i + 1, j * stride + nbv[1] + i, j * stride + nbv[1] + i + 1])
77  index += 4 * (nbv[1] - 1) + 4
78  import meshcat.geometry
79  return meshcat.geometry.TriangularMeshGeometry(vertices, indexes)
80 
81 class MeshcatVisualizer(BaseVisualizer):
82  """A Pinocchio display using Meshcat"""
83 
84  def getViewerNodeName(self, geometry_object, geometry_type):
85  """Return the name of the geometry object inside the viewer."""
86  if geometry_type is pin.GeometryType.VISUAL:
87  return self.viewerVisualGroupName + '/' + geometry_object.name
88  elif geometry_type is pin.GeometryType.COLLISION:
89  return None # TODO: collision meshes
90 
91  def initViewer(self, viewer=None, open=False, loadModel=False):
92  """Start a new MeshCat server and client.
93  Note: the server can also be started separately using the "meshcat-server" command in a terminal:
94  this enables the server to remain active after the current script ends.
95  """
96 
97  import meshcat
98 
99  self.viewer = meshcat.Visualizer() if viewer is None else viewer
100 
101  if open:
102  self.viewer.open()
103 
104  if loadModel:
105  self.loadViewerModel()
106 
107  def loadPrimitive(self, geometry_object):
108 
109  import meshcat.geometry
110 
111  # Cylinders need to be rotated
112  R = np.array([[1., 0., 0., 0.],
113  [0., 0., -1., 0.],
114  [0., 1., 0., 0.],
115  [0., 0., 0., 1.]])
116  RotatedCylinder = type("RotatedCylinder", (meshcat.geometry.Cylinder,), {"intrinsic_transform": lambda self: R })
117 
118  geom = geometry_object.geometry
119  if isinstance(geom, hppfcl.Capsule):
120  if hasattr(meshcat.geometry, 'TriangularMeshGeometry'):
121  obj = createCapsule(2. * geom.halfLength, geom.radius)
122  else:
123  obj = RotatedCylinder(2. * geom.halfLength, geom.radius)
124  elif isinstance(geom, hppfcl.Cylinder):
125  obj = RotatedCylinder(2. * geom.halfLength, geom.radius)
126  elif isinstance(geom, hppfcl.Box):
127  obj = meshcat.geometry.Box(npToTuple(2. * geom.halfSide))
128  elif isinstance(geom, hppfcl.Sphere):
129  obj = meshcat.geometry.Sphere(geom.radius)
130  else:
131  msg = "Unsupported geometry type for %s (%s)" % (geometry_object.name, type(geom) )
132  warnings.warn(msg, category=UserWarning, stacklevel=2)
133  obj = None
134 
135  return obj
136 
137  def loadMesh(self, geometry_object):
138 
139  import meshcat.geometry
140 
141  # Mesh path is empty if Pinocchio is built without HPP-FCL bindings
142  if geometry_object.meshPath == "":
143  msg = "Display of geometric primitives is supported only if pinocchio is build with HPP-FCL bindings."
144  warnings.warn(msg, category=UserWarning, stacklevel=2)
145  return None
146 
147  # Get file type from filename extension.
148  _, file_extension = os.path.splitext(geometry_object.meshPath)
149  if file_extension.lower() == ".dae":
150  obj = meshcat.geometry.DaeMeshGeometry.from_file(geometry_object.meshPath)
151  elif file_extension.lower() == ".obj":
152  obj = meshcat.geometry.ObjMeshGeometry.from_file(geometry_object.meshPath)
153  elif file_extension.lower() == ".stl":
154  obj = meshcat.geometry.StlMeshGeometry.from_file(geometry_object.meshPath)
155  else:
156  msg = "Unknown mesh file format: {}.".format(geometry_object.meshPath)
157  warnings.warn(msg, category=UserWarning, stacklevel=2)
158  obj = None
159 
160  return obj
161 
162  def loadViewerGeometryObject(self, geometry_object, geometry_type, color=None):
163  """Load a single geometry object"""
164  import meshcat.geometry
165 
166  viewer_name = self.getViewerNodeName(geometry_object, geometry_type)
167 
168  try:
169  if WITH_HPP_FCL_BINDINGS and isinstance(geometry_object.geometry, hppfcl.ShapeBase):
170  obj = self.loadPrimitive(geometry_object)
171  elif WITH_HPP_FCL_BINDINGS and isinstance(geometry_object.geometry, hppfcl.BVHModelBase):
172  obj = loadBVH(geometry_object.geometry)
173  else:
174  obj = self.loadMesh(geometry_object)
175  if obj is None:
176  return
177  except Exception as e:
178  msg = "Error while loading geometry object: %s\nError message:\n%s" % (geometry_object.name, e)
179  warnings.warn(msg, category=UserWarning, stacklevel=2)
180  return
181 
182  if isinstance(obj, meshcat.geometry.Object):
183  self.viewer[viewer_name].set_object(obj)
184  elif isinstance(obj, meshcat.geometry.Geometry):
185  material = meshcat.geometry.MeshPhongMaterial()
186  # Set material color from URDF, converting for triplet of doubles to a single int.
187  if color is None:
188  meshColor = geometry_object.meshColor
189  else:
190  meshColor = color
191  material.color = int(meshColor[0] * 255) * 256**2 + int(meshColor[1] * 255) * 256 + int(meshColor[2] * 255)
192  # Add transparency, if needed.
193  if float(meshColor[3]) != 1.0:
194  material.transparent = True
195  material.opacity = float(meshColor[3])
196  self.viewer[viewer_name].set_object(obj, material)
197 
198  def loadViewerModel(self, rootNodeName="pinocchio", color = None):
199  """Load the robot in a MeshCat viewer.
200  Parameters:
201  rootNodeName: name to give to the robot in the viewer
202  color: optional, color to give to the robot. This overwrites the color present in the urdf.
203  Format is a list of four RGBA floats (between 0 and 1)
204  """
205 
206  # Set viewer to use to gepetto-gui.
207  self.viewerRootNodeName = rootNodeName
208 
209  # Load robot meshes in MeshCat
210 
211  # Collisions
212  # self.viewerCollisionGroupName = self.viewerRootNodeName + "/" + "collisions"
213  self.viewerCollisionGroupName = None # TODO: collision meshes
214 
215  # Visuals
216  self.viewerVisualGroupName = self.viewerRootNodeName + "/" + "visuals"
217 
218  for visual in self.visual_model.geometryObjects:
219  self.loadViewerGeometryObject(visual,pin.GeometryType.VISUAL,color)
220 
221  def reload(self, new_geometry_object, geometry_type = None):
222  """ Reload a geometry_object given by its name and its type"""
223  geom_id = self.visual_model.getGeometryId(new_geometry_object.name)
224  self.visual_model.geometryObjects[geom_id] = new_geometry_object
225 
226  visual = self.visual_model.geometryObjects[geom_id]
227  self.delete(new_geometry_object, pin.GeometryType.VISUAL)
228  self.loadViewerGeometryObject(visual,pin.GeometryType.VISUAL,color = None)
229 
230  def clean(self):
231  self.viewer.delete()
232 
233  def delete(self, geometry_object, geometry_type):
234  viewer_name = self.getViewerNodeName(geometry_object, geometry_type)
235  self.viewer[viewer_name].delete()
236 
237  def display(self, q = None):
238  """Display the robot at configuration q in the viewer by placing all the bodies."""
239  if q is not None:
240  pin.forwardKinematics(self.model,self.data,q)
241 
242  pin.updateGeometryPlacements(self.model, self.data, self.visual_model, self.visual_data)
243  for visual in self.visual_model.geometryObjects:
244  # Get mesh pose.
245  M = self.visual_data.oMg[self.visual_model.getGeometryId(visual.name)]
246  # Manage scaling
247  scale = np.asarray(visual.meshScale).flatten()
248  S = np.diag(np.concatenate((scale,[1.0])))
249  T = np.array(M.homogeneous).dot(S)
250  # Update viewer configuration.
251  self.viewer[self.getViewerNodeName(visual,pin.GeometryType.VISUAL)].set_transform(T)
252 
253  def displayCollisions(self,visibility):
254  """Set whether to display collision objects or not.
255  WARNING: Plotting collision meshes is not yet available for MeshcatVisualizer."""
256  # TODO
257  warnings.warn("Plotting collision meshes is not available for MeshcatVisualizer", category=UserWarning, stacklevel=2)
258 
259  def displayVisuals(self,visibility):
260  """Set whether to display visual objects or not
261  WARNING: Visual meshes are always plotted for MeshcatVisualizer"""
262  # TODO
263  warnings.warn("Visual meshes are always plotted for MeshcatVisualizer", category=UserWarning, stacklevel=2)
264 
265 __all__ = ['MeshcatVisualizer']
def getViewerNodeName(self, geometry_object, geometry_type)
def reload(self, new_geometry_object, geometry_type=None)
AD< Scalar > max(const AD< Scalar > &x, const AD< Scalar > &y)
def delete(self, geometry_object, geometry_type)
def loadViewerGeometryObject(self, geometry_object, geometry_type, color=None)
def createCapsule(length, radius, radial_resolution=30, cap_resolution=10)
def loadViewerModel(self, rootNodeName="pinocchio", color=None)
def initViewer(self, viewer=None, open=False, loadModel=False)


pinocchio
Author(s):
autogenerated on Tue Jun 1 2021 02:45:04