1. 矿用激光雷达点云去噪与地面分割预处理
井下点云为什么必须先“洗”再“分”
矿用激光雷达在潮湿、高粉尘巷道中采集的原始点云,主要混入两类干扰:一是悬浮粉尘与水滴形成的近距密集噪点,它们在空间上随机散布、反射强度普遍偏低;二是巷道壁面水膜与煤泥造成的强度异常点,其距离正常但反射强度偏离同材质统计区间。若直接送入配准,这些点会污染法向估计与特征匹配,导致位姿解算抖动。因此预处理分两步:先按距离门限 + 反射强度门限做滤波,再按几何高度与法向把地面与侧壁分开,输出干净的地面点集与壁面点集。
滤波参数怎么定:距离与强度的双门限
设单帧点云为 P = {p_i},每点含 (x, y, z, intensity),雷达安装高度 h_mount(示例取 1.2 m),巷道断面近似矩形。滤波规则如下:
- 距离门限:剔除
r_i < r_min的近距粉尘团。示例r_min = 0.8 m,因为该距离内多为镜头前悬浮颗粒与水滴。 - 强度门限:粉尘与水滴回波弱,示例取
I_min = 25(0–255 归一化强度),低于该值判为噪点。 - 统计离群剔除:对剩余点做 k 近邻平均距离检验,邻域
k = 10,均值距离超过μ + 2σ的点剔除,用于清理稀疏飞点。
地面分割采用“高度带 + 法向”联合判据:地面点满足 |z_i + h_mount| < d_ground(示例 d_ground = 0.15 m,即相对巷道底板高度带),且局部法向与竖直方向夹角 θ < 25°;侧壁点满足法向近似水平(与竖直方向夹角 > 65°)。这样可避免把斜坡底板误判为壁面。
可运行的预处理实现(Python + NumPy)
下面代码自包含:生成一帧带粉尘噪点与壁面强度异常的模拟巷道点云,执行双门限滤波、统计离群剔除、法向估计与地面/侧壁分割,并打印各阶段点数与噪点率,便于验证参数是否合理。
import numpy as np
rng = np.random.default_rng(7)
# ---------- 1. 构造模拟巷道点云(示例数据,非实测) ----------
def make_frame(n_ground=20000, n_wall=20000, n_dust=3000, n_water=1500):
# 地面:z = -1.2(雷达高1.2m,底板在下方),x 沿巷道 0~20m,y 宽 -2~2m
gx = rng.uniform(0, 20, n_ground)
gy = rng.uniform(-2, 2, n_ground)
gz = -1.2 + rng.normal(0, 0.02, n_ground)
gi = rng.normal(120, 12, n_ground) # 地面反射较强
ground = np.c_[gx, gy, gz, gi]
# 侧壁:y = ±2,z 在 -1.2~1.0
side = rng.choice([-2.0, 2.0], n_wall)
wx = rng.uniform(0, 20, n_wall)
wz = rng.uniform(-1.2, 1.0, n_wall)
wi = rng.normal(95, 15, n_wall)
wall = np.c_[wx, side + rng.normal(0, 0.02, n_wall), wz, wi]
# 粉尘噪点:近距随机散布,强度低
dx = rng.uniform(0, 1.0, n_dust)
dy = rng.uniform(-1.5, 1.5, n_dust)
dz = rng.uniform(-1.0, 1.0, n_dust)
di = rng.uniform(5, 22, n_dust)
dust = np.c_[dx, dy, dz, di]
# 壁面水膜异常点:距离正常但强度异常低
ax = rng.uniform(0, 20, n_water)
ay = rng.choice([-2.0, 2.0], n_water)
az = rng.uniform(-1.2, 1.0, n_water)
ai = rng.uniform(8, 24, n_water)
water = np.c_[ax, ay, az, ai]
return np.vstack([ground, wall, dust, water])
pts = make_frame()
print(f"原始点数: {len(pts)}")
# ---------- 2. 距离 + 强度双门限滤波 ----------
xyz, inten = pts[:, :3], pts[:, 3]
r = np.linalg.norm(xyz, axis=1)
mask = (r >= 0.8) & (inten >= 25)
pts_f = pts[mask]
print(f"双门限后: {len(pts_f)} 剔除 {len(pts)-len(pts_f)}")
# ---------- 3. 统计离群剔除(k近邻平均距离) ----------
def statistical_outlier(p, k=10, std_ratio=2.0):
from scipy.spatial import cKDTree
tree = cKDTree(p[:, :3])
d, _ = tree.query(p[:, :3], k=k + 1)
mean_d = d[:, 1:].mean(axis=1)
thr = mean_d.mean() + std_ratio * mean_d.std()
return p[mean_d <= thr]
pts_f = statistical_outlier(pts_f)
print(f"离群剔除后: {len(pts_f)}")
# ---------- 4. 法向估计(PCA) ----------
def estimate_normals(p, radius=0.3):
from scipy.spatial import cKDTree
tree = cKDTree(p[:, :3])
normals = np.zeros((len(p), 3))
for i, q in enumerate(p[:, :3]):
idx = tree.query_ball_point(q, radius)
if len(idx) < 5:
normals[i] = [0, 0, 1]
continue
nb = p[idx, :3] - p[idx, :3].mean(axis=0)
_, _, vh = np.linalg.svd(nb, full_matrices=False)
normals[i] = vh[-1]
return normals
normals = estimate_normals(pts_f)
theta = np.degrees(np.arccos(np.clip(np.abs(normals[:, 2]), 0, 1))) # 与竖直夹角
# ---------- 5. 高度带 + 法向联合分割 ----------
h_mount, d_ground = 1.2, 0.15
z_rel = pts_f[:, 2] + h_mount
ground_mask = (np.abs(z_rel) < d_ground) & (theta < 25)
wall_mask = theta > 65
ground_pts = pts_f[ground_mask]
wall_pts = pts_f[wall_mask]
print(f"地面点: {len(ground_pts)} 侧壁点: {len(wall_pts)}")
# ---------- 6. 噪点率评估 ----------
# 以“距地面/壁面理论位置超过 0.1m 且强度低于 30”近似统计残留噪点
residual = pts_f[(np.abs(pts_f[:, 2] + 1.2) > 0.1) &
(np.abs(np.abs(pts_f[:, 1]) - 2.0) > 0.1) &
(pts_f[:, 3] < 30)]
print(f"残留噪点率: {len(residual)/len(pts_f)*100:.2f}%")
运行后应看到:原始约 4.45 万点,双门限剔除近距粉尘与弱回波后显著下降,统计离群再清理少量飞点;地面点与侧壁点数量接近理论比例,残留噪点率通常可压到 3% 以下。若噪点率偏高,优先检查 I_min 是否过低或 r_min 是否偏小。
现场标定时,先采集一段无粉尘的静态帧统计地面与壁面强度分布,取壁面强度 5% 分位数作为
I_min 初值;再在粉尘段上微调,避免把真实弱反射壁面(如湿煤壁)误删。结果判读与常见问题
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 地面点大量丢失 | d_ground 过小或底板有坡度 | 放宽到 0.2 m,或改用 RANSAC 拟合地平面 |
| 侧壁点混入地面 | 法向估计邻域过大,壁面被平滑 | 减小邻域半径至 0.2 m,重估法向 |
| 残留噪点率高 | I_min 偏低,水滴回波未滤除 | 提高 I_min 至 30,或加距离-强度联合阈值 |
| 管道被误删 | 管道强度低于门限 | 对 r > 3 m 区域单独放宽强度门限 |
完成本步后,输出的 ground_pts 与 wall_pts 即为后续帧间配准的干净输入;地面点可用于估计底板平面约束,壁面点用于提取结构化特征。