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

Update external wrench plugin with mutiple wrenches and improve rpc responses #418

Merged
merged 12 commits into from
Apr 16, 2019
Merged

Update external wrench plugin with mutiple wrenches and improve rpc responses #418

merged 12 commits into from
Apr 16, 2019

Conversation

yeshasvitirupachuri
Copy link
Member

Following the limitations of the externalWrench documented in #417 this PR updated the plugin with new functionalities.

@traversaro
Copy link
Member

Can we find a reviewer in the lab? (Perhaps @lrapetti?) I am a bit short of time till next week.

@lrapetti lrapetti self-requested a review March 29, 2019 07:43
@lrapetti lrapetti assigned lrapetti and unassigned lrapetti Apr 1, 2019
@lrapetti
Copy link
Member

lrapetti commented Apr 1, 2019

Looks Good To Me,
@traversaro I think we can merge.

@lrapetti
Copy link
Member

lrapetti commented Apr 3, 2019

It would be better to also create a README.md for the externawrench plugin (for example like the one that can be found for the linkattacher).

@traversaro
Copy link
Member

I have added a few comments to the PR as well. For future PR, can you use human understandable PR titles, in place of the one automatically generated by github from the branch names? Thanks!

Copy link
Member

@traversaro traversaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments.
Bonus points: what happens if the simulation is reset while a force is being applied? See

for an example on how to deal with the reset event.

@@ -0,0 +1,83 @@
#ifndef EXTERNALWRENCH_H
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a less ambiguous header guard (something like GAZEBO_YARP_PLUGINS_EXTERNALWRENCH_H) to avoid possible collisions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, please add a copyright notice, something like:

/*
 * Copyright (C) 2019 Istituto Italiano di Tecnologia (IIT)
 * All rights reserved.
 *
 * CopyPolicy: Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
 */

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

m_visPub = this->m_node->Advertise<msgs::Visual>("~/visual",100);

// Set the visual's name. This should be unique.
std::string visual_name = "__" + wrenchPtr->link_name + "__CYLINDER_VISUAL__" + boost::lexical_cast<std::string>(count);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid the unnecessary dependency on boost by just using C++11's std::to_string .
If the identifier needs to be unique, I suggest to add a prefix unique to this plugin, such as GYP_EXT_WRENCH_ , that can be useful if somebody needs to understand the nature of a given visual.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

m_lock.lock();
// new-command flag
command.addInt32(1);
command.addInt(1);
Copy link
Member

@traversaro traversaro Apr 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, addInt32 is the suggested method to use. Why revert back to addInt ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not the suggested method to use

not the suggested method or is the suggested method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, is the suggested method, I fixed the comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
model_has_link = false;

//srand(boost::lexical_cast<float>(std::time(NULL)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

double duration;
};

std::unique_ptr<wrenchCommand> wrenchPtr{new wrenchCommand()};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused. Why are you using std::unique_ptr here?
Why not a simple attribute of the class:

  wrenchCommand wrench;

or even better, given that the structure wrenchCommand is not used elsewhere, an anonymous struct:

   struct
   {
        std::string              link_name;
        yarp::sig::Vector        force;
        yarp::sig::Vector        torque;
        double                   duration;
   } wrench;

or given that the structure is never used standalone, why not simply including the attributes of the structure as attributes of the class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@traversaro I remember back in time starting out with simple data structures without pointers but then I quickly ran into some problems while handling dynamic wrench addition and removal in multiple wrench operation mode. That is why I belive I migrated to using pointer to handle them correctly. Now, I tested to change back to simple data structures again and in the multiple wrench case the visuals are funny looking

ezgif com-video-to-gif

But with the pointers usage it is stable

ezgif com-video-to-gif (1)

At the moment I prefer not to investigate it further and I would like to stick to the pointers usage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You obtain this behavior just by changing std::unique_ptr<wrenchCommand> wrenchPtr{new wrenchCommand()}; to wrenchCommand wrench;? If that is the case, it is extremely strange and I do not know what's going on.

If instead you obtain this because you changed boost::shared_ptr<ExternalWrench> newWrench(new ExternalWrench); to ExternalWrench newWrench and other changes related to ExternalWrench, I guess what is going on is that there is something going on related to the use of static count, that is increased whenever a new copy of ExternalWrench is created, but not when a new copy of the shared_ptr is created. This is exactly the tricky behavior related to static variables (that are actually just global variables a bit disguised) that I was mentioning in https://github.com/robotology/gazebo-yarp-plugins/pull/418/files#r273442326 .
I think the plugin will be much more maintainable if you just drop the static count and just have a normal local index or count attribute that can be set with a SetIndex or SetCount method, that can contain all the code currently contained in the constructor. This SetIndex/SetCount method can be set directly in the https://github.com/robotology/gazebo-yarp-plugins/pull/418/files#diff-6f34d47dcd5558cbef158050b5563c76R175 , using some kind of variable shared between RPCServerThread and ApplyExternalWrench that is increase/decreased appropriately.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is after changing std::unique_ptr<wrenchCommand> wrenchPtr{new wrenchCommand()}; to wrenchCommand wrench; and also boost::shared_ptr< std::vector< boost::shared_ptr<ExternalWrench>>> wrenchesVectorPtr{new std::vector< boost::shared_ptr<ExternalWrench>>()}; to std::vector<ExternalWrench> wrenchesVector. Anyway, I do agree that static count variable will eventually cause problems when having multiple models having the plugin. So, I will have a variable wrenchesIndex in ApplyExternalWrench class and set the wrenchIndex variable in ExternalWrench class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static count is removed and updated with new wrench count and index variables ada8291

Shared points are removed and simple data structures are used b7d1d38

double tick;
double tock;

bool model_has_link;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variables does not seem to be ever read, but just write too. Can it be removed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

model_has_link = false;

//srand(boost::lexical_cast<float>(std::time(NULL)));
srand(rand()%100);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C random functions should never be used in modern C++, as they implicitly modify the global state of the program, that as you may imagine is not ideal. Furthermore, what is the rationale of seeding the random generator with the last value of the random generator divided by 100?

In general, especially in a simulation program, I would never use randomness unless the seed of the used random generator is exposed to the user, otherwise you make the simulation output (that in a sense includes also the visualization!) non reproducible. You may think this is not a huge problem, but imagine that you have a series of screen for a paper, and you need to reproduce exactly it again but in an higher resolution for some reason: this would be impossible with this implementation.

To keep it simple, I think the best option is just to assign the color based on the count variable: either you initialize a local random generator that you seed with count for getting the color components, or you simply keep a table of a few colors (for example 20) and you assign colors using colors[count%20].

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either you initialize a local random generator that you seed with count for getting the color components

@traversaro what you mean by "local random generator" ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like:

std::mt19937 gen(42);
std::uniform_real_distribution<> dis(0.0, 1.0);
for(int i=0; i < 10; i++)
{
    std::cerr << "Random " << i << " : " << dis(gen) << std::endl;
}

Given that you seed it with a fixed number (42) the output of this function it is deterministic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated color random generation a4323de

ezgif com-video-to-gif (2)

wrenchPtr->duration = 0.0;

count++;
tick = yarp::os::Time::now();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid that in general this will not give you the expected duration of the external force, unless gzserver is executed with the YARP_CLOCK=/clock env variable and with the gazebo_yarp_clock plugin. If one of this conditions is not true, yarp::os::Time::now() will return you the system time, resulting in a duration of the external force in simulation that may be much shorter than expected (depending on the RTF) and even worse a duration that is not reproducible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper solution to this is to add a const gazebo::common::UpdateInfo& _info argument to the applyWrenches method, and use it to retrieved the simulation time step. See other onUpdate methods in the repo to get an inspiration https://github.com/robotology/gazebo-yarp-plugins/search?q=onUpdate&unscoped_q=onUpdate .

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time handling is updated 64199f6

void setNewCommandFlag(std::int32_t flag);
virtual void onStop();

boost::shared_ptr< std::vector< boost::shared_ptr<ExternalWrench>>> wrenchesVectorPtr{new std::vector< boost::shared_ptr<ExternalWrench>>()};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we use a good old std::vector<ExternalWrench>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #418 (comment)

{
private:

static int count;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything seems to be ok from what I understand, but static variables are always a bit tricky do deal with.
I don't think this needs to block the PR, but I think you can obtain something similar by setting the count to the in ExternalWrench constructor as you build it in the plugin class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@traversaro I am not following you exactly. The count variable is actually increased in the constructor of ExternalWrench

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but what happens if there are two different externalwrench plugins in the same model? As far as I was able to see, everything should work fine, but keeping count as a static variable may lead to some subtle bugs if this code is modified in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if there are two different externalwrench plugins in the same model?

At the moment this is not possible to do by a user as there will be port address conflict while trying to open the rpc port with the model name. So, I believe the usage of count as static is good in this case.

Copy link
Member

@traversaro traversaro Apr 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what about two different externalwrench plugins in two different model that are spawned together? The count would be shared also in this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While writing https://github.com/robotology/gazebo-yarp-plugins/pull/418/files#r273692453 , I am a bit confused by the count logic even in the case of the single plugin. Let's say that three wrenches are added, and they get the count 1, 2 and 3 , where 1 and 3 have a duration of 100 seconds, and 2 instead has a duration of 1 seconds. After the the wrench 2 is deleted, the count counter is decremented to 2. If (let's say after 10 seconds) a new wrench is added, the count counter is increased again, and so the new wrench gets a count of 3, that is a index that is already used by the wrench 3, and the visual representation will probably conflict!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conflict will happen for the visuals. So, it is better to not decrease the count of the wrenches after the duration is done. It is better to keep an increasing wrenchCount variable in ApplyExternalWrench and set the wrenchIndex variable of ExternalWrench class. This way each new wrench will have a different color without conflicts.

@yeshasvitirupachuri yeshasvitirupachuri changed the title Feature/update external wrench Update external wrench plugin with mutiple wrenches and improve rpc responses Apr 4, 2019
@yeshasvitirupachuri
Copy link
Member Author

It would be better to also create a README.md for the externawrench plugin (for example like the one that can be found for the linkattacher).

README file added 7366f2f

@yeshasvitirupachuri
Copy link
Member Author

yeshasvitirupachuri commented Apr 9, 2019

Some comments.
Bonus points: what happens if the simulation is reset while a force is being applied? See

gazebo-yarp-plugins/plugins/controlboard/src/ControlBoardDriver.cpp

Line 341 in 01ad1d1

this->m_resetConnection =
for an example on how to deal with the reset event.

On resetting gazebo, the wrenches are still applied as provided by the user commands. Do you think the wrenches should be cleared? @traversaro

@traversaro
Copy link
Member

On resetting gazebo, the wrenches are still applied as provided by the user commands. Do you think the wrenches should be cleared? @traversaro

My understanding of reset is that the simulation should restart from the initial condition. If a user asked for an external force of 1 N on one link for one hour, I think after a reset, the force should be cleared. Think for example of an RL scenario: would it make sense to keep the old force?

@yeshasvitirupachuri
Copy link
Member Author

yeshasvitirupachuri commented Apr 9, 2019

On resetting gazebo, the wrenches are still applied as provided by the user commands. Do you think the wrenches should be cleared? @traversaro

My understanding of reset is that the simulation should restart from the initial condition. If a user asked for an external force of 1 N on one link for one hour, I think after a reset, the force should be cleared. Think for example of an RL scenario: would it make sense to keep the old force?

Agreed!
Added onReset() method 2ddd481. On gazebo world reset, all the existing wrenches are cleared and the external wrench plugin is set to default single wrench operation mode.

ezgif com-video-to-gif (3)

@yeshasvitirupachuri
Copy link
Member Author

@traversaro any update on this ?

@traversaro
Copy link
Member

@traversaro any update on this ?

Sorry @Yeshasvitvs , I do not get notifications for new commits pushed in the PR, and even if I got them I have no way to know if all the review comments has been addressed. From your comment I imagine that the PR is ready for a new review round, but for the future feel free to comment on the PR when the review comments has been addressed, so the reviewer can check the new code.

@yeshasvitirupachuri
Copy link
Member Author

All the previous reviews comments are address in the recent commits 😉 please check them and let me know if there are more changes needed. Thanks @traversaro

@traversaro
Copy link
Member

There are some comments below, but nothing blocking the merge. Do you want to cleanup the commits or otherwise I will squash the commits as part of the merge.

Now the ExternalWrench::setWrenchColor function will take more and more time as simulation goes on, am I right. This is probably not desirable, especially if the wrenchIndex will grow a lot (something like ~10000 or similar).

@yeshasvitirupachuri
Copy link
Member Author

@traversaro Now the ExternalWrench::setWrenchColor function will take more and more time as simulation goes on

yes that is the way it is coded now

This is probably not desirable, especially if the wrenchIndex will grow a lot (something like ~10000 or similar)

Yes but it is an extreme case. Can you think of any other way to handle the uniform distribution through ApplyExternalWrench class ??

@traversaro
Copy link
Member

Yes but it is an extreme case. Can you think of any other way to handle the uniform distribution through ApplyExternalWrench class ??

std::mt19937 rand_gen(wrench_index);
std::uniform_real_distribution<> uni_dis(0.0, 1.0)

@yeshasvitirupachuri
Copy link
Member Author

Yes but it is an extreme case. Can you think of any other way to handle the uniform distribution through ApplyExternalWrench class ??

std::mt19937 rand_gen(wrench_index);
std::uniform_real_distribution<> uni_dis(0.0, 1.0)

@traversaro updated color handling using wrench index as seed c4da8fb

@traversaro
Copy link
Member

Ok, do you want to cleanup the commits? Otherwise I will squash the commits as part of the merge.

@yeshasvitirupachuri
Copy link
Member Author

@traversaro please take care of the commits while merging. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants