当前位置: 首页 > news >正文

Matlab写入点云数据到Rosbag

最近有需要读取一个点云并做处理后,重新写回rosbag。网上有很多读取的教程,但没有写入。自己写入时也遇到了很多麻烦,踩了一堆坑进行记录。

1. rosbag中一个lidar的msg有哪些信息?

通过如下代码,先读取一个rosbag的lidar的msg:

bag = rosbag('E:\04_Data\Share\test_data\double-water.bag');
lidarTopic = '/velodyne_points';
lidarMsgs = readMessages(select(bag, 'Topic', lidarTopic));
N_lidar = size(lidarMsgs, 1);% 输出topic看一下:
lidarMsgs{1}

可以看到,输出的结果如下:
在这里插入图片描述
其中:
Header对应rosbag中的header
Fields对应rosbag中每个点有多少个“字段”,
Heightwidth中,height永远是1,width的数量和点云点数相同
IsBigendian大端模式,根据系统选择。我的系统是0。
PointStep,一个数据点的“step”,可以认为,一个数据点有多长。多长取决于有多少个Fields,以及每个Fields的数据类型
RowStep,数值等于 PointStep*Width,这个是计算出来的
Data,注意是84400*1的uint8类型。这个很重要。84400和RowStep一致,uint8是ros在传输数据时,将message中的所有数据都编码成uint8类型。
IsDense表示是否是稠密的?这个暂不清楚有什么用。

这里重点解释Data的长度、类型以及Fields是什么

我们进一步看Fields有哪些内容。这里有5个,我们先列出来第一个:
在这里插入图片描述
最上方的INT8~FLOAT64这几行,是说明,并不是实际的数据;这表示,在ROS中数据类型的编号(后面还会涉及在matlab中的数据类型,所以强调区分;例如,ROS中FLOAT32占4个bytes,对应matlab中应该用占4bytes的single类型转化)是什么。

1.2 Fileds字段详解

Fileds(1)的Name是’x’,表示“这个字段的含义是x”;
Offset是0,表示“这个字段在一个(长度为PointStep中的)数据点中的“起始位置”是0,也就是说从第1位开始读某个长度的字节Bytes,就是’x’的数值。读多长呢?往下看
Datatype是7,表示这个字段的数据类型是7,7是什么?上面给出了 ‘FLOAT32: 7’,即是一个FLOAT32类型。
Count是1,表示只有一个数据(lidar字段中好像所有的count都是1)。

那么,这表示,“每一个数据的第一个字段是,从第1个byte开始,读取类型FLOAT32(实际上是4个bytes)长度的数据,读取1个,这个字段的名称是x”。

同样,y和z也是如此,如下(第二个字段是y):
在这里插入图片描述
由于刚才讲到,Datatype=7表示一个FLOAT32,长度需要4个bytes,所以x的下一个字段y就要从Offset=4开始。

接下来看不同其他Fileds。这里我的LiDAR数据包括: x, y, z, intensity, ring信息,所以共有5个字段。需要注意的是,每个字段用什么类型,是和rosbag中读取和转化有关的,这又涉及到ros中PCL的转化,这里不做展开介绍,可以参考之前的博客:【学习记录】Ouster雷达运行fastlio提示 Failed to find match for field ‘ring‘ 的解决办法。我们这里只讲,数据是这个格式,在matlab里面是什么样子的。为什么用FLOAT32,或者uint16,这些是雷达驱动底层、代码接口定义的,这里不做探究。

在我的数据中,intensity也是FLOAT32类型,ring是uint16类型,所以具体的intensity如下,从第12个bytes开始读取7类型的数据:
在这里插入图片描述
ring的格式如下,从16tytes开始读取Datatype=4的数据:
在这里插入图片描述

至此,我们搞清楚了rosbag读取一个message后,有哪些数据了。

1.3 Data字段详解

可以看到,msg中的Data字段是一个: 84400*1的uint8的数据。
84400=4220*20,其中4220是总的lidar点数,20是一个点所包含的上述5个Fields的长度。

具体的,我们看一下msg.Data的具体内容,如下:

在这里插入图片描述
可以看出,Data字段的前20个数据,是一个完整的lidar点。注释如下:
在这里插入图片描述
不信的话,我们可以将x转化回FLOAT32类型,看一下是不是真实的x坐标,如下图。确实如此。typecast将一个数据转成指定的类型,这里用single是因为,MATLAB中的single类型对应ros中的FLOAT32,字节都是4bytes。
在这里插入图片描述
y、z、intensity和ring的验证这里不表。

还注意到,在长度为20的一个数据中,ring后面有2个0,这是为什么?因为ros要求4字节对齐,必须是4的整数倍。目前到ring总共有18个bytes,因此需要再补2个凑到20。这两个0是不影响读取的,因为Field(5)字段已经给出了从16开始读取uint16长度数据,因此会忽略后面的。

2. 创建msg

创建msg需要把所有字段都创建,重点是Data部分怎么处理。我们首先假设点云数据是这样的,10个随机数:

% 假设数据
K = 10;
xyz = rand(K,3);          % 100个点的XYZ坐标
intensity = rand(K);     % 随机强度值
ring = randi(16,K)-1;   % 环号(0~15)% 数据格式转化为对应的类型。matlab->ros对应关系:single->float32, uint16->uint16
xyz = single(xyz);          % 强制转换为FLOAT32
intensity = single(intensity); % 强制转换为FLOAT32
ring = uint16(ring);        % 强制转换为UINT16

2.1 msg各个字段创建

对Header, Field等字段创建。这里我们先不管Data怎么搞。

% 创建PointCloud2消息对象
lidarMsg = rosmessage('sensor_msgs/PointCloud2');% 设置消息头信息
lidarMsg.Header.Stamp = rostime('now');
lidarMsg.Header.FrameId = 'lidar_frame';% 获取点云数量
numPoints = size(xyz, 1);% 设置点云基本信息
lidarMsg.Height = 1;
lidarMsg.Width = numPoints;
lidarMsg.IsDense = true;% 定义字段(x, y, z, intensity, ring)
fields = {'x','y','z','intensity','ring'};
offsets = uint32([0, 4, 8, 12, 16]); % 各字段的字节偏移量
datatypes = [7, 7, 7, 7, 4];        % 7=FLOAT32, 4=UINT16
counts = [1, 1, 1, 1, 1];% 添加字段到消息
lidarMsg.Fields = [];
for i = 1:length(fields)field = rosmessage('sensor_msgs/PointField');field.Name = fields{i};field.Offset = offsets(i);field.Datatype = datatypes(i);field.Count = counts(i);lidarMsg.Fields = [lidarMsg.Fields; field];
end% 设置点云数据步长(每个点20字节)
lidarMsg.PointStep = 20;
lidarMsg.RowStep = lidarMsg.PointStep * lidarMsg.Width;
lidarMsg.IsBigendian = false; % 小端字节序

搞清楚上面数据的分析,具体的创建就简单多了。
需要注意的是,需要对应准确相应字段的数据格式。

2.2 重点关注Data字段

重点关注Data字段怎么创建。
Data是把所有点按顺序拉成一列,然后每个点有Fields里面的字段。所以首先需要把所有点先组成一个Data,再拉成列。需要注意:

  • Data数据是,先一个点(x,y,z,intensity, ring…),再下一个点,拼接的;而不是所有的x再所有的y这样拼接;
  • 拼接时,注意首先要把每个点的所有数据格式都转成uint8,而不是先拼一起再转uint8,因为拼接时会有自动转化导致格式错误;
  • 不足4字节的,补0

代码如下:

% 预分配内存并逐个点填充字节
dataBytes = zeros(numPoints, 20, 'uint8');
for i = 1:numPoints% 转换x, y, z, intensity为小端字节序xyzIntensityBytes = typecast([xyz(i,:), intensity(i)], 'uint8');% 转换ring为小端字节序ringBytes = typecast(ring(i), 'uint8');% 合并数据(16字节xyzIntensity + 2字节ring + 2字节填充)dataBytes(i,:) = [xyzIntensityBytes, ringBytes, 0, 0];
end
% 将数据转换为列向量并赋值
lidarMsg.Data = reshape(dataBytes', [], 1);		% 注意这里的转置,否则reshape时按列拼接不对。

3. 其他

Data字段并不要求每个Field必须是连续的。我某个雷达录制的数据,'z’和’intensity’的起始位置分别是8和16,显然12-16这4个Bytes是空的,但不影响。可能是LiDAR自身预留的字段。

后记

折腾了一个晚上加一个白天,才把这些问题搞清楚。 真是不容易啊。

相关文章:

Matlab写入点云数据到Rosbag

最近有需要读取一个点云并做处理后,重新写回rosbag。网上有很多读取的教程,但没有写入。自己写入时也遇到了很多麻烦,踩了一堆坑进行记录。 1. rosbag中一个lidar的msg有哪些信息? 通过如下代码,先读取一个rosbag的l…...

业务流程相关的权威认证和培训有哪些

业务流程的认证和培训种类繁多,旨在帮助专业人士掌握业务流程管理 (BPM) 的知识和技能,从而提升个人职业发展和组织运营效率。下面分别介绍: 一、 业务流程认证和培训的种类 业务流程的认证和培训可以大致分为以下几类,涵盖了不…...

基于Spring Boot的兴顺物流管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...

【算法系列】荷兰国旗问题:三指针法原地排序

一、题目(leetcode75 颜色分类 --三分数组) 二、思路 算法核心:三指针分治策略 该问题被称为“荷兰国旗问题”(Dutch National Flag Problem),由计算机科学家Edsger Dijkstra提出。其核心思想是通过三个指针将数组划分为三个区…...

DeepSeek R1本地+私有云版医疗AI部署开发成功案例技术剖析

1. 引言 1.1 研究背景与意义 随着科技的飞速发展,人工智能(AI)在医疗领域的应用正逐渐成为推动医疗行业变革的重要力量。近年来,医疗 AI 取得了显著的进展,从疾病诊断、药物研发到医疗管理等各个环节,AI 技术都展现出了巨大的潜力。它能够处理和分析海量的医疗数据,为…...

ARM64 Trust Firmware [五]

本章介绍 ATF 中的 Runtime Service 是如何定义和被调用的。 要了解 SMC&#xff0c;必须从 SMC 指令本身开始&#xff0c;其指令如下图&#xff1a; 指令格式为&#xff1a;SMC #<imm>&#xff0c;从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用&#xff…...

rkipc main.c 中 rk_param_init函数分析

rk_param_init函数 这个函数是用来读取配置文件进行参数配置 这个函数在 luckfox-pico/project/app/rk_smart_door/smart_door/common/uvc/param/param.c 中 这个函数在main函数中被调用 //通过-c 配置文件路径 把配置文件传进来 case c:rkipc_ini_path_ optarg;//调用&am…...

正确清理C盘空间

一.系统清理 正确清理C盘空间主要是删除不需要的文件和应用程序&#xff0c;以释放磁盘空间。以下是一些常用的方法&#xff1a; 删除临时文件&#xff1a;在Windows搜索框中输入“%temp%”&#xff0c;打开临时文件夹&#xff0c;将其中的文件全部删除。 清理回收站&#xf…...

http代理IP怎么实现?如何解决代理IP访问不了问题?

HTTP代理是一种网络服务&#xff0c;它充当客户端和目标服务器之间的中介。当客户端发送请求时&#xff0c;请求首先发送到代理服务器&#xff0c;然后由代理服务器转发到目标服务器。同样&#xff0c;目标服务器的响应也会先发送到代理服务器&#xff0c;再由代理服务器返回给…...

【Gin-Web】Bluebell社区项目梳理5:投票功能分析与实现

本文目录 一、投票功能投票流程实现代码redis投票 一、投票功能 投票流程 首先我们要明确&#xff0c;就是 谁&#xff08;哪个用户&#xff1a;userID&#xff09; 给 哪个帖子&#xff08;postID&#xff09; 投了 什么票&#xff08;赞成票or反对票&#xff09;。 赞成票…...

多人协同创作gitea

多人协同创作gitea 在多台设备上协同使用Gitea&#xff0c;主要是通过网络访问Gitea服务器上的仓库来进行代码管理和协作。以下是一些关键步骤和建议&#xff0c;帮助你在多台设备上高效地使用Gitea进行协作&#xff1a; 1. 确保Gitea服务可访问 首先&#xff0c;你需要确保…...

Java 大视界 -- Java 大数据未来十年的技术蓝图与发展愿景(95)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

idea连接gitee完整教程

文章目录 idea连接gitee(使用idea远程兼容gitee) 使用idea远程兼容gitee并反向创建仓库和分支...

问卷数据分析|SPSS实操之相关分析

皮尔逊还是斯皮尔曼的选取主要看数据的分布 当数据满足正态分布且具有线性关系时&#xff0c;用皮尔逊相关系数 当有一个不满住时&#xff0c;用斯皮尔曼相关系数 1. 选择分析--相关--双变量 2. 将Z1-Y2加入到变量中&#xff0c;选择皮尔逊 3. 此处为结果&#xff0c;可看我案…...

汽配出库软件助力企业数字化转型,佳易王汽配出入库管理系统操作教程

一、概述 本实例以佳易王汽配出入库管理系统V17.1版本为例说明&#xff0c;其他版本可参考本实例。试用版软件资源可到文章最后了解&#xff0c;下载的文件为压缩包文件&#xff0c;请使用免费版的解压工具解压即可试用。 软件特点&#xff1a; 1、软件为绿色免安装版&#…...

二叉树(中等题)

1、先序&#xff0c;中序遍历确定二叉树 105 方法一、 前提 ① 必须不能有重复元素② 只有先序&#xff0b;中序和后序&#xff0b;中序才能实现唯一树 思考要点&#xff1a; 不要想着用for循环&#xff0c;递归一定更好解决输入是vector&#xff0c;递归就得考虑传入索…...

Versal - 基础6(Linux 开发 AIE-ML + 自动化脚本解析)

目录 1. 简介 2. 步骤解析 2.1 概览 2.1.1 步骤依赖关系 2.1.2 总目录结构 2.2 Vitis XPFM 2.2.1 Dir 2.2.2 Makefile 2.2.3 vitis_pfm.py 2.3 Kernels 2.3.1 Dir 2.3.2 Makefile 2.3.3 config 文件 2.4 AIE_app 2.4.1 Dir 2.4.2 Makefile 2.4.3 aie 要点 2.…...

什么是矩阵账号?如何高效运营tiktok矩阵账号

‍‌​​‌‌​‌​‍‌​​​‌‌​​‍‌​​​‌​‌​‍‌​​‌​​‌​‍‌​‌‌​‌‌‌‍‌​‌​‌​​​‍‌​​‌​‌‌​‍‌​​​​‌‌​‍‌​‌​​‌‌‌‍‌​​‌‌​‌​‍‌​‌​​‌‌‌‍‌​‌‌‌​​‌‍‌‌​​‌‌‌​‍‌‌​​‌‌​​‍‌…...

tortoiseSVN 如何克隆项目到本地

导入项目成功&#xff0c;如下图&#xff1a;...

趣解PostGet请求的原理、应用场景及区别

趣解Post&Get请求的原理、应用场景及区别 POST 和 GET 的「身份之谜」&#xff1a;快递员与侦探的终极对决 一、角色设定&#xff1a;快递员&#xff08;POST&#xff09; vs 侦探&#xff08;GET&#xff09; GET&#xff08;侦探&#xff09;&#xff1a; 任务&#xff…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...