You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

解决方案

核心原因

  1. OpenCV GUI操作依赖主线程事件循环,ROS服务回调可能在后台线程执行,导致窗口管理异常。
  2. 仅调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:54:58