From Mesh, into MeshManager, via SDF to a model in a world: how?
Howdy folks,
I am trying to get to the bottom of how exactly a mesh is loaded in Gazebo.
I am working on a Gazebo Plugin that generates trees procedurally such that I can create a forest where each tree's geometry/shape is unique (more or less).
I have figured out how to convert a procedurally generated tree to a Mesh, and then add this Mesh to MeshManager:
gazebo::common::Mesh * treeMesh = procTreeFunction();
treeMesh->SetName("Tree_1234");
gazebo::common::MeshManager::Instance()->AddMesh(treeMesh);
But what I am currently struggling with what to do next in order to see this tree in my Gazebo world, and what follows is questionable and is what I need some help with.
So next I am creating an SDF model string:
modelStr << "<sdf version='" << SDF_VERSION << "'>"
"<model name='" Tree_1234 "'>"
"<pose>" << pose << "</pose>"
"<link name='link'>"
"<velocity_decay>"
"<linear>0.01</linear>"
"<angular>0.01</angular>"
"</velocity_decay>"
"<inertial><mass>" << _mass << "</mass>"
"<inertia>"
"<ixx>" << Ixx << "</ixx>"
"<iyy>" << Iyy << "</iyy>"
"<izz>" << Izz << "</izz>"
"<ixy>" << 0.0 << "</ixy>"
"<ixz>" << 0.0 << "</ixz>"
"<iyz>" << 0.0 << "</iyz>"
"</inertia>"
"</inertial>"
"<collision name='collision'>"
"<geometry>"
"<mesh>"
"<uri>" Tree_1234 "</uri>"
"</mesh>"
"</geometry>"
"</collision>"
"<visual name='visual'>"
"<geometry>"
"<mesh>"
"<uri>" Tree_1234 "</uri>"
"</mesh>"
"</geometry>"
"<material>"
"<ambient>" << _materialAmbient[0] << " " << _materialAmbient[1] << " " << _materialAmbient[2] << " " << _materialAmbient[3] <<"</ambient>"
"<diffuse>" << _materialDiffuse[0] << " " << _materialDiffuse[1] << " " << _materialDiffuse[2] << " " << _materialDiffuse[3] <<"</diffuse>"
"<specular>" << _materialSpecular[0] << " " << _materialSpecular[1] << " " << _materialSpecular[2] << " " << _materialSpecular[3] <<"</specular>"
"<emissive>" << _materialEmissive[0] << " " << _materialEmissive[1] << " " << _materialEmissive[2] << " " << _materialEmissive[3] <<"</emissive>"
"</material>"
"</visual>"
"</link>"
"</model>"
"</sdf>";
And then I add the model to my world
sdf::SDF sdfDescription.SetFromString(modelStr.str());
_parent->InsertModelSDF(sdfDescription);
However, instead of seeing a tree, I am seeing a cube (that appears to be a unit cube of 1 m^3). The cuboid is rendered in the correct material settings and at the right location. It's just not a tree :)
Does anyone have an idea what I might be doing wrong?
Thanks in advance!!
Galto
Asked by Galto2000 on 2017-11-11 21:17:30 UTC
Answers
The reason why unit cubes show up, where one would expect the procedurally generated mesh to be, is because the mesh is produced in the server, and when it's added to the MeshManager in the server, that's where it's going to stay - in the server.
The client doesn't have access to an instance of the MeshManager singleton from the server, since it runs on a separate process.
The server, however, publishes a message with the SDF information. When the client receives this SDF message from the server, it then looks for a file called "Tree_1234" that is supposed to have the mesh information (more in particularly, in collada format), but it can't find it - since there is no file called "Tree_1234" and by default it generates a unit cube instead.
The simplest solution is to save off the mesh to a file first - as shown here:
gazebo::common::Mesh * treeMesh = procTreeFunction();
treeMesh->SetName("Tree_1234");
gazebo::common::MeshManager::Instance()->Export(treeMesh, "Tree_1234", std::string("dae"), false);
and then refer to this file in the SDF uri field as follows (and note the .dae extension):
modelStr << "<sdf version='" << SDF_VERSION << "'>"
"<model name='" Tree_1234 "'>"
"<pose>" << pose << "</pose>"
"<link name='link'>"
"<velocity_decay>"
"<linear>0.01</linear>"
"<angular>0.01</angular>"
"</velocity_decay>"
"<inertial><mass>" << _mass << "</mass>"
"<inertia>"
"<ixx>" << Ixx << "</ixx>"
"<iyy>" << Iyy << "</iyy>"
"<izz>" << Izz << "</izz>"
"<ixy>" << 0.0 << "</ixy>"
"<ixz>" << 0.0 << "</ixz>"
"<iyz>" << 0.0 << "</iyz>"
"</inertia>"
"</inertial>"
"<collision name='collision'>"
"<geometry>"
"<mesh>"
"<uri>" Tree_1234.dae "</uri>"
"</mesh>"
"</geometry>"
"</collision>"
"<visual name='visual'>"
"<geometry>"
"<mesh>"
"<uri>" Tree_1234.dae "</uri>"
"</mesh>"
"</geometry>"
"<material>"
"<ambient>" << _materialAmbient[0] << " " << _materialAmbient[1] << " " << _materialAmbient[2] << " " << _materialAmbient[3] <<"</ambient>"
"<diffuse>" << _materialDiffuse[0] << " " << _materialDiffuse[1] << " " << _materialDiffuse[2] << " " << _materialDiffuse[3] <<"</diffuse>"
"<specular>" << _materialSpecular[0] << " " << _materialSpecular[1] << " " << _materialSpecular[2] << " " << _materialSpecular[3] <<"</specular>"
"<emissive>" << _materialEmissive[0] << " " << _materialEmissive[1] << " " << _materialEmissive[2] << " " << _materialEmissive[3] <<"</emissive>"
"</material>"
"</visual>"
"</link>"
"</model>"
"</sdf>";
This works just fine, but the downside is that it will produce tons of meshes, one for each tree. Maybe not a big issue for some, maybe a huge inconvenience for others.
For the latter, I would suggest a more advanced method (and a more elegant one in my opinion) where one would create a second plugin that runs on the client and which subscribes to a customized "mesh-messages" - a message that should include all the vertices, normals and uv-coordinates of a mesh - that the first plugin (i.e. the one that runs on the server) is going to publish.
Here is a screen shot of procedurally generated trees that are loaded in Gazebo using the former method (saving off each mesh to a .dae file) - this is still a work in progress. Each tree is the location of an actual tree in the world that was recorded by placing markers in Google Earth and then parsing the KML file.
Asked by Galto2000 on 2017-11-20 21:21:37 UTC
Comments
Thanks for sharing your solution! This is the first time I see the collada exporter being used :)
Asked by chapulina on 2017-11-20 21:33:17 UTC
Hi, thanks for sharing ! I will definitely use it to create random instances of forests ;)
I wonder if you kept working on it and if you have something else that you can share that improve your forest generation ? Thanks again !
Asked by TTDM on 2018-04-17 11:23:53 UTC
The posted method here only addresses a specific sub-problem - that of how spawn geometry that was computed (rather than loaded in) in Gazebo. I have not made public my code but I can tell you that I made use of proctree (https://github.com/jarikomppa/proctree/tree/master/proctree ) for creating the geometry procedurally. Since a couple of months ago I moved over to Blender for simulating my sensors. I'll eventually migrate back to Gazebo when details have been worked out.
Asked by Galto2000 on 2018-04-17 12:40:24 UTC
Reading it more attentively, It is indeed not what I want since i can precompute 10 mesh and then randomly choose between those. My comment was mainly motivated by your "still a work in progress" remark, in case you published something and the impossibility to send a PM ;) Anyway, Thanks a lot for answering and sharing proctree that i might use ;)
Asked by TTDM on 2018-04-17 13:25:41 UTC
Thanks for the interest and good luck
Asked by Galto2000 on 2018-04-17 13:36:33 UTC
Comments
It's awesome that you're doing this! Do you see any errors when running in verbose mode? Anything about not finding the geometry?
Asked by chapulina on 2017-11-12 22:54:19 UTC
Thanks for your suggestion.
So, when I run it in verbose mode I get a whole bunch of these:
[Err] [Visual.cc:2726] No mesh specified [Err] [Visual.cc:2366] No mesh found, setting mesh to a unit box
One for each procedural object that I pushed in a MeshManager instance from my plugin. Otherwise it's clean.
Alright, I'll keep on digging... but any clues, ideas, advice....are always greatly appreciated
Asked by Galto2000 on 2017-11-13 18:56:45 UTC
Looking at the code in Visual.cc (Gazebo 7.1) - it seems when it encounters geometry that is tagged as "mesh", it automatically presumes that this is a mesh that was loaded from a file (e.g. collada). In my case, I am generating the mesh from code in my plugin and then I give it a name, e.g. "Tree_1234, and then I push it in the MeshManager. Then it subsequently is looking for a file called "Tree_1234" in Visual.cc, but it doesn't exist as a file, and as such turns it in unit box. Could it be?
Asked by Galto2000 on 2017-11-13 19:46:08 UTC
What you write in the SDF URI is supposed to be a file. There may be hacky ways to get it to work with programmatically generated meshes but that is not the intended use. I haven't looked enough at the code to suggest hacky ways of doing it, but I'd be trying not to use SDF.
Asked by chapulina on 2017-11-13 21:08:18 UTC
The hack that I am currently am contemplating is to simply generate an .STL file for each procedurally generated object. It's far from ideal, but if the objects are static it just means a little bit of waiting at the start (and perhaps checking if the model file already exists as to avoid re-generating meshes). What alternatives are there to SDF? Thanks!
Asked by Galto2000 on 2017-11-13 21:21:11 UTC
The alternative I'd look at is trying to reproduce whatever
InsertModelSDF
is doing, but using your own mesh instead. Makes sense? Also note that if you're creating the mesh on the server, the client has no way of knowing about it.Asked by chapulina on 2017-11-13 21:51:52 UTC
a-ha :) (I just had my little aha moment there, thank you!) .... So what you are suggesting of doing is to boldly go beyond writing a plugin, and actually editing some of the core Gazebo code? What parts of the code constitutes server and client code in the repository?
Asked by Galto2000 on 2017-11-13 22:11:52 UTC
It would be ideal if you could do it from a plugin, I'd hope changing the source code wouldn't be necessary. The reason why the client wouldn't know about a mesh you create on the server is that they run on separate processes. With mesh files, both server and client know where to look for them and they are loaded twice. I don't think there is a trivial way for you to tell the client about a mesh created programmatically.
Asked by chapulina on 2017-11-14 00:38:53 UTC
So we basically need to be able to send a Mesh object from server to client via some IPC method. Could this be done using any of the plugin types (sytem, visual, gui, ...?). Now I wonder in what parts plugins operate in: in the server or client, or both?
Asked by Galto2000 on 2017-11-14 10:47:06 UTC
World, Model, Sensor plugins act on the server, Visual plugins have one instance on the server (so cameras can see it) and another on the client. GUI plugins are client-only. System plugins can run on both, I believe, but as separate instances of the plugin. I've never tried using IPC like that
Asked by chapulina on 2017-11-14 12:01:23 UTC
With IPC I meant in general "inter process communication" (as opposed to sharing a common file). I believe Gazebo is using messages to pass information from the server to the client (like the URI to the mesh file name), so in a server-plugin we can publish a "Mesh message", and have the client-plugin subscribe to it.
Asked by Galto2000 on 2017-11-14 12:44:12 UTC
I see, I supposed you could try creating a custom message for that and figuring out a way to encode the mesh info into it
Asked by chapulina on 2017-11-14 12:55:38 UTC
I guess that's the long term plan. I need to get something working now, so I'll first go with saving each generated mesh into a file, and then passing the uri of the file as I have pretty much written the plugin for this already. When that is working and I am happy with it, I'll make the plugin code public. For the sake of this thread - shall I answer my own question with a summary of this discussion?
Asked by Galto2000 on 2017-11-14 14:24:49 UTC
Answering sounds good, good luck!
Asked by chapulina on 2017-11-14 15:47:33 UTC
Thanks for your help and great insights!
Asked by Galto2000 on 2017-11-14 19:58:53 UTC
Show us a screenshot when you're done ;)
Asked by chapulina on 2017-11-14 20:05:50 UTC
I added one in my answer below
thanks again for your help!
Asked by Galto2000 on 2017-11-22 15:32:46 UTC
Nice! Glad to see it worked out!
Asked by chapulina on 2017-11-22 15:54:59 UTC