Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ECM tutorial #628

Open
peci1 opened this issue Feb 12, 2021 · 9 comments
Open

Add ECM tutorial #628

peci1 opened this issue Feb 12, 2021 · 9 comments
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted We accept pull requests!

Comments

@peci1
Copy link
Contributor

peci1 commented Feb 12, 2021

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:

  • When can I assume a component surely exists?
  • When should I check for its existence?
  • How should I create the component if it doesn't exist (is default-construction always the right way?).
  • Who creates the components if not me in the plugin?
  • When does it happen relative to plugin/system Configure/PreUpdate/PostUpdate methods?
  • Why is ECM const in PostUpdate?
    • the create_system_plugins tutorial helps here a bit, but really in short
    • the example plugins are pretty unhelpful here because they don't do anything but couting some text, which doesn't mean anything for a newbie

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

@peci1 peci1 added the enhancement New feature or request label Feb 12, 2021
@chapulina
Copy link
Contributor

Thanks, it's helpful to know what questions developers need answered while working with the code. I'll add a link to #83, which also has several good questions.

@chapulina chapulina self-assigned this Feb 12, 2021
@chapulina chapulina added the documentation Improvements or additions to documentation label Feb 12, 2021
@adlarkin adlarkin self-assigned this May 3, 2021
@adlarkin
Copy link
Contributor

adlarkin commented May 5, 2021

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:

  • When a default server configuration is used (user commands, physics, and scene broadcaster system plugins), what entities and components are created by default, and how are they created?
  • What's the difference between using the ECM and SdfEntityCreator? When should one be used over the other? (this could also be related to the default server configuration note above)
  • What entities and components are created by system plugins? (perhaps this information can be documented in system header files)
  • Should components that are used periodically (either frequently or infrequently) be added and removed from components, or "zeroed out"? If the answer is "it depends", which scenarios require which components to be used in a particular way?

@jrutgeer
Copy link
Contributor

@peci1 @adlarkin
We're over two years further, but not much has changed unfortunately.
Neither this nor #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
Copy link
Contributor

azeey commented May 24, 2023

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,

_ecm.Each<components::Joint, components::JointPosition>(
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:

// Create joint position component if one doesn't exist
if (!_ecm.EntityHasComponentType(_joint,
components::JointPosition().TypeId()))
{
_ecm.CreateComponent(_joint, components::JointPosition());
}
// Create joint velocity component if one doesn't exist
if (!_ecm.EntityHasComponentType(_joint,
components::JointVelocity().TypeId()))
{
_ecm.CreateComponent(_joint, components::JointVelocity());
}

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
Copy link
Contributor

@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.:

  • General knowledge about the Entity/Component pattern. E.g. it is clear that an entity is just an identifier and components hold the data, but it is not straightforward to realize that components can be added to entities by one system and initialized/updated by other systems,
  • General overview: which systems use which components, where are those components created, where they are initialized, updated, removed, etc. Refer also to Define the component API of each system #83.
  • The interaction of gz-sim and gz-physics through the physics system,
  • Similar interactions of gz-sim with the other libraries (e.g. rendering),
  • The general concepts of the physics subsystem,
  • Etc.

@L-Aleksandr
Copy link

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
Copy link
Contributor

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
Copy link
Contributor

azeey commented Jan 24, 2024

#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
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request help wanted We accept pull requests!
Projects
None yet
Development

No branches or pull requests

7 participants