gazebosim / gz-sim

Open source robotics simulator. The latest version of Gazebo.
https://gazebosim.org
Apache License 2.0
708 stars 269 forks source link

Add ECM tutorial #628

Open peci1 opened 3 years ago

peci1 commented 3 years ago

Desired behavior

Have a tutorial explaining how to work with Entities and Components to newcomers.

Alternatives considered

Have people doing random things that sometimes work :-D I've looked in the tutorials folder, but there's nothing resembling an ECM tutorial (except the terminology tutorial which contains one-sentence explanations of the terms).

Implementation suggestion

I can't help creating the tutorial. I can only comment whether the new tutorial helped me understand the concepts better.

Additional context

As a newbie to the ECM system used here, it is extremely difficult for me to get a concept on how to work with all the ECM "magic". There's a lot of stuff happening automatically which I don't understand. I think if you want external collaborators to improve Gazebo, such a tutorial is really needed a lot.

For example, I don't understand why the following code doesn't crash:

Entity joint = ...;
auto comp = _ecm.Component<components::JointPosition>(joint);
if (!comp)
{
  _ecm.CreateComponent(joint, components::JointPosition());
  comp = _ecm.Component<components::JointPosition>(joint);
}

std::cout << comp->Data[0] << std::endl;

So, I initialize the component with a default-constructed components::JointPosition component, which should be backed by a vector of doubles. I expect that a default-constructed vector is empty, so I'd also expect the component to contain the empty list. But why does accessing the first element of the component data work right after the if? Is it because the component always exists before this plugin is created, so the if never executes, or is it because of some auto-magic of the ECM that would automatically connect the created component to what already exists in the simulation and figure out "hey, this is a revolute joint, so it's position component should always be a 1-vector"?

Further questions I came up with and are difficult for me to answer:

I think questions like this are very important to be clearly answered so that people can write safe and working code. The earlier such a tutorial is created, the more community contributions you can expect :)

chapulina commented 3 years ago

Thanks, it's helpful to know what questions developers need answered while working with the code. I'll add a link to https://github.com/ignitionrobotics/ign-gazebo/issues/83, which also has several good questions.

adlarkin commented 3 years ago

I'm adding a few other things to document/outline regarding ECM (and more generally, entity/component) usage for current/stable releases, which at this point covers Citadel through Edifice:

jrutgeer commented 1 year ago

@peci1 @adlarkin We're over two years further, but not much has changed unfortunately. Neither this nor https://github.com/gazebosim/gz-sim/issues/83 lead to any further action (that I am aware of at least), wrt clarifying the core concepts.

I can't help creating the tutorial. I can only comment whether the new tutorial helped me understand the concepts better.

I assume in the mean time you might have a better understanding of above mentioned concepts? So maybe you're fit for writing that tutorial now? :-)

Or, given writing a tutorial is a non-trivial amount of work: would you have any info that you can share (even if it's some old notes or even just a scan of handwritten notes or whatever) that can help to better understand the core concepts? Or, maybe you can answer your own questions above by now?

I think this is very much true:

think questions like this are very important to be clearly answered so that people can write safe and working code. The earlier such a tutorial is created, the more community contributions you can expect :)


For anyone interested in the answer to the JointPosition example:

I didn't fully dig into it, but I think the initialization is done here, in the physics plugin. So that implies that you can't use JointPosition without the entity being a joint and using the physics plugin (unless you write an own plugin that initializes it). However, above code (changed to comp->Data()[0]), does give a segmentation fault for me. So there's probably no magic afterall. ;-) The physics joint is created here.

azeey commented 1 year ago

I think the concept that is not documented is that the Physics system updates/populates some components by default but some other components are not updated unless the user (plugin author) has created that component on an entity. This is mainly done for performance reasons so that the Physics system can avoid costly component updates if no one is using them. In your JointPosition example, https://github.com/gazebosim/gz-sim/blob/42c285ef1a7b35d7e64c8fcdc4f270fb2b42b9e3/src/systems/physics/Physics.cc#L3613 is checking if the entity has the JointPosition component. If so, the Physics system will resize the underlying data structure to the right size based on the degrees of freedom of the joint. It then updates the values based on the results from the physics engine.

So systems like the JointStatePublisher are responsible for creating the components they need the Physics system to populate: https://github.com/gazebosim/gz-sim/blob/42c285ef1a7b35d7e64c8fcdc4f270fb2b42b9e3/src/systems/joint_state_publisher/JointStatePublisher.cc#L118-L130

With all that being said, there are also convenience classes that do this for you, e.g. The Joint class has a EnablePositionCheck member function that creates the necessary components. The goal is for these convenience classes to be the main interface for plugin authors and interacting with the ECM directly would be an advanced use case.

I agree all of this needs to be documented well, but I hope that was helpful.

jrutgeer commented 1 year ago

@azeey Thank you for the info, this sure does help.

Suddenly these common comments make sense! :-)


I think the concept that is not documented

Well... there are several more concepts that are very difficult to learn from scratch. E.g.:

L-Aleksandr commented 9 months ago

Has any tutorial or more detailed ECM documentation appeared in the meantime ? Unfortunately I have not found any information about the point where the ECM graph is fully constructed. It seems that in the system plugin connected with model in the Configure() function ECM contains only entities that appeared in the world file before the model with plugin. Only in PreUpdate() the ECM graph is complete. Is this a correct behavior or a bug? It is also interesting to know if it is possible to have a complete ECM graph in Configure().

jrutgeer commented 9 months ago

Any system can add and remove entities and components at any time, so there is in general no distinct point where the ECM is "complete".

A common pattern is to set a bool configured_ to true if the configure() step is successful, and then in the pre/update/post step: if(configured_) do the rest of the configuration and set another flag. And only if that flag is also true, do the actual work that the plugin should do.

See e.g. here for an example.

azeey commented 9 months ago

https://github.com/gazebosim/gz-sim/pull/2207 has a little more detail as to which entities are available when Configure is called. Quoting from there:

Note that when Configure() is called, all the elements in the parent element of the plugin have been loaded. For example, if the plugin is attached to a <model>, all the elements in that <model> would have been loaded. Similarly for <world>. However, if you need to access entities outside the plugin's parent element, they may not have finished loading at the time the plugin's Configure() is called. Then you may need to access those entities later, in *Update()

traversaro commented 9 months ago

fyi @xela-95, this is similar to what we discussed last week.