Robotics StackExchange | Archived questions

Publish message on destructor works only on rclpy

Hi everyone,

In a test node I just have one publisher that publishes the node state once on the constructor and once on the destructor of my node class. I figured out that this works on Python3 (rclpy) and not on C++ (rclcpp).

import rclpy
from rclpy.node import Node
import time

from std_msgs.msg import String


class Test(Node):

    def __init__(self):
        super().__init__('test')

        self.publisher = self.create_publisher(String, 'state_test', 10)
        msg = String()
        msg.data = "Node started"
        self.publisher.publish(msg)

    def __del__(self):
        msg = String()
        msg.data = "Node crashed"
        self.publisher.publish(msg)

def main(args=None):
    rclpy.init(args=args)

    node_test = Test()
    rclpy.spin(node_test)

    node_test.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

C++ code:

#include <chrono>
#include <functional>
#include <memory>
#include <string>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class Test : public rclcpp::Node
{
  public:
    Test()
    : Node("test"), count_(0)
    {
      pub_state = this->create_publisher<std_msgs::msg::String>("state_test", 10);
      auto message = std_msgs::msg::String();
      message.data = "Node started";
      pub_state->publish(message);
    }

    ~Test()
    {
      auto message = std_msgs::msg::String();
      message.data = "Node crashed";
      pub_state->publish(message);
    }

  private:
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_state;
    size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<Test>());
  rclcpp::shutdown();
  return 0;
}

Why is the result different on those two examples ?

Asked by Erysme on 2020-10-23 08:43:29 UTC

Comments

Isn't that obvious? In python you are explicitly calling the destruction before calling shutdown. In C++ you don't do that, so the destructor will only be called when the variable goes out of scope, i.e., after the shutdown when it can't publish anymore.

Asked by chfritz on 2020-10-23 12:44:59 UTC

Answers

As pointed out in the comment, in your C++ code the object is only destroyed at the end of the program when the scope for the shared pointed ends. At that time ros has already shut down and you can no longer publish. However, you can force the destruction of the shared pointer before shutdown, but limiting its scope a bit more. I haven't tested this, but the following should do the trick:

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  {
    auto node = std::make_shared<Test>();
    rclcpp::spin(node);
  }
  rclcpp::spin_some(); // need to let ros spin a bit more to process the publish queued by the destructor
  rclcpp::shutdown();
  return 0;
}

Asked by chfritz on 2020-10-23 13:07:34 UTC

Comments

Thanks for your answer, I agree that in python the destructor is "manually" called. Your script does not work for me. I am still searching a way to publish a message when the node is destroyed.

Asked by Erysme on 2020-10-26 09:52:57 UTC

Can you say more about why it doesn't work? Does it not compile/run, or does it still fail to call the destructor?

Asked by chfritz on 2020-10-26 09:54:41 UTC

It enters the ~Test() destructor and goes out (I can print something in), but the message is not published

Asked by Erysme on 2020-10-26 10:11:22 UTC

ah, right, there was something missing from my answer. You need to call spin_some (or some other spin method) once more after the destructor in order for the queued up publication to go out. I've updated the answer. Could you try this?

Asked by chfritz on 2020-10-26 10:18:01 UTC

Yeah thats a nice idea. I tested it. I got compilation error : spin_some require an argument. I tryed rclcpp::spin_some(node); but there were an arror because node have been destroyed.

Edit : rclcpp::on_shutdown(linked_function); seems to be the way to do what I want. I test different implementation and will post a code

Asked by Erysme on 2020-10-26 10:27:59 UTC

I tryed rclcpp::on_shutdown() to link a fonction that is called on shutdown. Unfortunately rclcpp::ok() return False in this function.

Ros communications seems to be closed just after the exit of the spin function.

Does somebody have an idea ?

Asked by Erysme on 2020-10-28 12:27:18 UTC

I think you've tried this approach long enough. I would just create a manual destroy method in the class and call that manually after the spin.

Asked by chfritz on 2020-10-29 15:19:47 UTC