激光雷达采集的 PTS 格式数据怎么转成 PCD 格式
说明: 本文为笔记风格的技术文章,综合整理自 CSDN 及社区相关资料,涵盖 PTS 与 PCD 格式介绍及多种转换方法。
一、PTS 格式简介
PTS(Laser Scan Data Format) 是一种最简便的点云文本格式,通常来自激光雷达扫描仪(如 Leica 等设备)。
文件结构
<总点数>
<x1> <y1> <z1> <intensity> <r1> <g1> <b1>
<x2> <y2> <z2> <intensity> <r2> <g2> <b2>
...
- 第一行: 记录总点数
- 后续每行 7 个值:
- 前 3 个:
x,y,z坐标 - 第 4 个:强度值 intensity(0~255,表示反射比例)
- 后 3 个:RGB 颜色估计值(0~255,单无符号字节)
- 前 3 个:
PTS 示例
3
1.0 2.0 3.0 128 255 0 0
4.0 5.0 6.0 200 0 255 0
7.0 8.0 9.0 64 0 0 255
二、PCD 格式简介
PCD(Point Cloud Data) 是 PCL(Point Cloud Library)库的官方指定格式,支持 ASCII 和二进制两种存储方式。
PCD 文件头说明
# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z # 字段名称
SIZE 4 4 4 # 每字段字节数
TYPE F F F # 类型:F=浮点, I=有符号, U=无符号
COUNT 1 1 1 # 每字段元素数
WIDTH 1000 # 点云宽度(无序时为总点数)
HEIGHT 1 # 高度(无序时为1)
VIEWPOINT 0 0 0 1 0 0 0 # 获取视点
POINTS 1000 # 总点数
DATA ascii # 存储类型:ascii / binary / binary_compressed
注意: 文件头字段必须按以上顺序定义,否则 PCL 读取会出错。
三、PTS → PCD 转换方法
方法 1:Python 脚本转换(最通用)
import numpy as np
def pts_to_pcd(pts_path: str, pcd_path: str, with_rgb: bool = False):
"""
将 PTS 格式点云文件转换为 PCD 格式
Args:
pts_path: 输入的 .pts 文件路径
pcd_path: 输出的 .pcd 文件路径
with_rgb: 是否包含 RGB 颜色信息
"""
with open(pts_path, 'r') as f:
lines = f.readlines()
# 第一行是总点数
num_points = int(lines[0].strip())
# 剩余行是点数据
data_lines = [line.strip() for line in lines[1:1 + num_points] if line.strip()]
points = []
colors = []
intensities = []
for line in data_lines:
parts = line.split()
if len(parts) >= 3:
x, y, z = float(parts[0]), float(parts[1]), float(parts[2])
points.append([x, y, z])
if len(parts) >= 4:
intensities.append(float(parts[3]))
if len(parts) >= 7:
r, g, b = int(parts[4]), int(parts[5]), int(parts[6])
colors.append([r, g, b])
points = np.array(points, dtype=np.float32)
n = len(points)
# 写 PCD 文件头
with open(pcd_path, 'w') as f:
f.write("# .PCD v0.7 - Point Cloud Data file format\n")
f.write("VERSION 0.7\n")
if with_rgb and colors:
f.write("FIELDS x y z intensity rgb\n")
f.write("SIZE 4 4 4 4 4\n")
f.write("TYPE F F F F U\n")
f.write("COUNT 1 1 1 1 1\n")
elif intensities:
f.write("FIELDS x y z intensity\n")
f.write("SIZE 4 4 4 4\n")
f.write("TYPE F F F F\n")
f.write("COUNT 1 1 1 1\n")
else:
f.write("FIELDS x y z\n")
f.write("SIZE 4 4 4\n")
f.write("TYPE F F F\n")
f.write("COUNT 1 1 1\n")
f.write(f"WIDTH {n}\n")
f.write("HEIGHT 1\n")
f.write("VIEWPOINT 0 0 0 1 0 0 0\n")
f.write(f"POINTS {n}\n")
f.write("DATA ascii\n")
# 写点数据
for i in range(n):
line = f"{points[i][0]} {points[i][1]} {points[i][2]}"
if intensities:
line += f" {intensities[i]}"
if with_rgb and colors and i < len(colors):
# PCD 中 rgb 存为 32-bit 无符号整数
r, g, b = colors[i][0], colors[i][1], colors[i][2]
rgb_int = (int(r) << 16) | (int(g) << 8) | int(b)
line += f" {rgb_int}"
f.write(line + "\n")
print(f"[OK] 转换完成:{n} 个点 → {pcd_path}")
# ── 使用示例 ──────────────────────────────────────────
# 仅含 XYZ
# pts_to_pcd("input.pts", "output.pcd", with_rgb=False)
# 含 Intensity 和 RGB
# pts_to_pcd("input.pts", "output.pcd", with_rgb=True)
方法 2:使用 Open3D 库转换(推荐)
Open3D 提供了简洁的 I/O 接口,能自动识别多种格式:
import open3d as o3d
# 读取 PTS(Open3D 可能不原生支持 .pts,需要先 parse)
# 替代:先用 numpy 读成数组,再构造点云
import numpy as np
def pts_to_pcd_open3d(pts_path: str, pcd_path: str):
"""用 Open3D 转换 PTS → PCD"""
with open(pts_path, 'r') as f:
lines = f.readlines()
num_points = int(lines[0].strip())
data = np.loadtxt(pts_path, skiprows=1, max_rows=num_points)
# 取前 3 列作为 XYZ 坐标
xyz = data[:, :3]
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
# 如果有颜色信息(列数 >= 7)
if data.shape[1] >= 7:
colors = data[:, 4:7] / 255.0 # 归一化到 [0,1]
pcd.colors = o3d.utility.Vector3dVector(colors)
o3d.io.write_point_cloud(pcd_path, pcd)
print(f"[OK] Open3D 转换完成 → {pcd_path}")
# 使用
# pts_to_pcd_open3d("input.pts", "output.pcd")
方法 3:使用 PCL 库(C++)
如果已经配置好 PCL 环境,也可以用 C++ 完成转换:
#include <iostream>
#include <fstream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "用法: ./pts2pcd input.pts output.pcd" << std::endl;
return -1;
}
std::ifstream infile(argv[1]);
if (!infile.is_open()) {
std::cerr << "无法打开文件: " << argv[1] << std::endl;
return -1;
}
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
int num_points;
infile >> num_points;
for (int i = 0; i < num_points; ++i) {
pcl::PointXYZRGB point;
double intensity;
int r, g, b;
infile >> point.x >> point.y >> point.z
>> intensity >> r >> g >> b;
point.r = static_cast<uint8_t>(r);
point.g = static_cast<uint8_t>(g);
point.b = static_cast<uint8_t>(b);
cloud->push_back(point);
}
infile.close();
cloud->width = cloud->points.size();
cloud->height = 1;
cloud->is_dense = true;
pcl::io::savePCDFileASCII(argv[2], *cloud);
std::cout << "[OK] PCL 转换完成 → " << argv[2] << std::endl;
return 0;
}
编译:
cmake_minimum_required(VERSION 3.10)
project(pts2pcd)
find_package(PCL REQUIRED)
add_executable(pts2pcd pts2pcd.cpp)
target_link_libraries(pts2pcd ${PCL_LIBRARIES})
方法 4:使用 CloudCompare 可视化工具
CloudCompare 是一款免费的点云处理软件,支持图形界面操作:
- 打开 → 选择
.pts文件 - 设置分隔符(空格)、跳过首行(Skip first line = 1)、各列的字段映射(X/Y/Z 列号)
- 确认后点云即加载到视图中
- File → Save as → 选择
.pcd格式 → 保存
注意: CloudCompare 导出的 PCD 有时在 PCL 中读取强度信息可能有问题,建议用代码转换以确保字段映射正确。
四、常见问题与注意事项
4.1 PTS 中没有总行数怎么办?
有些 PTS 文件可能缺少第一行的总点数,此时需要自行统计行数:
def count_lines(filepath):
with open(filepath, 'r') as f:
return sum(1 for _ in f)
4.2 PTS 中强度/颜色缺失
- 若 PTS 只有
x y z(3 列),转 PCD 时 FIELDS 只写x y z - 若 PTS 有
x y z intensity(4 列),FIELDS 写x y z intensity - 若 PTS 有完整 7 列,可以按需求选择是否保留 RGB
4.3 PCD 的二进制 vs ASCII
| 模式 | 优点 | 缺点 |
|---|---|---|
| DATA ascii | 可读性好,可用文本编辑器查看 | 文件体积大,读写慢 |
| DATA binary | 文件小,读写快 | 不可读 |
| DATA binary_compressed | 文件最小 | 需要 PCL 1.0+ |
对于 PTS → PCD 转换,推荐先用 ASCII,调试确认无误后再改为 binary。
4.4 转换后 PCL 读取强度为 0
原因: 字段映射不一致。例如 PCD 中写的是 FIELDS x y z intensity,但 PCL 读入时可能期望 intensity 在第 4 列(float),如果顺序或类型不匹配,读到的值全是 0。
解决方案: 打开 PCD 检查文件头部,确保:
FIELDS x y z intensity
SIZE 4 4 4 4
TYPE F F F F
五、参考资源
| 资源 | 链接 |
|---|---|
| PCL 官方 PCD 格式文档 | https://pointclouds.org/documentation/tutorials/pcd_file_format.html |
| Open3D 官方文档 | https://www.open3d.org/docs/release/ |
| CloudCompare | https://www.cloudcompare.org/ |
| CSDN - 点云数据格式认识 | https://blog.csdn.net/qq_40719268/article/details/127673792 |
| CSDN - 速腾激光雷达点云格式转换 | https://blog.csdn.net/abanchao/article/details/127741041 |
六、总结
| 转换方式 | 适用场景 | 难度 |
|---|---|---|
| Python 脚本(纯手写) | 灵活控制字段映射、无第三方依赖 | ★★☆ |
| Python + Open3D | 快速转换、后续需要可视化/处理 | ★☆☆ |
| C++ + PCL | 已有 PCL 环境、需处理大规模点云 | ★★★ |
| CloudCompare | 单次图形化操作、少量文件 | ★☆☆ |
推荐路径: 日常少量转换用 CloudCompare;批量/自动化用 Python + Open3D;工业级管道用 C++ + PCL。