订阅ROS2中相机的相关话题并保存RGB、深度和点云图

系统:Ubuntu22.04
ROS2版本:ROS2 humble

1.订阅ROS2中相机的相关话题并保存RGB图、深度图和点云图

ros2 topic list

/stellar_1/rgb/image_raw
/camera/depth/image_raw
/stellar_1/points2

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(bag_to_image)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(OpenCV REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

add_executable(bag_to_image_node src/bag_to_image_node.cpp)

# ROS2中指定节点或库的依赖项
ament_target_dependencies(bag_to_image_node rclcpp) # 核心C++客户端库,旨在简化开发和实现机器人应用程序
ament_target_dependencies(bag_to_image_node sensor_msgs) # 消息包,提供了一系列标准化的消息类型,用于传输来自各种传感器的数据
ament_target_dependencies(bag_to_image_node cv_bridge) # 用于连接ROS与OpenCV,提供在ROS消息格式与OpenCV图像格式之间的转换
ament_target_dependencies(bag_to_image_node OpenCV)

install(TARGETS
    bag_to_image_node
    DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  set(ament_cmake_copyright_FOUND TRUE)
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

bag_to_image_node.cpp

#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/image.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <filesystem>
#include <fstream>
#include <queue>

class ImageSaver : public rclcpp::Node
{
public:
    ImageSaver() : Node("image_saver"), frame_count(0)
    {
        // 新建RGB图像、深度图像、点云图像文件夹
        std::filesystem::create_directories("rgb_images");
        std::filesystem::create_directories("depth_images");
        std::filesystem::create_directories("point_clouds");

        // 订阅话题 
        rgb_subscriber = this->create_subscription<sensor_msgs::msg::Image>(
            "/stellar_1/rgb/image_raw",
            10,
            std::bind(&ImageSaver::rgb_callback, this, std::placeholders::_1));

        depth_subscriber = this->create_subscription<sensor_msgs::msg::Image>(
            "/camera/depth/image_raw",
            10,
            std::bind(&ImageSaver::depth_callback, this, std::placeholders::_1));

        point_cloud_subscriber = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/stellar_1/points2",
            10,
            std::bind(&ImageSaver::point_cloud_callback, this, std::placeholders::_1));
    }

private:
    std::queue<sensor_msgs::msg::Image::SharedPtr> rgb_queue;
    std::queue<sensor_msgs::msg::Image::SharedPtr> depth_queue;

    void rgb_callback(const sensor_msgs::msg::Image::SharedPtr msg)
    {
        rgb_queue.push(msg);
        process_frames();
    }

    void depth_callback(const sensor_msgs::msg::Image::SharedPtr msg)
    {
        depth_queue.push(msg);
        process_frames();
    }

    void point_cloud_callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg)
    {
        // 保存点云图为.bin格式
        std::ofstream outfile("point_clouds/frame_" + std::to_string(frame_count) + ".bin", std::ios::binary);
        outfile.write(reinterpret_cast<const char *>(msg->data.data()), msg->data.size());
        outfile.close();
        frame_count++;
        process_frames(); // Check if we can process frames after saving point cloud
    }

    void process_frames()
    {
        while (!rgb_queue.empty() && !depth_queue.empty() && frame_count > 0) {
            auto rgb_msg   = rgb_queue.front();
            auto depth_msg = depth_queue.front();

            // 计算时间戳差值
            int64_t rgb_time   = rgb_msg->header.stamp.sec * 1e9 + rgb_msg->header.stamp.nanosec;
            int64_t depth_time = depth_msg->header.stamp.sec * 1e9 + depth_msg->header.stamp.nanosec;

            // 检查时间戳是否足够接近
            if (std::abs(rgb_time - depth_time) < 100000000) { // 100 ms
                cv_bridge::CvImagePtr cv_rgb_ptr = cv_bridge::toCvCopy(rgb_msg, sensor_msgs::image_encodings::BGR8);
                cv::imwrite("rgb_images/frame_" + std::to_string(frame_count) + ".png", cv_rgb_ptr->image);

				// TYPE_32FC1:
				// 32F:表示每个元素是 32 位(4 字节)浮点数(float)。
				// C1:表示单通道(Channel 1),即每个像素只有一个值。常用于灰度图像或单通道浮点数据。
				// TYPE_16UC1:
				// 16U:表示每个元素是 16 位(2 字节)无符号整数(unsigned int)。
				// C1:同样表示单通道,即每个像素只有一个值。常用于灰度图像或单通道整数数据,通常用于深度图等场景。
                cv_bridge::CvImagePtr cv_depth_ptr =
                    cv_bridge::toCvCopy(depth_msg, sensor_msgs::image_encodings::TYPE_16UC1);
                cv::imwrite("depth_images/frame_" + std::to_string(frame_count) + ".png", cv_depth_ptr->image);

                rgb_queue.pop();
                depth_queue.pop();
            } else if (rgb_time < depth_time) {
                rgb_queue.pop(); // 移除旧的 RGB 消息
            } else {
                depth_queue.pop(); // 移除旧的深度消息
            }
        }
    }

    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr rgb_subscriber;
    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr depth_subscriber;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr point_cloud_subscriber;
    int frame_count;
};

int main(int argc, char **argv)
{
    std::cout << "bag to image node start.." << std::endl;
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<ImageSaver>());
    rclcpp::shutdown();

    return 0;
}

运行:

source install/setup.bash
ros2 run bag_to_image bag_to_image_node

在这里插入图片描述

2.将点云图.bin格式转换为.pcd格式

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <thread>

// 将点云图.bin转换为.pcd
void convert_bin_to_pcd(const std::string &bin_file, const std::string &pcd_file)
{
    // 读取二进制点云数据
    std::ifstream infile(bin_file, std::ios::binary);
    if (!infile) {
        std::cerr << "Error opening binary file!" << std::endl;
        return;
    }

    pcl::PointCloud<pcl::PointXYZ> cloud;
    pcl::PointXYZ point;

    // 假设每个点包含x, y, z坐标
    while (infile.read(reinterpret_cast<char *>(&point), sizeof(pcl::PointXYZ))) {
        cloud.points.push_back(point);
    }
    cloud.width  = cloud.points.size();
    cloud.height = 1; // 单个点云

    infile.close();

    // 保存为 PCD 文件
    pcl::io::savePCDFileASCII(pcd_file, cloud);
    std::cout << "Converted to PCD file: " << pcd_file << std::endl;
}

int main(int argc, char **argv)
{
    convert_bin_to_pcd("frame_0.bin", "frame_0.pcd");

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/886972.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

建筑资质的未来发展趋势

&#x1f3d7;️建筑资质是建筑企业进入市场的通行证&#xff0c;它不仅关系到企业的竞争力&#xff0c;也影响着整个建筑行业的健康发展。随着政策的调整和技术的进步&#xff0c;建筑资质管理正面临着新的变革。 1. 资质管理的数字化转型&#xff1a;&#x1f310; 随着信息技…

Gaussian-splatting 项目环境配置笔记(Win11)

如果你是配置别的项目的过程中用到了3D GS相关的内容&#xff0c;然后这部分内容环境一直配不好&#xff0c;也可以跟随这个博客配一下环境&#xff0c;配完后起码3D GS部分就搞定了。 文章目录 概述项目链接&#xff1a;VS2019直接下载链接CUDA不同版本下载链接安装Condasetup…

63.5 注意力提示_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录注意力提示生物学中的注意力提示查询、键和值注意力的可视化使用 show_heatmaps 显示注意力权重代码示例 代码解析结果 小结练习 注意力提示 &#x1f3f7;sec_attention-cues 感谢读者对本书的关注&#xff0c;因为读者的注意力是一种稀缺…

【MATLAB2024b】安装离线帮助文档(windows)

文章目录 一、在 MATLAB 设置中安装二、从math works 网站下载ISO&#xff1a;给无法联网的电脑安装三、重要说明 版本&#xff1a;matlab 2024b&#xff08;或者大于等于2023a&#xff09; 所需空间&#xff1a;10~15 GB 平台&#xff1a;Windows 需要注册math works账号。 一…

深度学习-19-深入理解并训练自己的Tokenizer分词器

文章目录 1 tokenization是什么2 Tokenization方法简介2.1 单词级的Tokenization2.2 子词Tokenization技术2.3 举例说明2.3.1 字符级别2.3.2 词语级别2.3.3 子词级别3 训练自己的Tokenizer3.1 下载数据集3.2 huggingface的Tokenizer实现3.3 my-tokenizer.json字段说明3.4 验证一…

鸿蒙harmonyos next flutter混合开发之开发package

​​​​​​ 创建 package flutter create --templatepackage mypackage package代码如下&#xff1a; 创建hello_world.dart ///HelloWorld返回hello world 拼接param class HelloWorld {String helloWorld(String param) > "hello world ${param}"…

【视频目标分割-2024CVPR】Putting the Object Back into Video Object Segmentation

Cutie 系列文章目录1 摘要2 引言2.1背景和难点2.2 解决方案2.3 成果 3 相关方法3.1 基于记忆的VOS3.2对象级推理3.3 自动视频分割 4 工作方法4.1 overview4.2 对象变换器4.2.1 overview4.2.2 Foreground-Background Masked Attention4.2.3 Positional Embeddings 4.3 Object Me…

CSS实现服务卡片

CSS实现服务卡片 效果展示 CSS 知识点 回顾整体CSS知识点灵活运用CSS知识点 页面整体布局 <div class"container"><div class"card"><div class"box"><div class"icon"><ion-icon name"color-pal…

Mac 卸载 IDEA 流程

1、现在应用程序中删除Idea 2、进入Library目录 cd /Users/zhengzhaoxiang/Library 3、删除IntelliJIdea2023.3&#xff08;根据自己的版本而定&#xff09;记得进去看下是否删除干净了 rm -rf Logs/JetBrains/IntelliJIdea2023.3 rm -rf Preferences/com.jetbrains.intel…

蘑菇分类检测数据集 21类蘑菇 8800张 带标注 voc yolo

蘑菇分类检测数据集 21类蘑菇 8800张 带标注 v 蘑菇分类检测数据集 21类蘑菇 8800张 带标注 voc yolo 蘑菇分类检测数据集介绍 数据集名称 蘑菇分类检测数据集 (Mushroom Classification and Detection Dataset) 数据集概述 该数据集专为训练和评估基于YOLO系列目标检测模型…

python爬虫 - 初识爬虫

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、爬虫的关键概念 &#xff08;一&#xff09;HTTP请求与响应 &#xff0…

uni-app在线预览pdf

这里推荐下载pdf.js 插件 PDF.js - Browse Files at SourceForge.net 特此注意 如果报 Promise.withResolvers is not a function 请去查看版本兼容问题 降低pdf.js版本提高node版本 下载完成后 在 static 文件夹下新建 pdf 文件夹&#xff0c;将解压文件放进 pdf 文件…

基于SpringBoot+Vue的摄影社团管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

API接口开发系列文章:构建高效、安全与可扩展的API服务

前言 在当今的数字化时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为连接不同系统、服务和应用的核心桥梁。无论是企业内部的数据交互&#xff0c;还是面向第三方的服务开放&#xff0c;API都扮演着至关重要的角色。本系列文章将深入探讨API接口开发的各个…

【nlp自然语言】知识图谱,全文检索,自然语言nlp,数据资产标签,集成管理平台

一、项目介绍 一款全源码&#xff0c;可二开&#xff0c;可基于云部署、私有部署的企业级知识库云平台&#xff0c;一款让企业知识变为实打实的数字财富的系统&#xff0c;应用在需要进行文档整理、分类、归集、检索、分析的场景。 为什么建立知识库平台&#xff1f; 助力企业…

雷池 WAF 如何配置才能正确获取到源 IP

经常有大哥反馈说雷池攻击日志里显示的 IP 有问题。 这里我来讲一下为什么一些情况下雷池显示的攻击 IP 会有问题。 问题说明 默认情况下&#xff0c;雷池会通过 HTTP 连接的 Socket 套接字读取客户端 IP。在雷池作为最外层网管设备的时候这没有问题&#xff0c;雷池获取到的…

【Linux】进程地址空间(初步了解)

文章目录 1. 奇怪的现象2. 虚拟地址空间3. 关于页表4. 为什么要有虚拟地址 1. 奇怪的现象 我们先看一个现象&#xff1a; 为什么父子进程从“同一块地址中”读取到的值不一样呢&#xff1f; 因为这个地址不是物理内存的地址 &#xff0c;如果是物理内存的地址是绝对不可能出…

Android Context是什么?有很多的context他们之间有什么区别?什么时候该使用哪个?

目录 一、Context是什么&#xff1f; 在Android中&#xff0c;Context是一个抽象类 &#xff0c;它代表了应用程序的当前状态&#xff0c;包括资源和类加载器等&#xff0c;它提供了一个应用运行所需的信息&#xff0c;比如我们要获取资源 &#xff0c;那么需要她&#xff0c;…

自动驾驶-轨迹拼接

自动驾驶在进行规划之前&#xff0c;要确定当前帧轨迹规划的起点&#xff0c;这个起点常被误认为是当前车辆的位置&#xff0c;即每次以车辆的当前位置进行轨迹规划&#xff1b;其实不是这样的&#xff0c;直观上&#xff0c;这会导致本次次规划的轨迹同上次规划的轨迹之间是不…

Hadoop之WordCount测试

1、Hadoop简介&#xff1a; Hadoop是Apache旗下的一个用Java语言实现的开源软件框架&#xff0c;是一个开发和运行处理大规模数据的软件平台。 Hadoop的核心组件包括Hadoop分布式文件系统&#xff08;HDFS&#xff09;和MapReduce编程模型。HDFS是一个高度容错的系统&#xf…