Receiving Meshes via ROS in Python
I am using Voxblox_ros
(https://github.com/ethz-asl/voxblox) which is a real-time mesh generator, and it saves a .ply
file under a folder.
What I want to achieve is to read the mesh that is saved under that folder. I've looked it up and found that there is a message type called shape_msgs/Mesh
, which sounded like something I could use. However, I am having question marks in my head about how exactly to fill in the data to this message.
I created a msg (MeshInfo.msg
):
string mesh_id
shape_msgs/Mesh part_mesh
and a srv(MeshArray.srv
):
---
MeshInfo[] mesh_array
which I refer in the code as:
self.mesh_srv = rospy.Service("voxblox_ros/generate_mesh", MeshArray, self.mesh_processor_srv) # get the mesh
I've come up with this kind of code which I have not tested yet, just wanted to illustrate what I have in mind:
def mesh_processor_srv(self, req):
# this service does not return anything by itself, it only saves the mesh to the designated folder
# so, one must implement a mechanism here to import the mesh which is saved in that folder
rospy.wait_for_service('voxblox_ros/generate_mesh')
MeshArrayResponse = rospy.ServiceProxy('voxblox_ros/generate_mesh', MeshArray)
# navigate to voxblox_ros package, under mesh_results, there should be mesh file stored as .ply
files = os.listdir(home + "/ros_ws/src/voxblox/voxblox_ros/mesh_results/") # get the mesh files
meshlist = [] # to keep the meshes in
# loop over each mesh, import and create our mesh object
for i in range(len(files)):
mesh = MeshInfo()
mesh.mesh_id = "mesh_" + os.path.splitext(os.path.basename(files[i]))[0] # get the mesh name from the file
mesh.part_mesh = PlyData.read(home + "/ros_ws/src/voxblox/voxblox_ros/mesh_results/" + files[i]) # get the mesh data
meshlist.append(mesh)
response = MeshArrayResponse()
response.mesh_array = meshlist
return response
which I am not sure of. Did someone have similar task and had somehow figured out how to crack?
Asked by Jägermeister on 2019-08-06 09:40:05 UTC
Answers
A solution using existing things could be to read the .ply into a pcl::PolygonMesh
(see this or this) and then convert it like it's done here.
Another solution, since the message already exists, would be to copy the triangles into the shape_msgs::Mesh
. Iirc a better idea, which I've used before, is assimp, not sure if it can load .ply meshes, but if it can, the package geometric_shapes comes to the rescue with its geometric_shapes::createMeshFromResource
. Edit2: I was missing a piece in the explanation: shapes::constructMsgFromShape
converts the Mesh*
to a variant of shape_msgs::Mesh
. The code would be almost the same as in q245995.
Edit1: ply seems supported as per this, it might be worth a try.
Asked by aPonza on 2019-08-06 10:53:08 UTC
Comments
I need a Python solution, therefore I guess the PCL solution is out of option. PCL-Python
is a very unstable release that is not working in many ways, and not supported properly. On another look, all these are C++ libraries/packages, or?
Asked by Jägermeister on 2019-08-07 03:57:37 UTC
Everything is C++, you're correct. I didn't think python was a requirement, not sure then...
Asked by aPonza on 2019-08-07 04:08:38 UTC
My mistake, included in the title now, thank you though, perhaps your answer can guide those who need a C++ solution to the same problem.
Asked by Jägermeister on 2019-08-07 04:10:15 UTC
pyassimp apparently reads 3D file formats like ply
and stl
, and there is a usage example in the MoveIt planning scene interface that is probably helpful:
def makeMesh(self, name, pose, filename):
if not use_pyassimp:
rospy.logerr('pyassimp is broken on your platform, cannot load meshes')
return
scene = pyassimp.load(filename)
if not scene.meshes:
rospy.logerr('Unable to load mesh')
return
mesh = Mesh()
for face in scene.meshes[0].faces:
triangle = MeshTriangle()
if len(face.indices) == 3:
triangle.vertex_indices = [face.indices[0],
face.indices[1],
face.indices[2]]
mesh.triangles.append(triangle)
for vertex in scene.meshes[0].vertices:
point = Point()
point.x = vertex[0]
point.y = vertex[1]
point.z = vertex[2]
mesh.vertices.append(point)
pyassimp.release(scene)
o = CollisionObject()
o.header.stamp = rospy.Time.now()
o.header.frame_id = self._fixed_frame
o.id = name
o.meshes.append(mesh)
o.mesh_poses.append(pose)
o.operation = o.ADD
return o
Asked by fvd on 2020-05-23 11:22:10 UTC
Comments