Shutdown_hook in ROS2

Hi,

In ROS1, I can use rospy.on_shutdown(shutdown_hook) to do something before the node is down like stopping the AMR

But I do not see the function in ROS2.
How can I do the same function in ROS2?

Thanks

In ROS 2, there isn’t a direct equivalent to rospy.on_shutdown() for registering a shutdown hook. But you can do something similar by structuring your node’s lifecycle management carefully:

    def shutdown_hook(self):
        # Place your pre-shutdown logic here
        self.get_logger().info('Performing pre-shutdown actions...')
        # Example: Stop the AMR
        # self.stop_amr()

def main(args=None):
    rclpy.init(args=args)
    
    my_node = MyCustomNode()
    
    try:
        rclpy.spin(my_node)
    except KeyboardInterrupt:
        pass
    finally:
        # Call the shutdown hook before destroying the node
        my_node.shutdown_hook()
        my_node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

Hi, I was busy so could not come back sooner.
This time I will keep up until the end

I try your suggestion but it does not work.
It said: Failed to publish log message to rosout: publisher’s context is invalid, at ./src/rcl/publisher.c:389

And the robot does not stop
Could you check my code?

#! /usr/bin/env python3

import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSDurabilityPolicy

from geometry_msgs.msg import Twist
from std_msgs.msg import Int32

import os, sys, argparse
from pathlib import Path
from glob import glob
import time
from datetime import datetime
import logging


class TopicPublisher(Node):
    def __init__(self, nodeName):
        self.nodeName = nodeName
        super().__init__(node_name=self.nodeName)
        self.get_logger().set_level(rclpy.logging.LoggingSeverity.INFO)
        self.ctrlC = False

        # region init
        self.publisher  = self.create_publisher(
            msg_type    = Twist, 
            topic       = 'cmd_vel', 
            qos_profile = QoSProfile(
                depth = 10,
                reliability = QoSReliabilityPolicy.RELIABLE,    # BEST_EFFORT: sensor | RELIABLE: control
                durability  = QoSDurabilityPolicy.VOLATILE     # TRANSIENT_LOCAL: latch | VOLATILE: no latch
            )
        )
        self.twist = Twist()

        self.timer = self.create_timer(2, self.timer_callback)
        # endregion

    def shutdown_callback(self):
        self.ctrlC = True

        self.twist.linear.x = 0.0
        self.twist.angular.z = 0.0
    
        self.get_logger().warning(f"{self.nodeName} is shutting down")

        self.publisher.publish(self.twist)
        self.get_logger().warning(f"self.twist.data: {self.twist}")


    def timer_callback(self):
        self.twist.linear.x = 0.2
        self.twist.angular.z = 0.2
    
        self.publisher.publish(self.twist)
        self.get_logger().info(f"self.twist.data: {self.twist}")


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

    topicPublisher = TopicPublisher("topic_publisher_node")
    
    try:
        rclpy.spin(topicPublisher)
    except KeyboardInterrupt:
        pass
    finally:
        topicPublisher.shutdown_callback()
        topicPublisher.destroy_node()
        rclpy.try_shutdown()
    
        
if __name__ == '__main__':
    main()

    # for key, value in os.environ.items():
    #     print(f"{key} -- {value}")

Please check and help me

Thanks.

Hi, it’s not that the shutdown hook doesn’t work, it seems like there is an issue with the logging. Does it work without the logging? your logic seems correct

I tried several times but it is the same. Please check

#! /usr/bin/env python3

import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSDurabilityPolicy

from geometry_msgs.msg import Twist
from std_msgs.msg import Int32

import os, sys, argparse
from pathlib import Path
from glob import glob
import time
from datetime import datetime
import logging


class TopicPublisher(Node):
    def __init__(self, nodeName):
        self.nodeName = nodeName
        super().__init__(node_name=self.nodeName)
        self.get_logger().set_level(rclpy.logging.LoggingSeverity.INFO)
        self.ctrlC = False

        # region init
        self.publisher  = self.create_publisher(
            msg_type    = Twist, 
            topic       = 'cmd_vel', 
            qos_profile = QoSProfile(
                depth = 10,
                reliability = QoSReliabilityPolicy.RELIABLE,    # BEST_EFFORT: sensor | RELIABLE: control
                durability  = QoSDurabilityPolicy.VOLATILE     # TRANSIENT_LOCAL: latch | VOLATILE: no latch
            )
        )
        self.twist = Twist()

        self.timer = self.create_timer(2, self.timer_callback)
        # endregion

    def shutdown_callback(self):
        self.ctrlC = True

        self.twist.linear.x = 0.0
        self.twist.angular.z = 0.0
    
        # self.get_logger().warning(f"{self.nodeName} is shutting down")

        self.publisher.publish(self.twist)
        # self.get_logger().warning(f"self.twist.data: {self.twist}")


    def timer_callback(self):
        self.twist.linear.x = 0.2
        self.twist.angular.z = 0.2
    
        self.publisher.publish(self.twist)
        self.get_logger().info(f"self.twist.data: {self.twist}")


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

    topicPublisher = TopicPublisher("topic_publisher_node")
    
    try:
        rclpy.spin(topicPublisher)
    except KeyboardInterrupt:
        pass
    finally:
        topicPublisher.shutdown_callback()
        topicPublisher.destroy_node()
        rclpy.try_shutdown()
    
        
if __name__ == '__main__':
    main()

    # for key, value in os.environ.items():
    #     print(f"{key} -- {value}")

This topic was automatically closed after 6 days. New replies are no longer allowed.

Does anyone help me, please?

The node in your script isn’t shutting down because shutdown_callback() is never called unless a KeyboardInterrupt exeception occurs.

A node will continue running with rclpy.spin() until a shutdown event is triggered, like pressing Ctrl+C. The shutdown function is only invoked in the finally block here, so there is no condition to break the rclpy.spin().

I assume you are expecting an automatic shutdown without manual intervention, so you need to add some logic to trigger this event. More specifically, to break timer_callback().

Here you can check the shutdown documentation and different ways you can use it, as well as some examples in scripts:

https://docs.ros2.org/galactic/api/rclpy/api/init_shutdown.html

I want every time the rclpy shut down, it will execute a function before really shutdown like rospy.on_shutdown(function) in ROS1.

In Ros1, after I register a function in rospy.on_shutdown(function). If I press Ctrl+C, it will execute function() then stopping. So I want to do the same in ROS2.

I follow your guide but I press Ctrl + C, ros2 can execute the function() but commands as sending messages to a topic, logging, … do not work. It raise error like images I sent. That is my problem and I want to get help

I’ve done a deep dive to see what is happening here, and it seems Humble and more recent distros are having issues precisely in that area. It seems like it’s not something you are coding incorrectly, but rather a racy condition that happens that prevents you from publishing any ROS 2 information when there is a keyboardinterrupt.

Here is an interesting open issue related to this:

I don’t know how to go around this issue. I think this issue is related so let’s see if @albertoezquerro has a workaround.

1 Like