-
Notifications
You must be signed in to change notification settings - Fork 418
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
Modify sub node copy constructor to create new node parameters object #1796
Conversation
2a2b3e9
to
225d8aa
Compare
You've probably tried this, but do you observe the expected behavior from #1778 and #731 (comment)? |
6c1c0c4
to
975fc83
Compare
@aprotyas I haven't tried but I believe I was mistaken originally when I made that issue. At the time I was confused by some behavior I was seeing and thought that it was due to that but i'm pretty sure I was just confused. Especially since in the code spinning operates on the node base interface which nodes and their sub nodes share |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if i am not mistaken, sub-nodes share all resource with parent node but only namespace is extended and propagated accordingly. apparently this fix is to allocate the own resources for sub-node, so i am not sure if this is the path we take?? 🤔 any comments ?
* \throws rclcpp::exceptions::InvalidParameterTypeException if the parameter | ||
* was created as statically typed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch ❗
There is probably a design discussion to be had here because I'm not sure if @jlack1987 can implement what this PR aims to do without creating a different resource for the subnode. |
@fujitatomoya I believe you are mistaken, unless i'm mistaken that is 😄 the change here preserves all of the resources shared between nodes except for the pointer to the It seems like this is the way it should behave based on the discussions here. Are there perhaps some additional unit tests you can think of that I could implement that would help show that this either does or does not result in the desired behavior and/or results in some other regression? |
@jlack1987 thanks for the comment!
this is what i mean. if this is true, what about the topics and services? (sorry i am not sure either, but it should be consistent behavior for all entities?) btw, this fix solves #731 (comment)? (I think it does not.) |
@fujitatomoya topics and services stay the same here(i'll write some more unit tests to show that today). And as far as the comments you referenced here, the short answer is that it kinda sorta fixes it 😜. It does fix the behavior as far as the interaction with the param server from the users frame of reference goes, but the way it fixes it does not match how topics/services are handled via simple namespace extension; however, after getting into the code I personally don't believe appending/prepending namespaces to the params is a good/clean way to fix that. I'll try to explain my reasoning on that. First though if you haven't please check out this simple test to see how the user interacts with parameters from sub-nodes. Currently(on released code and The way I did it was to create a new |
I'm pretty sure there isn't a "correct" answer to this since #731 says |
@aprotyas i'm referring to how it behaves with the change in this PR. I honestly don't have a preference one way or another whether params are accessible from other subnodes or parent nodes. I'm currently migrating a large code base from ros1->ros2 and I either have to fix the param behavior for sub nodes in ros2 or not use ros2 params 😭 so i'm very invested in fixing this however you all believe it should be done. Based on the user interaction that I get from the way I have fixed it here, I think what I have here works, but I get it if it's not what you all want, so I'm open to changing the implementation |
I think it is clear that subnodes are currently under-specified, so one other way out of this conundrum is to not use subnodes. That is, you may not want to get bogged down in fully specifying subnodes at this point, but you definitely want to use parameters; they are the right way forward. If you describe what you are using subnodes for, maybe we can suggest another way to do things. Of course, if you want to fully specify subnodes, then that is great too. I think at that point we'd want to move this to a design discussion instead. |
803e56b
to
3673fd7
Compare
Yeah @clalancette i'll try to describe the conundrum i'm in from a code structure perspective. A representative example would ultimately be that I have classes that create classes that create classes and so on and so on for as long as you'd like to go. At every step of the way, the classes use the node handle given to them to obtain their configuration, and for the classes they create they pass it along downstream where more namespace branches are created for others to retrieve their configuration. At the end of the day everybody looks on their specific branch of the namespace tree to get their configuration, subscribe to and advertise their topics/services/actions etc. I then end up having 2 threads, one non-realtime thread that calls Fast forward to me trying to update this to ros2 and the way I configure the stack won't work anymore bc if I create two classes that have params of the same name and pass them sub nodes then there is a conflict, and this is something that happens at least hundreds of times in my code base. I thought perhaps as a work around I could just have all these classes create their own top level node, but then there's the issue of having hundreds of execution contexts. I guess for that i'd have to pass an executor all around my code base and manually add all the nodes to it, and i'm not even sure how well that would work for the nodes and their parameters. Here's a simple chunk of code that I think shows my plight Keep in mind this is a silly chunk of code that wouldn't even compile, but I think it should display the type of problem i'm running into with using parameters in converting from ros1 to ros2. I'm gonna rely on you all reading between the lines a bit here with this example but I think you'll get it 😄 // Some basic PID controller class to show how it could be configured from rosparam
class PID
{
PID(rclcpp::NodeSharedPtr node)
{
// create another leaf in the namespace tree, and grab your PID configuration parameters from it.
// These calls would fail after the first PID construction with the current behavior of sub node params, bc
// they would be duplicate parameters
node_ = node->create_sub_node("pid");
node_->declare_param("kp");
node_->declare_param("kd");
node_->declare_param("ki");
}
rclcpp::NodeSharedPtr node_;
};
// simple joint struct
class Joint
{
Joint(const std::string name) : name_(name)
{
}
double position_, velocity_, effort_;
std::string name_;
};
// Controller class that aggregates PID's
class Controller
{
Controller(rclcpp::NodeSharedPtr node, std::vector<JointsPtr> joints)
{
joints_ = joints;
// iterate through the joints, creating a PID for each joint
for(auto& joint : joints)
{
// make a PID for each joint, further branching the namespace out to the joint name
pids_->push_back(std::make_shared<PID>(node->create_sub_node(joint->name)))
}
}
std::vector<JointsPtr> joints_;
std::vector<PIDPtr> pids_;
};
// "wholebody" controller that splits out control of subsystems
class WholeBody
{
WholeBody() : node_(new rclcpp::Node("whole_body"))
{
}
// joints vector here contains all joints on a bot
bool init(std::vector<JointsPtr> joints)
{
// assume I separate out the joints for each controller
left_arm_joints = ...
right_arm_joints = ...
left_leg_joints = ...
right_leg_joints = ...
head_joints = ...
//
// create some sub-system controllers, branching out the namespace tree to each one
controllers_.push_back(Controller(node_->create_sub_node("left_arm"), left_arm_joints));
controllers_.push_back(Controller(node_->create_sub_node("right_arm"), right_arm_joints));
controllers_.push_back(Controller(node_->create_sub_node("left_leg") left_leg_joints));
controllers_.push_back(Controller(node_->create_sub_node("right_leg"), right_leg_joints));
controllers_.push_back(Controller(node_->create_sub_node("head"), head_joints));
// and so on...
}
rclcpp::NodeSharedPtr node_;
my_ns::ControllerPtr controllers_;
}; |
d992f20
to
e647d51
Compare
@jlack1987 @aprotyas @clalancette thanks for constructive opinion, appreciate it. atm, i was considering that rclcpp/rclcpp/include/rclcpp/node.hpp Lines 1236 to 1244 in d04319a
but as you mentioned, there are some area we need to discuss.
probably sub-nodes should not, but parent should? |
@fujitatomoya is there any chance we could split the discussion? Yes there are design discussion things like you are asking about sub node param visibility that perhaps could go in other tickets, but there's also the situation of parameters for sub nodes being nearly unusable which is broken core functionality that, if it's acceptable to you all, i'd like to address as soon as we can all agree on a fix |
Signed-off-by: Jordan Lack <jlack@houstonmechatronics.com> Add unit test for sub node subscription take Signed-off-by: Jordan Lack <jlack@houstonmechatronics.com>
e647d51
to
754e5ca
Compare
I am still inclined to think that subordinate node manages the namespace expansion internally. but i understand that you have concrete problem, so if that is major opinion, i am okay to go. any advice? @aprotyas @clalancette @wjwwood @ivanpauno @iuhilnehc-ynos |
@fujitatomoya I actually initially implemented the fix this way, but I realized quickly that namespacing parameters sorta doesn't make sense because it would mean the internally stored name of the parameter would have a bunch of namespaces in it which would be problematic when calling a users Think about this case,
Additionally, when one of these params changes, the users |
isn't that |
@fujitatomoya I went ahead and implemented most of the "extend namespace internally" implementation so I could display the behavior and why I think it's not the best approach. Best way to look is to look at my test. So first I make a sub node and a sub node on that sub node here. The end behavior is largely the same between the two implementations until we get to [adding on_set_parameters_callback callbacks] to the nodes. Looking then at the next line where I set subsubnode's parameter. When that is set, it calls the callback for both the other subnode and the parent nodes callbacks and notifies them that While this may seem like a minor annoyance for this simple test, for a large node that has dozens of sub nodes that all have sub nodes of their own and so on, it would mean possibly hundreds of callbacks would be hit every time any one of those subnodes parameters changes and in almost all of those callbacks they're probably being notified of a parameter change on a parameter that they have no knowledge even exists bc it was created in another sub node somewhere else in the tree of nodes/subnodes. In addition, the name of the parameter that changed would have to have the entire sub namespace prepended to it when the callback is called otherwise there'd be no way of differentiating between a parameter "a" that exists in multiple sub nodes. I hope this makes sense. Let me know what you think! |
@jlack1987 yeah, i think that can be a problem for user experience. thanks for the explanation. |
btw, this fix changes the current behavior for |
@fujitatomoya yeah you are correct. I found a similar-ish issue where the |
@fujitatomoya I think i'm at the end of my road here. I don't fully understand all the code enough to say for sure, but it seems that I can't make sub node params show up with If anybody has any advice on a possible implementation to fix this issue i'm open to any suggestions 🙏 |
for allowing us to do that, i think eventually we need to create
this is true. we can have discussion in MW WG, if you'd like to bring the topic. https://docs.ros.org/en/rolling/Governance.html#middleware, always welcome! |
Sorry, I need to catch on this (long) thread to make a more thoughtful response, but my general feedback after skimming, is that a "sub-node" should completely be syntactic sugar on top of a Node, and to that end, it shouldn't require any additional entities at the rcl level (like additional node objects etc...). Additionally, I think it shouldn't require new instances of Any other issues, like number of callbacks to process when there are lots of parameters, or separation of concern when listing parameters, should be address some other way other than having multiple places where we can store parameters within a node. This is all my professional opinion about it, but I could be wrong and/or convinced otherwise. Hopefully I can find more time to iterate with you guys on it soon. |
Added "more-information-needed" as a stand-in for "needs more discussion". |
Yeah I agree with you @wjwwood, initially I thought there was a simple solution to the problem and implemented said solution but upon further digging it became clear that solving it is much more difficult than I thought. My only workaround right now is a painful one but it's all I can come up with, and it's to name parameters with their full namespace in the param file. Something like,
Obviously this isn't great bc I have hundreds of parameters and some very long namespaces but I don't see any other solution. |
Also, feel free to close this MR whenever as it's not a complete solution |
i still think this is the way it is. it just takes care of namespace on top of parent node. |
You are correct I think with the exception of parameters. With respect to sub nodes, the behavior of parameters is not intuitive and not the way they "should" behave in my opinion. I know ros2 is different from ros1, but there's a really nice mechanic from ros1 that we're obviously trying to mimic here and that's
and the current state of things does give the desired mechanics for topics and services, but not for parameters. |
For context, I did not know about subnodes until I read this thread. I've also been struggling with the idea of how to namespace parameters for different subsystems in a node and then handle reconfiguring those subsystems when a parameter is dynamically updated. The approach I've been working on so far is to have a single object that I pass around to each subsystem to handle the dynamic reconfiguring of subsystems. Each subsystem then registers callbacks for parameter updates to this dynamic reconfigure object. This dynamic reconfigure object has the one One large downside to my approach is that each subsystem has to have a namespace it is initialized with and uses for declaring and getting parameters. It also uses this namespace to register a callback for when any parameter in its namespace is changed. So far I have not had to deal with sub-subsystems. I've done this so far by just having one level of namespaces for subsystems. Having features built into ROS for handling parameters of subsystems within a node would be really nice. |
@fujitatomoya do you know if this was discussed in a MW WG meeting? |
i remember that we had a quick talk but not really discussed, probably it would be nice to bring this back to topic. |
Gonna glose this MR as the approach is ultimately flawed. I have linked to this PR though in the issue for which this PR attempts to fix so others can find their way here and see the discussion which I think could be useful to others. |
My attempt at fixing #731. The tldr of the change here is when a sub node is created, the pointer to the new node creates a new pointer to a
NodeParameters
object instead of being given the "parent" nodes pointer. This results in the following behavior change from parameters,add_on_set_parameters_callback
call, it will only be triggered when a parameter registered to that particular node or sub node is modified, i.e. ifnode_a
has a sub nodesub_node_a
and a parameter ofsub_node_a
is modified, callbacks registered tonode_a
will not be calledallow_undeclared_parameters
will apply to any sub nodesI feel like the fix to this issue can't be this simple; however, I have been unable to find any test failures or problems that result from this change. I did add unit tests that failed prior to making this modification and then pass after the change.
Signed-off-by: Jordan Lack jlack@houstonmechatronics.com