1 from ..
import pinocchio_pywrap
as pin
2 from ..utils
import npToTuple
4 from .
import BaseVisualizer
9 from distutils.version
import LooseVersion
13 WITH_HPP_FCL_BINDINGS =
True 15 WITH_HPP_FCL_BINDINGS =
False 17 DEFAULT_COLOR_PROFILES = {
18 "gray": ([0.98, 0.98, 0.98], [0.8, 0.8, 0.8]),
19 "white": (np.ones(3),),
21 COLOR_PRESETS = DEFAULT_COLOR_PROFILES.copy()
25 """Check whether the geometry object contains a Mesh supported by MeshCat""" 26 if geometry_object.meshPath ==
"":
29 _, file_extension = os.path.splitext(geometry_object.meshPath)
30 if file_extension.lower()
in [
".dae",
".obj",
".stl"]:
37 import meshcat.geometry
as mg
39 if isinstance(mesh, hppfcl.HeightFieldOBBRSS):
40 heights = mesh.getHeights()
41 x_grid = mesh.getXGrid()
42 y_grid = mesh.getYGrid()
43 min_height = mesh.getMinHeight()
45 X, Y = np.meshgrid(x_grid, y_grid)
50 num_cells = (nx) * (ny) * 2 + (nx + ny) * 4 + 2
55 faces = np.empty((num_tris, 3), dtype=int)
60 X.reshape(num_vertices),
61 Y.reshape(num_vertices),
62 heights.reshape(num_vertices),
68 X.reshape(num_vertices),
69 Y.reshape(num_vertices),
70 np.full(num_vertices, min_height),
78 for y_id
in range(ny):
79 for x_id
in range(nx):
80 p0 = x_id + y_id * (nx + 1)
85 faces[face_id] = np.array([p0, p3, p1])
87 faces[face_id] = np.array([p3, p2, p1])
91 p0_low = p0 + num_vertices
92 p1_low = p1 + num_vertices
94 faces[face_id] = np.array([p0, p1_low, p0_low])
96 faces[face_id] = np.array([p0, p1, p1_low])
100 p2_low = p2 + num_vertices
101 p3_low = p3 + num_vertices
103 faces[face_id] = np.array([p3, p3_low, p2_low])
105 faces[face_id] = np.array([p3, p2_low, p2])
109 p0_low = p0 + num_vertices
110 p3_low = p3 + num_vertices
112 faces[face_id] = np.array([p0, p3_low, p3])
114 faces[face_id] = np.array([p0, p0_low, p3_low])
118 p1_low = p1 + num_vertices
119 p2_low = p2 + num_vertices
121 faces[face_id] = np.array([p1, p2_low, p2])
123 faces[face_id] = np.array([p1, p1_low, p2_low])
129 p2 = 2 * num_vertices - 1
132 faces[face_id] = np.array([p0, p1, p2])
134 faces[face_id] = np.array([p0, p2, p3])
137 elif isinstance(mesh, (hppfcl.Convex, hppfcl.BVHModelBase)):
138 if isinstance(mesh, hppfcl.BVHModelBase):
139 num_vertices = mesh.num_vertices
140 num_tris = mesh.num_tris
142 call_triangles = mesh.tri_indices
143 call_vertices = mesh.vertices
145 elif isinstance(mesh, hppfcl.Convex):
146 num_vertices = mesh.num_points
147 num_tris = mesh.num_polygons
149 call_triangles = mesh.polygons
150 call_vertices = mesh.points
152 faces = np.empty((num_tris, 3), dtype=int)
153 for k
in range(num_tris):
154 tri = call_triangles(k)
155 faces[k] = [tri[i]
for i
in range(3)]
157 if LooseVersion(hppfcl.__version__) >= LooseVersion(
"1.7.7"):
158 vertices = call_vertices()
160 vertices = np.empty((num_vertices, 3))
161 for k
in range(num_vertices):
162 vertices[k] = call_vertices(k)
164 vertices = vertices.astype(np.float32)
167 mesh = mg.TriangularMeshGeometry(vertices, faces)
171 vertices.T, color=np.repeat(np.ones((3, 1)), num_vertices, axis=1)
173 mg.PointsMaterial(size=0.002),
181 import meshcat.geometry
as mg
186 [1.0, 0.0, 0.0, 0.0],
187 [0.0, 0.0, -1.0, 0.0],
188 [0.0, 1.0, 0.0, 0.0],
189 [0.0, 0.0, 0.0, 1.0],
192 RotatedCylinder =
type(
193 "RotatedCylinder", (mg.Cylinder,), {
"intrinsic_transform":
lambda self: R}
196 geom = geometry_object.geometry
197 if isinstance(geom, hppfcl.Capsule):
198 if hasattr(mg,
"TriangularMeshGeometry"):
201 obj = RotatedCylinder(2.0 * geom.halfLength, geom.radius)
202 elif isinstance(geom, hppfcl.Cylinder):
203 obj = RotatedCylinder(2.0 * geom.halfLength, geom.radius)
204 elif isinstance(geom, hppfcl.Cone):
205 obj = RotatedCylinder(2.0 * geom.halfLength, 0, geom.radius, 0)
206 elif isinstance(geom, hppfcl.Box):
207 obj = mg.Box(
npToTuple(2.0 * geom.halfSide))
208 elif isinstance(geom, hppfcl.Sphere):
209 obj = mg.Sphere(geom.radius)
210 elif isinstance(geom, hppfcl.ConvexBase):
213 msg =
"Unsupported geometry type for %s (%s)" % (
214 geometry_object.name,
217 warnings.warn(msg, category=UserWarning, stacklevel=2)
224 nbv = np.array([
max(radial_resolution, 4),
max(cap_resolution, 4)])
228 vertices = np.zeros((nbv[0] * (2 * nbv[1]) + 2, 3))
229 for j
in range(nbv[0]):
230 phi = (2 * np.pi * j) / nbv[0]
231 for i
in range(nbv[1]):
232 theta = (np.pi / 2 * i) / nbv[1]
233 vertices[position + i, :] = np.array(
235 np.cos(theta) * np.cos(phi) * r,
236 np.cos(theta) * np.sin(phi) * r,
237 -h / 2 - np.sin(theta) * r,
240 vertices[position + i + nbv[1], :] = np.array(
242 np.cos(theta) * np.cos(phi) * r,
243 np.cos(theta) * np.sin(phi) * r,
244 h / 2 + np.sin(theta) * r,
247 position += nbv[1] * 2
248 vertices[-2, :] = np.array([0, 0, -h / 2 - r])
249 vertices[-1, :] = np.array([0, 0, h / 2 + r])
250 indexes = np.zeros((nbv[0] * (4 * (nbv[1] - 1) + 4), 3))
253 last = nbv[0] * (2 * nbv[1]) + 1
254 for j
in range(nbv[0]):
255 j_next = (j + 1) % nbv[0]
256 indexes[index + 0] = np.array(
257 [j_next * stride + nbv[1], j_next * stride, j * stride]
259 indexes[index + 1] = np.array(
260 [j * stride + nbv[1], j_next * stride + nbv[1], j * stride]
262 indexes[index + 2] = np.array(
263 [j * stride + nbv[1] - 1, j_next * stride + nbv[1] - 1, last - 1]
265 indexes[index + 3] = np.array(
266 [j_next * stride + 2 * nbv[1] - 1, j * stride + 2 * nbv[1] - 1, last]
268 for i
in range(nbv[1] - 1):
269 indexes[index + 4 + i * 4 + 0] = np.array(
270 [j_next * stride + i, j_next * stride + i + 1, j * stride + i]
272 indexes[index + 4 + i * 4 + 1] = np.array(
273 [j_next * stride + i + 1, j * stride + i + 1, j * stride + i]
275 indexes[index + 4 + i * 4 + 2] = np.array(
277 j_next * stride + nbv[1] + i + 1,
278 j_next * stride + nbv[1] + i,
279 j * stride + nbv[1] + i,
282 indexes[index + 4 + i * 4 + 3] = np.array(
284 j_next * stride + nbv[1] + i + 1,
285 j * stride + nbv[1] + i,
286 j * stride + nbv[1] + i + 1,
289 index += 4 * (nbv[1] - 1) + 4
290 import meshcat.geometry
292 return meshcat.geometry.TriangularMeshGeometry(vertices, indexes)
296 """A Pinocchio display using Meshcat""" 299 FRAME_VEL_COLOR = 0x00FF00
305 "preset1": [np.zeros(3), [1.0, 1.0, 1.0]],
306 "preset2": [[0.0, 0.0, 0.6], [0.8, 1.0, 1.2]],
307 "acrobot": [[0.0, 0.1, 0.0], [0.5, 0.0, 0.2]],
308 "cam_ur": [[0.4, 0.6, -0.2], [1.0, 0.4, 1.2]],
309 "cam_ur2": [[0.4, 0.3, 0.0], [0.5, 0.1, 1.4]],
310 "cam_ur3": [[0.4, 0.3, 0.0], [0.6, 1.3, 0.3]],
311 "cam_ur4": [[-1.0, 0.3, 0.0], [1.3, 0.1, 1.2]],
312 "cam_ur5": [[-1.0, 0.3, 0.0], [-0.05, 1.5, 1.2]],
313 "talos": [[0.0, 1.2, 0.0], [1.5, 0.3, 1.5]],
314 "talos2": [[0.0, 1.1, 0.0], [1.2, 0.6, 1.5]],
318 """Return the name of the geometry object inside the viewer.""" 319 if geometry_type
is pin.GeometryType.VISUAL:
321 elif geometry_type
is pin.GeometryType.COLLISION:
324 def initViewer(self, viewer=None, open=False, loadModel=False):
325 """Start a new MeshCat server and client. 326 Note: the server can also be started separately using the "meshcat-server" command in a terminal: 327 this enables the server to remain active after the current script ends. 332 self.
viewer = meshcat.Visualizer()
if viewer
is None else viewer
348 self, preset_name="gray"
350 """Set the background.""" 351 col_top, col_bot = COLOR_PRESETS[preset_name]
356 self.
viewer.set_cam_target(target)
359 self.
viewer.set_cam_pos(position)
362 """Set the camera angle and position using a given preset.""" 369 elt.set_property(
"zoom", zoom)
382 import meshcat.geometry
385 if geometry_object.meshPath ==
"":
386 msg =
"Display of geometric primitives is supported only if pinocchio is build with HPP-FCL bindings." 387 warnings.warn(msg, category=UserWarning, stacklevel=2)
391 _, file_extension = os.path.splitext(geometry_object.meshPath)
392 if file_extension.lower() ==
".dae":
393 obj = meshcat.geometry.DaeMeshGeometry.from_file(geometry_object.meshPath)
394 elif file_extension.lower() ==
".obj":
395 obj = meshcat.geometry.ObjMeshGeometry.from_file(geometry_object.meshPath)
396 elif file_extension.lower() ==
".stl":
397 obj = meshcat.geometry.StlMeshGeometry.from_file(geometry_object.meshPath)
399 msg =
"Unknown mesh file format: {}.".format(geometry_object.meshPath)
400 warnings.warn(msg, category=UserWarning, stacklevel=2)
406 """Load a single geometry object""" 407 import meshcat.geometry
413 if WITH_HPP_FCL_BINDINGS
and isinstance(
414 geometry_object.geometry, hppfcl.ShapeBase
417 elif isMesh(geometry_object):
418 obj = self.
loadMesh(geometry_object)
420 elif WITH_HPP_FCL_BINDINGS
and isinstance(
421 geometry_object.geometry,
422 (hppfcl.BVHModelBase, hppfcl.HeightFieldOBBRSS),
424 obj =
loadMesh(geometry_object.geometry)
427 "The geometry object named " 428 + geometry_object.name
429 +
" is not supported by Pinocchio/MeshCat for vizualization." 431 warnings.warn(msg, category=UserWarning, stacklevel=2)
435 except Exception
as e:
436 msg =
"Error while loading geometry object: %s\nError message:\n%s" % (
437 geometry_object.name,
440 warnings.warn(msg, category=UserWarning, stacklevel=2)
443 if isinstance(obj, meshcat.geometry.Object):
444 self.
viewer[viewer_name].set_object(obj)
445 elif isinstance(obj, meshcat.geometry.Geometry):
446 material = meshcat.geometry.MeshPhongMaterial()
449 meshColor = geometry_object.meshColor
453 int(meshColor[0] * 255) * 256**2
454 + int(meshColor[1] * 255) * 256
455 + int(meshColor[2] * 255)
458 if float(meshColor[3]) != 1.0:
459 material.transparent =
True 460 material.opacity = float(meshColor[3])
461 self.
viewer[viewer_name].set_object(obj, material)
464 scale = list(np.asarray(geometry_object.meshScale).flatten())
465 self.
viewer[viewer_name].set_property(
"scale", scale)
468 """Load the robot in a MeshCat viewer. 470 rootNodeName: name to give to the robot in the viewer 471 color: optional, color to give to the robot. This overwrites the color present in the urdf. 472 Format is a list of four RGBA floats (between 0 and 1) 481 for collision
in self.collision_model.geometryObjects:
488 for visual
in self.visual_model.geometryObjects:
492 def reload(self, new_geometry_object, geometry_type=None):
493 """Reload a geometry_object given by its name and its type""" 494 if geometry_type == pin.GeometryType.VISUAL:
495 geom_model = self.visual_model
497 geom_model = self.collision_model
498 geometry_type = pin.GeometryType.COLLISION
500 geom_id = geom_model.getGeometryId(new_geometry_object.name)
501 geom_model.geometryObjects[geom_id] = new_geometry_object
503 self.
delete(new_geometry_object, geometry_type)
504 visual = geom_model.geometryObjects[geom_id]
510 def delete(self, geometry_object, geometry_type):
515 """Display the robot at configuration q in the viewer by placing all the bodies.""" 517 pin.forwardKinematics(self.model, self.data, q)
526 if geometry_type == pin.GeometryType.VISUAL:
527 geom_model = self.visual_model
528 geom_data = self.visual_data
530 geom_model = self.collision_model
531 geom_data = self.collision_data
533 pin.updateGeometryPlacements(self.model, self.data, geom_model, geom_data)
534 for visual
in geom_model.geometryObjects:
537 M = geom_data.oMg[geom_model.getGeometryId(visual.name)]
540 scale = np.asarray(visual.meshScale).flatten()
541 S = np.diag(np.concatenate((scale, [1.0])))
542 T = np.array(M.homogeneous).dot(S)
547 self.
viewer[visual_name].set_transform(T)
553 self.
viewer[visual_name].set_transform(T)
556 """Add a visual GeometryObject to the viewer, with an optional color.""" 561 if not hasattr(self.
viewer,
"get_image"):
563 "meshcat.Visualizer does not have the get_image() method." 564 " You need meshcat >= 0.2.0 to get this feature." 568 """Capture an image from the Meshcat viewer and return an RGB array.""" 569 if w
is not None or h
is not None:
571 img = self.
viewer.get_image(w, h)
573 img = self.
viewer.get_image()
574 img_arr = np.asarray(img)
578 """Set whether to display collision objects or not.""" 579 if self.collision_model
is None:
589 """Set whether to display visual objects or not.""" 590 if self.visual_model
is None:
600 self, frame_id, v_scale=0.2, color=FRAME_VEL_COLOR
602 pin.updateFramePlacement(self.model, self.data, frame_id)
603 vFr = pin.getFrameVelocity(
604 self.model, self.data, frame_id, pin.LOCAL_WORLD_ALIGNED
606 line_group_name =
"ee_vel/{}".format(frame_id)
608 [v_scale * vFr.linear], [frame_id], [line_group_name], [color]
618 """Draw vectors extending from given frames.""" 619 import meshcat.geometry
as mg
621 if len(vecs) != len(frame_ids)
or len(vecs) != len(vec_names):
623 "Number of vectors and frames IDs or names is inconsistent." 625 for i, (fid, v)
in enumerate(zip(frame_ids, vecs)):
626 frame_pos = self.data.oMf[fid].translation
627 vertices = np.array([frame_pos, frame_pos + v]).astype(np.float32).T
629 geometry = mg.PointsGeometry(position=vertices)
630 geom_object = mg.LineSegments(
631 geometry, mg.LineBasicMaterial(color=colors[i])
634 self.
viewer[prefix].set_object(geom_object)
637 __all__ = [
"MeshcatVisualizer"]
def drawFrameVelocities(self, frame_id, v_scale=0.2, color=FRAME_VEL_COLOR)
result_of::push_front< V const, T >::type append(T const &t, V const &v)
Append the element T at the front of boost fusion vector V.
def getViewerNodeName(self, geometry_object, geometry_type)
def _check_meshcat_has_get_image(self)
def reload(self, new_geometry_object, geometry_type=None)
def enableCameraControl(self)
def setCameraPosition(self, position)
AD< Scalar > max(const AD< Scalar > &x, const AD< Scalar > &y)
def displayCollisions(self, visibility)
def disableCameraControl(self)
def setCameraPreset(self, preset_key)
def delete(self, geometry_object, geometry_type)
def _draw_vectors_from_frame(self, vecs, frame_ids, vec_names, colors)
def setCameraZoom(self, zoom)
def loadViewerGeometryObject(self, geometry_object, geometry_type, color=None)
def createCapsule(length, radius, radial_resolution=30, cap_resolution=10)
def updatePlacements(self, geometry_type)
def setCameraPose(self, pose=np.eye(4))
def setBackgroundColor(self, preset_name="gray")
def loadViewerModel(self, rootNodeName="pinocchio", color=None)
def addGeometryObject(self, obj, color=None)
def initViewer(self, viewer=None, open=False, loadModel=False)
def loadPrimitive(geometry_object)
def isMesh(geometry_object)
def displayVisuals(self, visibility)
def display(self, q=None)
def setCameraTarget(self, target)
def loadMesh(self, geometry_object)
dictionary CAMERA_PRESETS
def captureImage(self, w=None, h=None)