菜单

Administrator
发布于 2026-05-19 / 1 阅读
0
0

激光雷达 PTS 格式数据转 PCD 格式

激光雷达采集的 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,单无符号字节)

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 是一款免费的点云处理软件,支持图形界面操作:

  1. 打开 → 选择 .pts 文件
  2. 设置分隔符(空格)、跳过首行(Skip first line = 1)、各列的字段映射(X/Y/Z 列号)
  3. 确认后点云即加载到视图中
  4. 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


评论