ROS Noetic图片显示服务二次调用失败问题求助
解决ROS服务中第二次调用OpenCV无法创建窗口的问题
环境信息
- Ubuntu 20.04
- ROS Noetic
- OpenCV 4.2.0.34
问题现象
开发的ROS图片显示服务首次调用正常,但第二次调用时,即便执行了cv2.destroyWindow销毁窗口,cv2.namedWindow仍无法创建新窗口。
相关代码
服务节点代码
#! /usr/bin/env python3 # -*- coding: utf-8 -*- import rospy import cv2 from img_reader.srv import ImgReader, ImgReaderResponse def showImage(path): img = cv2.imread(path) cv2.namedWindow('img', cv2.WINDOW_NORMAL) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyWindow('img') def readImgCB(req): try: showImage(req.img_path) msg = "" return ImgReaderResponse(True, msg) except: msg = "Image Path is Incorrect" return ImgReaderResponse(False, msg) if __name__ == "__main__": try: rospy.init_node("img_reader") rospy.Service("img_reader_srv", ImgReader, readImgCB) rospy.spin() except rospy.ROSInterruptException: rospy.logerr("Error")
服务定义ImgReader.srv
# Desired image path. string img_path --- uint16 result_code # Status message (empty if service succeeded). string message
解决方案
核心原因
- OpenCV GUI操作依赖主线程事件循环,ROS服务回调可能在后台线程执行,导致窗口管理异常。
- 仅调用
destroyWindow无法彻底清空GUI事件队列,残留事件会阻碍新窗口创建。
方案1:优化窗口销毁逻辑
修改showImage函数,确保窗口资源完全释放并清空事件队列:
def showImage(path): # 先检查目标窗口是否存在,存在则销毁 if cv2.getWindowProperty('img', cv2.WND_PROP_VISIBLE) >= 1: cv2.destroyWindow('img') # 读取图片并显示 img = cv2.imread(path) cv2.namedWindow('img', cv2.WINDOW_NORMAL) cv2.imshow('img', img) cv2.waitKey(0) # 销毁所有窗口而非单个,确保资源释放 cv2.destroyAllWindows() # 调用一次waitKey清空残留事件队列 cv2.waitKey(1)
方案2:强制GUI操作在主线程执行
由于OpenCV GUI必须在主线程运行,通过任务队列将显示任务转移到主线程处理:
#! /usr/bin/env python3 # -*- coding: utf-8 -*- import rospy import cv2 import os from img_reader.srv import ImgReader, ImgReaderResponse from threading import Queue task_queue = Queue() task_completed = False def showImage(path): global task_completed task_completed = False try: # 提前检查路径合法性 if not os.path.exists(path): raise FileNotFoundError(f"Path not found: {path}") img = cv2.imread(path) cv2.namedWindow('img', cv2.WINDOW_NORMAL) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows() cv2.waitKey(1) task_completed = True except Exception as e: rospy.logerr(f"Display failed: {str(e)}") task_completed = False def readImgCB(req): global task_completed try: task_queue.put(req.img_path) # 等待主线程完成任务 while not task_completed and not rospy.is_shutdown(): rospy.sleep(0.01) if task_completed: return ImgReaderResponse(0, "") else: return ImgReaderResponse(1, "Failed to display image") except Exception as e: return ImgReaderResponse(1, f"Error: {str(e)}") if __name__ == "__main__": try: rospy.init_node("img_reader") rospy.Service("img_reader_srv", ImgReader, readImgCB) # 主线程循环处理任务队列 while not rospy.is_shutdown(): if not task_queue.empty(): path = task_queue.get() showImage(path) rospy.sleep(0.01) except rospy.ROSInterruptException: rospy.logerr("ROS Interrupt Exception")
额外优化建议
- 服务定义中
result_code为uint16类型,建议用0表示成功、1表示失败,避免布尔值隐式转换的潜在问题。 - 在
showImage中增加路径合法性检查,提前排除无效路径导致的异常。
内容的提问来源于stack exchange,提问作者Bilal
相关产品推荐
相关产品推荐

