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中每个点有多少个“字段”,
Height和width中,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,必须从 SMC 指令本身开始,其指令如下图: 指令格式为:SMC #<imm>,从官方文档了解到该指令只能在 EL1 以及更高的异常等级上调用ÿ…...
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盘空间主要是删除不需要的文件和应用程序,以释放磁盘空间。以下是一些常用的方法: 删除临时文件:在Windows搜索框中输入“%temp%”,打开临时文件夹,将其中的文件全部删除。 清理回收站…...
http代理IP怎么实现?如何解决代理IP访问不了问题?
HTTP代理是一种网络服务,它充当客户端和目标服务器之间的中介。当客户端发送请求时,请求首先发送到代理服务器,然后由代理服务器转发到目标服务器。同样,目标服务器的响应也会先发送到代理服务器,再由代理服务器返回给…...
【Gin-Web】Bluebell社区项目梳理5:投票功能分析与实现
本文目录 一、投票功能投票流程实现代码redis投票 一、投票功能 投票流程 首先我们要明确,就是 谁(哪个用户:userID) 给 哪个帖子(postID) 投了 什么票(赞成票or反对票)。 赞成票…...
多人协同创作gitea
多人协同创作gitea 在多台设备上协同使用Gitea,主要是通过网络访问Gitea服务器上的仓库来进行代码管理和协作。以下是一些关键步骤和建议,帮助你在多台设备上高效地使用Gitea进行协作: 1. 确保Gitea服务可访问 首先,你需要确保…...
Java 大视界 -- Java 大数据未来十年的技术蓝图与发展愿景(95)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
idea连接gitee完整教程
文章目录 idea连接gitee(使用idea远程兼容gitee) 使用idea远程兼容gitee并反向创建仓库和分支...
问卷数据分析|SPSS实操之相关分析
皮尔逊还是斯皮尔曼的选取主要看数据的分布 当数据满足正态分布且具有线性关系时,用皮尔逊相关系数 当有一个不满住时,用斯皮尔曼相关系数 1. 选择分析--相关--双变量 2. 将Z1-Y2加入到变量中,选择皮尔逊 3. 此处为结果,可看我案…...
汽配出库软件助力企业数字化转型,佳易王汽配出入库管理系统操作教程
一、概述 本实例以佳易王汽配出入库管理系统V17.1版本为例说明,其他版本可参考本实例。试用版软件资源可到文章最后了解,下载的文件为压缩包文件,请使用免费版的解压工具解压即可试用。 软件特点: 1、软件为绿色免安装版&#…...
二叉树(中等题)
1、先序,中序遍历确定二叉树 105 方法一、 前提 ① 必须不能有重复元素② 只有先序+中序和后序+中序才能实现唯一树 思考要点: 不要想着用for循环,递归一定更好解决输入是vector,递归就得考虑传入索…...
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 如何克隆项目到本地
导入项目成功,如下图:...
趣解PostGet请求的原理、应用场景及区别
趣解Post&Get请求的原理、应用场景及区别 POST 和 GET 的「身份之谜」:快递员与侦探的终极对决 一、角色设定:快递员(POST) vs 侦探(GET) GET(侦探): 任务ÿ…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
