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

ROS2 C++中cv::VideoCapture打开本地视频失败问题求助

ROS2 C++节点中OpenCV打开本地视频失败的解决方法

问题概述

在ROS2 C节点中实现CamReader类,调用摄像头(cap.open(0))功能正常,但打开本地视频文件/home/pi/Meurtres_au_paradis.S01E01.mp4时失败,触发GStreamer相关错误。非ROS2环境下的C程序可正常播放该视频,需解决此问题以进行C++ DNN人脸识别(Python性能不足)。

相关代码

cam_reader.hpp

#include "rclcpp/rclcpp.hpp"
#include "opencv2/opencv.hpp"
#include "sensor_msgs/msg/image.hpp"
#include "cv_bridge/cv_bridge.h"

class CamReader : public rclcpp::Node
{
public:
    CamReader();

    cv::VideoCapture cap;

private:
    // Subscribers(未实现)
    // Publishers
    rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_publisher_;
    // Timers
    rclcpp::TimerBase::SharedPtr get_image_timer_;
    // Callbacks
    void get_image_callback_();
};

cam_reader.cpp(原代码)

#include <cam_reader/cam_reader.hpp>

CamReader::CamReader ()
Node("cam_reader") // 语法错误:缺少初始化列表冒号

char videopath[] = "/home/pi/Meurtres_au_paradis.S01E01.mp4";
this->cap.open(*videopath); // 错误:传递char而非字符串指针
this->cap.open(0); // 覆盖了之前的视频打开操作
if (!this->cap.isOpened())
{
    RCLCPP_INFO(this->get_logger(), "Error opening camera");
}

get_image_timer_ = this->create_wall_timer(
    std::chrono::milliseconds(2000),
    std::chrono::milliseconds(50), // 错误:create_wall_timer只接受一个周期参数
    std::bind(&CamReader::get_image_callback_, this));

auto default_qos = rclcpp::QoS(rclcpp::SystemDefaultsQoS());
default_qos.reliable();

image_publisher_ = this->create_publisher<sensor_msgs::msg::Image>("/image", default_qos);
}

void CamReader::get_image_callback_()
{
    cv::Mat frame;
    this->cap >> frame;

    cv_bridge::CvImage img_bridge;
    sensor_msgs::msg::Image cam_msg;
    std_msgs::msg::Header header;

    img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::RGB8, frame);
    img_bridge.toImageMsg(cam_msg);
    image_publisher_->publish(cam_msg);
}

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared()); // 错误:未指定类型
    rclcpp::shutdown();
    return 0;
}

报错信息

WARN:0@0.203] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (2401) handleMessage OpenCV | GStreamer warning: Embedded video playback halted; module v4l2src0 reported: Cannot identify device '/dev/video47'.
WARN:0@0.203] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (1356) open OpenCV | GStreamer warning: unable to start pipeline
WARN:0@0.204] global /home/pi/opencv/modules/videoio/src/cap_gstreamer.cpp (862) isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
WARN:0@0.204] global /home/pi/opencv/modules/videoio/src/cap_v4l.cpp (902) open VIDEOIO(V4L2:/dev/video47): can't open camera by index
[INFO] [1679388758.511573816] [cam_reader]: Error opening camera

解决方案

1. 修复代码中的语法错误

  • 构造函数初始化:修正Node继承的初始化列表,改为CamReader::CamReader() : Node("cam_reader")
  • 视频路径传递:直接传递字符串指针而非解引用char数组,即this->cap.open(videopath),同时注释掉覆盖视频的this->cap.open(0)
  • 定时器参数:create_wall_timer仅需一个周期参数,移除多余的std::chrono::milliseconds(2000)
  • main函数修正:std::make_shared需指定类型,改为std::make_shared<CamReader>()

2. 强制指定OpenCV视频后端

ROS2环境下OpenCV可能默认优先使用GStreamer后端,但该后端对本地视频的兼容性不如FFmpeg。强制指定FFmpeg后端打开视频:

std::string videopath = "/home/pi/Meurtres_au_paradis.S01E01.mp4";
if (!this->cap.open(videopath, cv::CAP_FFMPEG)) {
    RCLCPP_ERROR(this->get_logger(), "Failed to open video file");
}

可通过cv::getBuildInformation()检查OpenCV是否编译了FFmpeg支持,确保输出中包含FFmpeg: YES。

3. 验证文件权限与路径

  • 确认视频文件路径正确,可通过终端执行ls /home/pi/Meurtres_au_paradis.S01E01.mp4验证
  • 检查文件权限,确保当前用户有读取权限:chmod +r /home/pi/Meurtres_au_paradis.S01E01.mp4

修改后的完整cam_reader.cpp示例

#include <cam_reader/cam_reader.hpp>

CamReader::CamReader() : Node("cam_reader")
{
    std::string videopath = "/home/pi/Meurtres_au_paradis.S01E01.mp4";
    // 强制使用FFmpeg后端打开视频
    if (!this->cap.open(videopath, cv::CAP_FFMPEG)) {
        RCLCPP_ERROR(this->get_logger(), "Failed to open video file: %s", videopath.c_str());
        rclcpp::shutdown();
        return;
    }

    // 创建定时器,周期50ms
    get_image_timer_ = this->create_wall_timer(
        std::chrono::milliseconds(50),
        std::bind(&CamReader::get_image_callback_, this));

    auto default_qos = rclcpp::QoS(rclcpp::SystemDefaultsQoS());
    default_qos.reliable();

    image_publisher_ = this->create_publisher<sensor_msgs::msg::Image>("/image", default_qos);
}

void CamReader::get_image_callback_()
{
    cv::Mat frame;
    if (!this->cap.read(frame)) {
        RCLCPP_WARN(this->get_logger(), "End of video stream");
        get_image_timer_->cancel();
        return;
    }

    cv_bridge::CvImage img_bridge;
    sensor_msgs::msg::Image cam_msg;
    std_msgs::msg::Header header;
    header.stamp = this->get_clock()->now(); // 添加时间戳

    img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, frame);
    img_bridge.toImageMsg(cam_msg);
    image_publisher_->publish(cam_msg);
}

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

内容的提问来源于stack exchange,提问作者petitbouchon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:22:03