Add controller dependency graph for ::AUTO switch mode#2578
Conversation
aeea81a to
9c9ef20
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #2578 +/- ##
==========================================
+ Coverage 89.31% 89.35% +0.04%
==========================================
Files 145 145
Lines 16541 16931 +390
Branches 1396 1426 +30
==========================================
+ Hits 14773 15129 +356
- Misses 1231 1249 +18
- Partials 537 553 +16
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Co-authored-by: Christoph Fröhlich <christophfroehlich@users.noreply.github.com>
|
This PR is stale because it has been open for 45 days with no activity. Please tag a maintainer for help on completing this PR, or close it if you think it has become obsolete. |
|
This pull request is in conflict. Could you fix it @saikishor? |
|
This PR is stale because it has been open for 45 days with no activity. Please tag a maintainer for help on completing this PR, or close it if you think it has become obsolete. |
christophfroehlich
left a comment
There was a problem hiding this comment.
Oops, I found an unfinished review here. What is the current state of this feature?
| pal_statistics::RegistrationsRAII stats_registrations_; | ||
|
|
||
| public: | ||
| using SharedPtr = std::shared_ptr<ControllerInterfaceBase>; |
There was a problem hiding this comment.
should we use RCLCPP_SHARED_PTR_DEFINITIONS and RCLCPP_WEAK_PTR_DEFINITIONS from "rclcpp/macros.hpp"?
PeterMitrano
left a comment
There was a problem hiding this comment.
Very excited about this feature and eventually fulling implementing FORCE_AUTO! I wouldn't say that I looked closely enough for this to be a "review", but I just wanted to point out a few small things, including one of the tests not making sense to me. Thanks for all the effort here!
| graph.add_dependency(E, F); | ||
|
|
||
| EXPECT_THAT( | ||
| graph.get_dependencies_to_activate("A"), |
There was a problem hiding this comment.
kind of surprising that these include the node itself. in this case "A", and "B" in the case below 🤔
There was a problem hiding this comment.
Yeah, it is not so intuitive I suppose, but yes, it gives the whole chain with dependencies
|
|
||
| EXPECT_THAT( | ||
| graph.get_dependencies_to_activate("A"), | ||
| testing::UnorderedElementsAre("A", "B", "C", "D", "E", "F")); |
There was a problem hiding this comment.
why E and F? am I understanding this correctly?
graph LR;
A-->B;
B-->C;
C-->D;
E-->B;
E-->F;
There was a problem hiding this comment.
Yes,
Inorder to activate A, we need to activate B and B needs E and E needs F, that's why. It was quite complicated while implementing it
There was a problem hiding this comment.
But according to the diagram, B does not need E. Rather, E needs B, but that's irrelevant for activating A.
Am I misunderstanding this?
There was a problem hiding this comment.
I am imagining it like this: Let's say B is a low level joint level controller, and A offers Cartesian control and B offers trajectory control, both on top of E. If I activate both at the same time that would be a resource conflict. So if I activate A, then we can't activate E also.
There was a problem hiding this comment.
Hello!
That's not how it works
Let's say, B exports 5 interfaces and then A takes 3 of those and E takes 2 of them. If you are activating A, then as it needs B to activate, but B cannot be left with half of the interfaces taken right?, so it'll have to also activate E
Did i explain myself?
If not, I can give a longer explanation when I get access to my PC
There was a problem hiding this comment.
Sorry I got "B" and "E" reversed in the statement "B offers trajectory control, both on top of E" above.
But anyways, ok let's assume that A and E do not claim the same interfaces, but rather difference ones. Even then I'm not sure I agree about leaving unclaimed interfaces. In my experience it's perfectly fine to have unclaimed interfaces. From B's perspective, it just won't be receiving any valid commands on that interface, which seems fine to me.
Thanks for taking the time to explain! I am interested in contributing more to ros controls repos :)
There was a problem hiding this comment.
Hello!
We cannot leave unclaimed interfaces because we cannot know what reference to take to do the control strategy. it depends on the situation though
There was a problem hiding this comment.
True, but usually that's indicated with "inf". At least , in the hardware interfaces I've implemented that's how it works. But ok, I understand your point.
Maybe I just need to think of get_dependencies_to_activate as "here are all the controllers we might need to activate". Because there will need to be some filter for the case where A and E are in conflict, because they try to claim the same interfaces, right?
There was a problem hiding this comment.
Sorry for coming late to this discussion.
Have you considered a case where there's two possible controllers in the place corresponding to E in the example above? If two different controllers can potentially claim the same interfaces from B how can you go about picking which to activate?
I think supporting unclaimed interfaces is a must here, since the only unequivocal dependencies are from A to B, B to C, etc., whereas B to E may be ambiguous as stated above.
Consider the parallelism with hardware interfaces themselves. There's usually no problem with running them with no controller claiming their interfaces, is there? Should this not be handled the same way?
| EXPECT_THAT(graph.get_dependencies_to_activate("D"), testing::UnorderedElementsAre("D")); | ||
| EXPECT_THAT( | ||
| graph.get_dependencies_to_activate("E"), | ||
| testing::UnorderedElementsAre("E", "F", "B", "C", "D", "A")); |
| // controllers can be activated together with the current controller. | ||
|
|
||
| mutually_exclusive_predecessor_groups.clear(); | ||
| const auto are_all_reference_interfaces_found = |
There was a problem hiding this comment.
this and the are_all_command_interfaces_found lambdas seem to have no captures and do essentially the same thing. Maybe it could be pulled out into one function at file scope?
| void get_controllers_to_activate(std::vector<std::string> & controllers_to_activate) const | ||
| { | ||
| // Check the predecessors of the controller and check if they belong to the controller's state | ||
| // interfaces If they do, add them to the list of controllers to activatestate_itf |
| size_t successors_reference_interfaces_count = 0; | ||
| for (const auto & successor : successors) | ||
| { | ||
| successors_reference_interfaces_count += successor->reference_interfaces.size(); |
There was a problem hiding this comment.
maybe I missed it, but this variable doesn't seem to be used anywhere else?
| EXPECT_THAT(graph.get_dependencies_to_activate("D"), testing::UnorderedElementsAre("D")); | ||
| EXPECT_THAT( | ||
| graph.get_dependencies_to_activate("E"), | ||
| testing::UnorderedElementsAre("E", "F", "B", "C", "D", "A")); |
|
This PR is stale because it has been open for 45 days with no activity. Please tag a maintainer for help on completing this PR, or close it if you think it has become obsolete. |
This PR aims to add the things needed for the ::AUTO switch mode and addresses few other things.
With the ::AUTO switch mode, you can activate and deactivate the whole chain
graph LR; A-->B; B-->C; C-->D; D-->E;Here, you can activate the whole chain, activating controller A alone and deactivate the whole chain deactivating the controller E
graph LR; A-->B; B-->C; B-->D; D-->E;Here, you can activate the whole chain, activating controller A alone and deactivate the whole chain deactivating the controller E and C
Activating controller D will only activate D and E
Activating controller B will activate B, C, D and E