Gazebo | Ignition | Community
Ask Your Question
0

From Mesh, into MeshManager, via SDF to a model in a world: how?

asked 2017-11-11 20:17:30 -0600

Galto2000 gravatar image

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

edit retag flag offensive close merge delete

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?

chapulina gravatar imagechapulina ( 2017-11-12 21:54:19 -0600 )edit

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

Galto2000 gravatar imageGalto2000 ( 2017-11-13 17:56:45 -0600 )edit

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?

Galto2000 gravatar imageGalto2000 ( 2017-11-13 18:46:08 -0600 )edit

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.

chapulina gravatar imagechapulina ( 2017-11-13 20:08:18 -0600 )edit

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!

Galto2000 gravatar imageGalto2000 ( 2017-11-13 20:21:11 -0600 )edit

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.

chapulina gravatar imagechapulina ( 2017-11-13 20:51:52 -0600 )edit

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?

Galto2000 gravatar imageGalto2000 ( 2017-11-13 21:11:52 -0600 )edit

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.

chapulina gravatar imagechapulina ( 2017-11-13 23:38:53 -0600 )edit

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?

Galto2000 gravatar imageGalto2000 ( 2017-11-14 09:47:06 -0600 )edit

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

chapulina gravatar imagechapulina ( 2017-11-14 11:01:23 -0600 )edit

1 Answer

Sort by ยป oldest newest most voted
1

answered 2017-11-20 20:21:37 -0600

Galto2000 gravatar image

updated 2017-11-22 14:31:00 -0600

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.

image description

edit flag offensive delete link more

Comments

Thanks for sharing your solution! This is the first time I see the collada exporter being used :)

chapulina gravatar imagechapulina ( 2017-11-20 20:33:17 -0600 )edit

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 !

TTDM gravatar imageTTDM ( 2018-04-17 11:23:53 -0600 )edit
1

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.

Galto2000 gravatar imageGalto2000 ( 2018-04-17 12:40:24 -0600 )edit

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 ;)

TTDM gravatar imageTTDM ( 2018-04-17 13:25:41 -0600 )edit

Thanks for the interest and good luck

Galto2000 gravatar imageGalto2000 ( 2018-04-17 13:36:33 -0600 )edit

Question Tools

2 followers

Stats

Asked: 2017-11-11 20:17:30 -0600

Seen: 1,234 times

Last updated: Nov 22 '17