Support lifecycle node - NodeInterfaces#352
Conversation
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
@mikeferguson If you have some time I also appreciate your review. I don't know if we should deprecate the old API, what do you think? |
There was a problem hiding this comment.
Thanks for tackling this! Regarding the offer to add me as a co-author: Thanks, I would appreciate this. You can simply use the e-mail address I used for signing-off the other commits.
As a summary of thoughts:
- I've only chosen pass-by-value for the NodeInterfaces as a simple start, did you think about whether passing it as a pointer would be more suitable?
- Since image_transport_plugins seem to depend only on the SimpleXXXPlugin classes which offer a default implementation for the new NodeInterface methods, the changes should compile with the rolling version of image_transport_plugins (pretty nice!). Maybe one could use this pattern also for deprecating the node pointer? I think in the end it would be nice to use only the NodeInterfaces, although this might require a deprecation cycle
| std::bind(&Impl::checkImagesSynchronized, impl_.get())); | ||
| } | ||
|
|
||
| CameraSubscriber::CameraSubscriber( |
There was a problem hiding this comment.
That seems like a lot of code duplication. Maybe we can avoid this by using the fact that the node pointer should always be usable for obtaining the corresponding NodeInterfaces struct? In my view the main difference between the old and the new constructors is only whether the node pointer or the node interfaces are passed to the Impl-constructor.
There was a problem hiding this comment.
...and ideally we'd remove the Node pointer variant in the next distro.
|
@ahcorde thanks for pushing this forward. My email can be found from my profile. I will have a look when I got back. |
| struct CameraPublisher::Impl | ||
| { | ||
| using NodeLoggingInterface = rclcpp::node_interfaces::NodeLoggingInterface; | ||
| using RequiredInterfaces = rclcpp::node_interfaces::NodeInterfaces<NodeLoggingInterface>; |
There was a problem hiding this comment.
will this RequiredInterfaces introduce confusion over the one defined in image_transport namespace?
There was a problem hiding this comment.
Has compilation error:
/ros2_ws/src/image_common/image_transport/src/camera_subscriber.cpp: In constructor ‘image_transport::CameraSubscriber::CameraSubscriber(image_transport::RequiredInterfaces, const std::string&, const Callback&, const std::string&, rmw_qos_profile_t)’:
/ros2_ws/src/image_common/image_transport/src/camera_subscriber.cpp:162:29: error: no matching function for call to ‘message_filters::Subscriber<sensor_msgs::msg::CameraInfo_<std::allocator<void> > >::subscribe(image_transport::RequiredInterfaces&, std::string&, rmw_qos_profile_t&)’
162 | impl_->info_sub_.subscribe(required_test_interfaces, info_topic, custom_qos);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /ros2_ws/src/image_common/image_transport/src/camera_subscriber.cpp:34:
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:365:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodePtr, const std::string&, const rclcpp::QoS&) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; NodePtr = std::shared_ptr<rclcpp::Node>; std::string = std::__cxx11::basic_string<char>]’
365 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:366:13: note: no known conversion for argument 1 from ‘image_transport::RequiredInterfaces’ {aka ‘rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeParametersInterface, rclcpp::node_interfaces::NodeLoggingInterface, rclcpp::node_interfaces::NodeTimersInterface, rclcpp::node_interfaces::NodeTopicsInterface>’} to ‘message_filters::Subscriber<sensor_msgs::msg::CameraInfo_<std::allocator<void> > >::NodePtr’ {aka ‘std::shared_ptr<rclcpp::Node>’}
366 | NodePtr node, const std::string & topic,
| ~~~~~~~~^~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:381:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodeType*, const std::string&, const rclcpp::QoS&) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; std::string = std::__cxx11::basic_string<char>]’
381 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:382:16: note: no known conversion for argument 1 from ‘image_transport::RequiredInterfaces’ {aka ‘rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeParametersInterface, rclcpp::node_interfaces::NodeLoggingInterface, rclcpp::node_interfaces::NodeTimersInterface, rclcpp::node_interfaces::NodeTopicsInterface>’} to ‘rclcpp::Node*’
382 | NodeType * node, const std::string & topic,
| ~~~~~~~~~~~^~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:398:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodePtr, const std::string&, const rclcpp::QoS&, rclcpp::SubscriptionOptions) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; NodePtr = std::shared_ptr<rclcpp::Node>; std::string = std::__cxx11::basic_string<char>; rclcpp::SubscriptionOptions = rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void> >]’
398 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:398:8: note: candidate expects 4 arguments, 3 provided
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:419:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodeType*, const std::string&, const rclcpp::QoS&, rclcpp::SubscriptionOptions) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; std::string = std::__cxx11::basic_string<char>; rclcpp::SubscriptionOptions = rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void> >]’
419 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:419:8: note: candidate expects 4 arguments, 3 provided
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:451:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodePtr, const std::string&, rmw_qos_profile_t) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; NodePtr = std::shared_ptr<rclcpp::Node>; std::string = std::__cxx11::basic_string<char>; rmw_qos_profile_t = rmw_qos_profile_s]’
451 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:452:13: note: no known conversion for argument 1 from ‘image_transport::RequiredInterfaces’ {aka ‘rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeParametersInterface, rclcpp::node_interfaces::NodeLoggingInterface, rclcpp::node_interfaces::NodeTimersInterface, rclcpp::node_interfaces::NodeTopicsInterface>’} to ‘message_filters::Subscriber<sensor_msgs::msg::CameraInfo_<std::allocator<void> > >::NodePtr’ {aka ‘std::shared_ptr<rclcpp::Node>’}
452 | NodePtr node, const std::string & topic,
| ~~~~~~~~^~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:470:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodeType*, const std::string&, rmw_qos_profile_t) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; std::string = std::__cxx11::basic_string<char>; rmw_qos_profile_t = rmw_qos_profile_s]’
470 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:471:16: note: no known conversion for argument 1 from ‘image_transport::RequiredInterfaces’ {aka ‘rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeParametersInterface, rclcpp::node_interfaces::NodeLoggingInterface, rclcpp::node_interfaces::NodeTimersInterface, rclcpp::node_interfaces::NodeTopicsInterface>’} to ‘rclcpp::Node*’
471 | NodeType * node, const std::string & topic,
| ~~~~~~~~~~~^~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:490:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodePtr, const std::string&, rmw_qos_profile_t, rclcpp::SubscriptionOptions) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; NodePtr = std::shared_ptr<rclcpp::Node>; std::string = std::__cxx11::basic_string<char>; rmw_qos_profile_t = rmw_qos_profile_s; rclcpp::SubscriptionOptions = rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void> >]’
490 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:490:8: note: candidate expects 4 arguments, 3 provided
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:512:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe(NodeType*, const std::string&, rmw_qos_profile_t, rclcpp::SubscriptionOptions) [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node; std::string = std::__cxx11::basic_string<char>; rmw_qos_profile_t = rmw_qos_profile_s; rclcpp::SubscriptionOptions = rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void> >]’
512 | void subscribe(
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:512:8: note: candidate expects 4 arguments, 3 provided
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:538:8: note: candidate: ‘void message_filters::Subscriber<M, NodeType>::subscribe() [with M = sensor_msgs::msg::CameraInfo_<std::allocator<void> >; NodeType = rclcpp::Node]’
538 | void subscribe() override
| ^~~~~~~~~
/opt/ros/rolling/include/message_filters/message_filters/subscriber.hpp:538:8: note: candidate expects 0 arguments, 3 provided
gmake[2]: *** [CMakeFiles/image_transport.dir/build.make:146: CMakeFiles/image_transport.dir/src/camera_subscriber.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:189: CMakeFiles/image_transport.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2
---
Failed <<< image_transport [6.97s, exited with code 2]
There was a problem hiding this comment.
I had no build problems on my machine, could it be that you are using an older version of the message filter package? The PR adapting the interface to rclcpp::NodeInterfaces (this commit) has just been merged a few days ago, thus, you'll likely need the current rolling branch for compatibility.
There was a problem hiding this comment.
You are right. It is good when I build message_filters from source since the binary is not released just yet.
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
Changes on image_transpor_plugins #352 |
| struct ImageTransport::Impl | ||
| { | ||
| rclcpp::Node::SharedPtr node_; | ||
| RequiredInterfaces required_interfaces_; |
There was a problem hiding this comment.
Out of curiosity: Why did you remove the old pointer in your second iteration?
In my view keeping this pointer was one key benefit of your suggested implementation, as it allowed to keep the image_transport_plugins unchanged. I would really appreciate a plugin interface centered around the rclcpp::NodeInterfaces as the long run solution, however, I cannot quantify the additional overhead caused by the synchronization need for both repositories. As a maintainer you have likely more insights regarding this topic.
Depending on how large that overhead is, I would see your first iteration which kept the pointer as an acceptable solution since backwards compatibility is included. The full switch to using only rclcpp::NodeInterfaces in the plugin interfaces may then still be carried later on (potentially including multiple steps / deprecation cycles for backwards compatibility?)
EDIT:
Did the first version which kept the old pointer even work with the unmodified rolling state of the plugins for you? Even though it compiles I would expect runtime problems, since the plugins (e.g. for compressed image transport) explicitly override advertiseImpl(rclcpp::Node * node, ...).
Just for clarity since you link this MR instead of the other repository: Since we are now back to the point where the two initial MRs started off (with changes both in image_common and image_transport_plugins), I would like to ask again which implications the synchronization need of both repositories has. |
The idea is to merge these changes only in the I'm aware of (which are in rosdistro):
|
|
I maintain the ffmpeg_image_transport and foxglove_compressed_video_transport packages and encourage you to get this PR over the finish line. It's been almost 6 years now that this is an open issue. |
|
I fully support the opinion of @berndpfrommer. This issue has been open for such a long time, hence, we should really move forward and implement the lifecycle node support (even though this might include breaking changes). @ahcorde |
|
One of the big problems here, it's the Suggestions are welcome: One possible solution is deprecate What do you think ? |
|
Honestly, I am not sure whether there is even a nice way to move forward without breaking backwards compatibility (at least I haven't found one yet). As far as my understanding goes we agree that the desired interface from image transport perspective should be a NodeInterfaces instance. Meaning that either a normal node or a lifecycle node will be split up into their individual interfaces. I see your point that breaking backwards compatibility will (potentially) create problems for users, however, I would like to highlight that the missing support for lifecycle nodes already burdens users to create workarounds (e.g. legacy node within lifecycle node only for image transport) or even blocks features (e.g. in the case of @berndpfrommer). And that for quite a long time. |
|
As this packages is a core package, I will bring this topic to next TGC meeting and let's see what we can do |
|
Are there any updates on this? I didn't find much within the official meeting protocol |
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
@ahcorde If I remember correctly from my original PR, it should be possible to template the respective functions (see here. This way, everything which can be converted to the RequiredInterfaces class should be directly usable with image_transport (without any further changes in the user code). In my view the "code duplication" of the templated function is justified given that it allows to reuse any other user code relying on image transport without changes. |
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
| SubscriberFilter( | ||
| rclcpp::Node * node, const std::string & base_topic, | ||
| rclcpp::Node * node, | ||
| const std::string & base_topic, | ||
| const std::string & transport) | ||
| { | ||
| subscribe(node, base_topic, transport); | ||
| subscribe(*node, base_topic, transport); | ||
| } |
There was a problem hiding this comment.
Is there a future plan for this function? Will the rclcpp::Node accepting functions and constructors be deprecated in the future in favor of their NodeInterfaces equivalents?
If not:
Is there anything against templating the NodeType in those functions / constructors? This would also allow passing LifecycleNodes in there. From a user perspective it may be a bit confusing that we want to introduce lifecycle support but nonetheless keep the convenience overloads restricted to rclcpp::Node.
EDIT:
The question applies to all files where separate rclcpp::Node overloads are available, I've only chosen this instance as an example.
There was a problem hiding this comment.
I would say I prrfer to use NodeInterfaces but can keep some methods which accept rclcpp::Node if that makes sense.
This is very nice documented issue ros2/geometry2#698 why we shouldn't use templates and why we should use rclcpp::node_interfaces::NodeInterfaces
There is a ppt too https://docs.google.com/presentation/d/1bdXOOZPhR9yAnyGNxoLhuO_bU4RW5IjnzvCtrVlpe_g/edit?slide=id.g24afec4abf4_0_0#slide=id.g24afec4abf4_0_0
Feel free to make suggestions in the code or open a PR against these one with your suggestions. Happy to review it and/or discuss these changes
There was a problem hiding this comment.
Sorry if my suggestion wasn't formulated clearly, so let me have another try on this:
I am fully aware that rclcpp::node_interfaces::NodeInterfaces is the way to go (otherwise I wouldn't have introduced them within message filters). Instead, the intended target of my question are the "convenience" (wrapper) functions which are currently kept (i.e. taking rclcpp::Node*).
In my view it is not consistent to support both, normal and lifecycle nodes, but provide those "convenience" functions only for rclcpp::Node*.
In order to have a consistent interface, there are two options in my view:
- Deprecate every method which relies on
rclcpp::Nodeso that in the future only the NodeInterfaces are used. - Add support for lifecycle node pointers next to functions / c-tors which accept
rclcpp::Node*.
I am slightly favoring option 2, since option 1 will potentially cause a lot of deprecation warnings. However, I would be fine with option 1 as well as my main point of criticsm is the inconsistency which would be solved by both options.
There was a problem hiding this comment.
Added deprecation to ever method which relies on rclcpp::Node 6bf7d3d
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
Pulls: #352, ros2/rviz#1472 |
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
Pulls: #352, ros2/rviz#1472 |
|
@authaldo do you mind to take another look? |
authaldo
left a comment
There was a problem hiding this comment.
I only skimmed quickly through your last commit as the already mentioned issue with testing the code still persists for me. Once that is solved, I am happy to take a more detailed look at the code again.
Hence, I would again ask you (and the other who already approved the changes) to check whether the following works on your machine:
To simplify recreating the test, I switched to an official ros docker image:
docker run -it --rm ros:jazzy-perception
Additionally, I installed tmux and wget and cloned the following repositories:
git clone https://github.com/ros-perception/image_common.git --branch ahcorde/rolling/lifecycle
git clone https://github.com/ros2/message_filters.git
git clone https://github.com/authaldo/image_transport_tutorials.git
git clone https://github.com/ros-perception/image_transport_plugins
In my fork of the image_transport_tutorials the publisher node is a lifecycle node. After building and sourcing I entered tmux and split my terminal once. In the first one, I start the publisher with this image downloaded via wget:
ros2 run image_transport_tutorials my_publisher ROS2_Jazzy_Jalisco_poster.png
The second terminal is used for the subscriber. The raw subscription via
ros2 topic echo /camera/image --no-arr
works flawlessly. However, once the plugin is involved, e.g., by subscribing to the compressed image
ros2 topic echo /camera/image/compressed --no-arr the publisher node crashes instantly.
The current rolling branch of image_common together with the unmodified image_transport_tutorials publisher allows subscribing to the compressed image.
Does this only happen for me? Which plugins did you test? To the best of my knowledge the CI tests do not cover the interaction across the repositories (beyond building). Or am I wrong about that?
|
Did you try these branch in the follwoing repositories ?
Sorry I didn't include these related PRs in the description, now it's updated |
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
@authaldo Let me know if this instructions are working for you |
|
Sorry for the late feedback, I was unfortunately busy with other stuff! With the adapted version of the Honestly, I am a bit confused now:
Combined with the draft state of the respective branch in the There is one last thing that bothers me: |
Related with this, I will make an announcement in ROS discourse, I already sent message to mainteners of the other rosdistro image transport plugins. Do you think this is enough? |
authaldo
left a comment
There was a problem hiding this comment.
@ahcorde
I just wanted to have it noted. Most users hopefully will anyways solely rely on the official plugins, so it shouldn't be too problematic. In the end you're the maintainer, so if it's fine for you then go ahead and merge this. We've waited long enough for lifecycle support within image_common 😄
|
Thank you! This closes a major ROS2 feature gap. I can now implement lifecycle nodes for my camera drivers! |
|
Finally 👍 @ahcorde What's the state of ros-perception/image_transport_plugins#180? It is still marked as draft. EDIT: |
* Fix image_common NodeT deprecation warnings from ros-perception/image_common#352 - migrate to NodeInterfaces * Fix image_common rmw_qos_profile_t deprecation warnings from ros-perception/image_common#364 - migrate to rclcpp::QoS * Fix rviz update float deprecation warnings from ros2/rviz#1533 - migrate to std::chrono::duration * Fix geometry2 tf2_ros .h deprecation warnings from ros2/geometry2#805 - migrate Kilted and Rolling to .hpp * Fix geometry2 tf2_ros NodeT deprecation warnings from ros2/geometry2#714 - migrate to NodeInterfaces * Fix rclcpp spin_some deprecation warnings from ros2/rclcpp#2848 - migrate to SingleThreadedExecutor --------- Co-authored-by: Andrea <realeandrea@yahoo.it>
* Fix image_common NodeT deprecation warnings from ros-perception/image_common#352 - migrate to NodeInterfaces * Fix image_common rmw_qos_profile_t deprecation warnings from ros-perception/image_common#364 - migrate to rclcpp::QoS * Fix rviz update float deprecation warnings from ros2/rviz#1533 - migrate to std::chrono::duration * Fix geometry2 tf2_ros .h deprecation warnings from ros2/geometry2#805 - migrate Kilted and Rolling to .hpp * Fix geometry2 tf2_ros NodeT deprecation warnings from ros2/geometry2#714 - migrate to NodeInterfaces * Fix rclcpp spin_some deprecation warnings from ros2/rclcpp#2848 - migrate to SingleThreadedExecutor --------- Co-authored-by: Andrea <realeandrea@yahoo.it>
@Benjamin-Tan and @authaldo
I created a new PR using NodeInterfaces based on #351 and #304. I can add you as coauthor if you send me your emails
As you have already worked on this I will appreciate if you can review it.
The only test that it's failing it's
12 - image_transport-qos_override_lifecycle (Failed)The node is not able to override the qos from parameters
Related PRs