障碍感知 | 基于2D激光点云的行人检测器DROW算法详解(附Python实现与ROS仿真)
DROW检测器是第一个基于深度学习的2D激光雷达行人检测算法。本文基于DROW数据集训练DROW检测器,并将其部署到ROS行人仿真环境中测试检测效果,为室内机器人的环境感知打下基础
1 DROW算法简介
检测人类是服务机器人在应用中的关键技能,许多检测算法已在3D激光雷达中得到了发展,这些方法大致可以分为两类。第一类方法使用点云数据的有序投影,第二类方法则通过设计神经网络架构处理无序的点云数据并对其进行推理,例如PointNet
和VoxelNet
。然而与室外场景(如自动驾驶等)不同,对于许多服务型和家用应用中的移动机器人来说,广泛采用的是水平安装的2D激光扫描仪,目前,针对使用2D激光雷达数据进行人类检测的研究仍然较少。
以往的算法大多依赖手工设计的特征,而DROW检测器是第一个基于深度学习的行人检测算法。DROW
通过在网络中提供一个小的、完全在线的时间窗口进一步提高了检测精度。DROW
算法提供了DROW数据集,加入了人物标注,使其成为目前最大的2D距离数据人物标注数据集,数据采集在实际环境中经过几天的记录,具有较高的多样性。
2 平面点云聚合算法
由于激光雷达点空间密度随距离变化明显,直接应用网络进行对激光雷达信息进行目标检测,网络的感受野必须覆盖物体的大部分。但是激光雷达附近的物体包含大量的激光束,而远处的物体只能被少量的激光束击中,这使得网络容易对训练场景的背景产生过拟合。
为了避免背景信息过拟合问题,DROW
算法实现激光雷达数据中的人体目标检测。利用激光雷达提供的真实世界尺度信息,同时采用深度引导的滑动窗口法对激光数据点进行预处理,使待检对象在不同的距离上有大致相同的信息表现,从而降低在不同距离上隐藏层对不同特征输入的需要,归一化不同深度的特征,减小对特征工程的需求,提高目标检测准确性。
具体处理的算法阐述如下:在给定时间 t t t内由 N N N个点 { s n t } n = 1 N \{s_n^t\}_{n=1}^N {snt}n=1N组成的激光雷达数据 r t ∈ R N r^t\in R^N rt∈RN,生成 N N N个局部感知的视野窗口 { c n t } n = 1 N \{c_n^t\}_{n=1}^N {cnt}n=1N对应激光雷达点 s n t s_n^t snt周围欧几里得空间的固定窗口。通过计算每个点 s n t s_n^t snt的开角 α n t \alpha_n^t αnt确定窗口大小
α n t = 2 arctan W 2 r n t \alpha_n^t=2\arctan \frac{W}{2r_n^t} αnt=2arctan2rntW
其中 W = 1.0 W =1.0 W=1.0为预先设定的窗口宽度的超参数, r n t r_n^t rnt为点 s n t s_n^t snt的距离测量值,即激光点的距离信息。之后将开角 α n t \alpha_n^t αnt邻域内的激光雷达测量数据以固定数量 M M M重新进行线性采样,将窗口内的激光数据点通过减去中心点距离 r n t r_n^t rnt使实现去均值处理。经过处理后的激光雷达点无论距离 r n t r_n^t rnt为何值,卷积神经网络的感受野总是覆盖相同尺寸的真实范围。去除在 [ − H r , + H r ] [-H_r, +H_r] [−Hr,+Hr]的深度范围之外的背景和前景数据点,其中 H r = 1.0 H_r=1.0 Hr=1.0为窗口深度的超参数。并且最后将所获得窗口 c n t c_n^t cnt中的所有值归一化至 [ − 1 , 1 ] [−1,1] [−1,1],减少不同维度取值范围的差异带来的干扰。
将归一化后的 c n t c_n^t cnt进行分类与回归,通过激活函数 S o f t M a x SoftMax SoftMax输出对每个窗口是否属于为人体腿部类别进行分类,如果为感兴趣目标,则回归窗口的中心位置进行分类投票。由于激光雷达信息作为基于距离的范围数据本质上具有旋转不变性,则以当前激光信息点为中心对齐的坐标系取代绝对坐标系 ( x , y ) (x, y) (x,y)对其偏移量 ( Δ x , Δ y ) (\Delta x, \Delta y) (Δx,Δy)进行加权投票,最后使用非极大值抑制合并投票并获得检测质心
一个由激光点云映射的二维聚合窗口实例如下所示,颜色代表距离的不同
3 DROW算法网络架构
DROW
算法网络架构如下所示
其中原始的平面的激光点云通过聚合雷达窗口映射为2D类图像形式的输入,经过若干卷积池化操作,输出到特征层。最终使用两个特征提取头,分别输出一维的行人标签和二维的行人坐标
4 仿真实现
4.1 Python实现
DROW
检测器的核心实现如下所示
class DrowNet(nn.Module):
def __init__(self, loss_kwargs: dict, model_kwargs: dict) -> None:
super(DrowNet, self).__init__()
self.dropout = model_kwargs["DROPOUT"]
self.max_num_pts = model_kwargs["MAX_NUM_PTS"]
self.criterion = DrowNetCriterion(loss_cfg=loss_kwargs)
self.conv_block_1 = nn.Sequential(
_conv1d_3(1, 64), _conv1d_3(64, 64), _conv1d_3(64, 128)
)
self.conv_block_2 = nn.Sequential(
_conv1d_3(128, 128), _conv1d_3(128, 128), _conv1d_3(128, 256)
)
self.conv_block_3 = nn.Sequential(
_conv1d_3(256, 256), _conv1d_3(256, 256), _conv1d_3(256, 512)
)
self.conv_block_4 = nn.Sequential(_conv1d_3(512, 256), _conv1d_3(256, 128))
self.conv_cls = nn.Conv1d(128, 1, kernel_size=1)
self.conv_reg = nn.Conv1d(128, 2, kernel_size=1)
# initialize weights
for m in self.modules():
if isinstance(m, (nn.Conv1d, nn.Conv2d)):
nn.init.kaiming_normal_(m.weight, a=0.1, nonlinearity="leaky_relu")
elif isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def __str__(self) -> str:
return "DROW"
def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]:
'''
Forward process
Parameters
----------
x: Tensor
input data with dim [B, CT, N, L] --- (batch, cutout, scan, points per cutout)
Return
----------
pred_cls: Tensor
predicted class with dim [B, CT, 1]
pred_reg: Tensor
predicted regression with dim [B, CT, 2]
'''
n_batch, n_cutout, n_scan, n_pts = x.shape
# forward cutout from all scans
out = x.view(n_batch * n_cutout * n_scan, 1, n_pts)
out = self._conv_and_pool(out, self.conv_block_1) # /2
out = self._conv_and_pool(out, self.conv_block_2) # /4
# (batch, cutout, scan, channel, pts)
out = out.view(n_batch, n_cutout, n_scan, out.shape[-2], out.shape[-1])
# combine all scans
out = torch.sum(out, dim=2) # (B, CT, C, L)
# forward fused cutout
out = out.view(n_batch * n_cutout, out.shape[-2], out.shape[-1])
out = self._conv_and_pool(out, self.conv_block_3)
out = self.conv_block_4(out)
out = F.avg_pool1d(out, kernel_size=int(out.shape[-1]))
pred_cls = self.conv_cls(out).view(n_batch, n_cutout, -1)
pred_reg = self.conv_reg(out).view(n_batch, n_cutout, 2)
return pred_cls, pred_reg
4.2 行人仿真
在DROW
数据集中的测试效果如下所示
其中蓝色圆圈是真值,红色圆圈是行人检测值,可以看到跟踪效果非常好
为了实际的部署,需要在ROS中进一步测试。在pedestrian.yaml
文件中配置并启动DROW
检测器,其中model
选择DROW3
,ckpt_jrdb_ann_drow3_e40.pth
是模型对应的权重文件
pedestrians:
update_rate: 5
ped_tracker:
enable: true
model: DROW3
weight: ckpt_jrdb_ann_drow3_e40.pth
实时监测效果如下
完整工程代码请联系下方博主名片获取
🔥 更多精彩专栏:
更多推荐
所有评论(0)