ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

Cannot exit node when pressing ctrl-c in while not rospy.is_shutdown()

asked 2022-06-23 04:07:10 -0500

loupdmer gravatar image

Hi everybody,

I am using ROS 20 noetic on ubuntu 20.04. I try using my camera (Arducam IMX477 with usb adapter). I first created a node to acquire the frame and publish them on a topic with two services which allow me to change the resolution and save a frame.

Here is the code ros_IMX477.py :

import rospy
import os
import sys
from pathlib import Path

import cv2
import time
import datetime
import threading

from cv_bridge import CvBridge
from sensor_msgs.msg import Image
bridge = CvBridge()

# Initialize architecture
FILE = Path(sys.argv[0]).resolve() # Get the full path of the file
node_name = os.path.splitext(FILE.name)[0]

import rospkg
rospack = rospkg.RosPack()
package = rospkg.get_package_name(FILE) # Get the package name
ROOT = Path(package).resolve() # Get the full path of the package

from vision.srv import CameraResolution, CameraResolutionResponse, CameraOnOff, CameraOnOffResponse

# Topics to subscribe/publish
camera_topic = "/cam_2/color/image_raw"

# Define a camera handler
class Camera_handler:

    def __init__(self):
        # Create the camera handler object

        try:
            entries = os.listdir("/dev")
            num = min([int(entry[5:]) for entry in entries if "video" in entry])
            src = "/dev/video"+str(num)
            self.cap = cv2.VideoCapture(src, cv2.CAP_V4L2)
        except cv2.error as error:
            print("[Error]: {}".format(error))

        if not self.cap.isOpened():
            print("Cannot open camera handler")
            exit() 

        # Init parameters
        (self.ret, self.img) = self.cap.read()
        self.idx = 1 # The number of frame grabbed
        self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
        self.fps = self.cap.get(cv2.CAP_PROP_FPS)

        rospy.set_param('imx477', {'width': self.width, 'height': self.height, 'fps': self.fps})

        self.cam_pub = rospy.Publisher(camera_topic, Image, queue_size=1) # Publisher
        print("Camera handler capturing %s at (%d,%d) %dFPS" %(src, int(self.width),int(self.height),int(self.fps)))
        self.ResolutionService = rospy.Service('setResolution', CameraResolution, self.setResolution)
        self.CaptureService = rospy.Service('Capture', CameraOnOff, self.Capture)
        self.mutex = threading.Semaphore()

    def read(self):
        self.mutex.acquire()
        (self.ret, self.img) = self.cap.read()
        if self.ret:
            self.img = cv2.rotate(self.img, cv2.ROTATE_90_CLOCKWISE)
            im = bridge.cv2_to_imgmsg(self.img, encoding="bgr8")

            # Select one frame according to the index and publish it
            im.header.stamp = rospy.get_rostime() # Set a timestamp
            im.header.frame_id = "frame_" + str(self.idx)
            im.header.seq = self.idx
            self.cam_pub.publish(im) # Publish the frame
            #rospy.loginfo("frame published")
            self.idx+=1
        self.mutex.release()

    def setResolution(self, req):
        resolution = round(req.resolution,1)
        if resolution == 0.3:
            width, height = 640, 480
        elif resolution == 2:
            width, height = 1920, 1080
        elif resolution == 5:
            width, height = 2592, 1944
        elif resolution == 8:
            width, height = 3840, 2160
        elif resolution == 12:
            width, height = 4032, 3040
        else:
            print("Required resolution %.1fMpx unavailable" %resolution)
            return CameraResolutionResponse(False)

        try :

            self.mutex.acquire()
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
            # update attributes
            self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
            self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            rospy.set_param('imx477', {'width': self.width, 'height': self.height, 'fps': self.fps})

            print("Parameters updated to (%d,%d ...
(more)
edit retag flag offensive close merge delete

1 Answer

Sort by ยป oldest newest most voted
0

answered 2022-06-23 18:43:42 -0500

Mike Scheutzow gravatar image

updated 2022-06-30 06:57:36 -0500

Your programming error is that you are consuming 100% of the cpu in the loop, and never giving the ros code a chance to run.

All of your while loops must call rospy.sleep(n) or r.sleep(), where r is a rospy.Rate object. To get started, try n = 0.250.

If you run top in a terminal window and see your rospy-based node consuming 100% cpu, you have a problem you need to fix.

Edit: this advice applies to your publisher too.

edit flag offensive delete link more

Comments

Thank you for your reply

My cpu is consuming around 60% of each cores (2 cores) when running the two nodes with a resolution of 640x480. In this resolution I can acquire frames at 80fps, I could reduce to 30fps with the rospy.Rate(30) but with such a frequence, the issue still occur. I want to perform image processing at real time, using n = 0.250 fix the issue but I can't use such a frequency.

In fact, it only embarrass me when I want to kill the node, but need to understand why to not be locked in the future.

loupdmer gravatar image loupdmer  ( 2022-06-24 03:54:08 -0500 )edit

I don't understand your comment. In the code you show us, the while loop does not do any useful work (it only logs messages). You say Control-C works when looping at 4 Hz, but it does not work at 30 Hz? This is very unlikely.

Mike Scheutzow gravatar image Mike Scheutzow  ( 2022-06-24 07:46:14 -0500 )edit

I had the issue with a more complete code, but in order to check whether it was not my functions inside the loop which cause the problem, I removed everything to test with simple print. It appears that the issue was still there.

I agree with you that is very unlikely but it is the case, at 4Hz it works, at 30Hz it doesn't. I put 3 videos at this GitHub link

I am aware that is not a safe way to show you my issue, if you have another methods I am ok to use it.

Thanks

loupdmer gravatar image loupdmer  ( 2022-06-28 07:55:00 -0500 )edit

My guess is that it is taking a long time to disconnect the subscriber, probably because your publisher is not responding to the ros request. Your publisher - which is a ros node - needs to sleep() or spin() as well.

Mike Scheutzow gravatar image Mike Scheutzow  ( 2022-06-30 06:56:26 -0500 )edit

Sorry for my late response I haven't been able to rework on the topic yet. I will try this and come back soon, thank you for your help

loupdmer gravatar image loupdmer  ( 2022-07-06 02:16:23 -0500 )edit

I have tested something simple : In one terminal : roscore In a second terminal : the following code

import rospy

def main():
    rospy.init_node("test", anonymous = False)
    rospy.loginfo("Initializing node %s" %rospy.get_name())
    r = rospy.Rate(50)

    # Initialize the mission
    rospy.loginfo("Node %s initialized" %rospy.get_name())
    while not rospy.is_shutdown():
        rospy.loginfo("in")
        rospy.loginfo("out")
        r.sleep()
    print("Exit")

if __name__ == '__main__':
    main()

If I do ctrl-c when only "in" is printed, it locks.

loupdmer gravatar image loupdmer  ( 2022-07-06 10:28:15 -0500 )edit

Question Tools

1 follower

Stats

Asked: 2022-06-23 04:07:10 -0500

Seen: 136 times

Last updated: Jun 30 '22