lidar坐标系
lidar坐标系可以简单归纳为标准lidar坐标系和nucense lidar坐标系,参考链接。标准lidar坐标系方向和车辆的ego坐标系是一致的。
- 标准lidar坐标系
opendet3d,mmdetection3d和kitt都i使用了该坐标系。x朝车辆行驶方向,y左,z上。
up z
^ x front
| /
| /
left y <------ 0
kitti采集平台传感器安装示意图如下,其中红色圆圈标记的为lidar坐标系。
后面说的global yaw就是目标与’-y’的夹角,与’-y’重合时是0, 与x重合为90度。
- nucense lidar坐标系
nucense传感器坐标系示意图如下,可以看出lidar坐标系和和标准lidar坐标系有个90度的旋转关系。up z ^ y front | / | / o------ > x right
``
需要注意的是,mmdetection3d的nuscenes_dataset.py中,在未对齐nuscnese 和标准lidar坐标系就直接转成了LiDARInstance3DBoxes
gt_bboxes_3d = LiDARInstance3DBoxes(
ann_info['gt_bboxes_3d'], # nucense lidar coord.
box_dim=ann_info['gt_bboxes_3d'].shape[-1],
origin=(0.5, 0.5, 0.5)).convert_to(self.box_mode_3d)
github上的解释如下,看样子只是想用LiDARInstance3DBoxes包装下,然后利用它的接口函数 :
@starnstar. Hi, we have set the origin as (0.5,0.5,0.5) in NuScenes to fit the original box coordinates in NuScenes. Then, we can just use the interface of the class LiDARInstance3DBoxes, such as ‘center’, ‘corners’, ‘height’ and so on, ignoring the inner complex translation.
local yaw & global yaw
由于透视投影的关系,目标在相平面上的成像会同时收到目标转动和相对相机位移的双重影响。所以引出了local yaw和global yaw。
网络学习的对象为local yaw(下面的 α z \alpha_z αz, 其中 α z = α x + p i / 2 \alpha_z = \alpha_x + pi/2 αz=αx+pi/2), 推理时根据目标位置+local yaw计算出global yaw。
α x \alpha_x αx在kitti数据集中的定义为:
α∈[−π,π],即从 −180∘ 到 180∘。
α=0:目标物体的方向与相机光轴完全对齐(面向相机)。
α>0:目标物体的朝向偏向相机光轴的 左侧(逆时针方向)。
α<0:目标物体的朝向偏向相机光轴的 右侧(顺时针方向)。
部分公司2d目标标注的local yaw:目标与相机z同向重叠:90度,与右侧方向的相机x轴重叠:0度。
global yaw为[-pi, pi]之间,一般正前方为0,左边为90,右边-90. 参考lidar_box3d.py中的定义:
class LiDARInstance3DBoxes(BaseInstance3DBoxes):
"""3D boxes of instances in LIDAR coordinates.
Coordinates in LiDAR:
.. code-block:: none
up z x front (yaw=0)
^ ^
| /
| /
(yaw=0.5*pi) left y <------ 0
The relative coordinate of bottom center in a LiDAR box is (0.5, 0.5, 0),
and the yaw is around the z axis, thus the rotation axis=2. The yaw is 0 at
the positive direction of x axis, and increases from the positive direction
of x to the positive direction of y.
Attributes:
tensor (Tensor): Float matrix with shape (N, box_dim).
box_dim (int): Integer indicating the dimension of a box. Each row is
(x, y, z, x_size, y_size, z_size, yaw, ...).
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
"""
YAW_AXIS = 2
@property
def corners(self) -> Tensor:
"""Convert boxes to corners in clockwise order, in the form of (x0y0z0,
x0y0z1, x0y1z1, x0y1z0, x1y0z0, x1y0z1, x1y1z1, x1y1z0).
.. code-block:: none
up z
front x ^
/ |
/ |
(x1, y0, z1) + ----------- + (x1, y1, z1)
/| / |
/ | / |
(x0, y0, z1) + ----------- + + (x1, y1, z0)
| / . | /
| / origin | /
left y <------- + ----------- + (x0, y1, z0)
(x0, y0, z0)
Returns:
Tensor: A tensor with 8 corners of each box in shape (N, 8, 3).
"""
if self.tensor.numel() == 0:
return torch.empty([0, 8, 3], device=self.tensor.device)
dims = self.dims
corners_norm = torch.from_numpy(
np.stack(np.unravel_index(np.arange(8), [2] * 3), axis=1)).to(
device=dims.device, dtype=dims.dtype)
corners_norm = corners_norm[[0, 1, 3, 2, 4, 5, 7, 6]]
# use relative origin (0.5, 0.5, 0)
corners_norm = corners_norm - dims.new_tensor([0.5, 0.5, 0])
corners = dims.view([-1, 1, 3]) * corners_norm.reshape([1, 8, 3])
# rotate around z axis
corners = rotation_3d_in_axis(
corners, self.tensor[:, 6], axis=self.YAW_AXIS)
corners += self.tensor[:, :3].view(-1, 1, 3)
return corners
mmdetection3d box_3d_mode.py中定义的各种坐标系:
class Box3DMode(IntEnum):
"""Enum of different ways to represent a box.
Coordinates in LiDAR:
.. code-block:: none
up z
^ x front
| /
| /
left y <------ 0
The relative coordinate of bottom center in a LiDAR box is (0.5, 0.5, 0),
and the yaw is around the z axis, thus the rotation axis=2.
Coordinates in Camera:
.. code-block:: none
z front
/
/
0 ------> x right
|
|
v
down y
The relative coordinate of bottom center in a CAM box is (0.5, 1.0, 0.5),
and the yaw is around the y axis, thus the rotation axis=1.
Coordinates in Depth:
.. code-block:: none
up z
^ y front
| /
| /
0 ------> x right
The relative coordinate of bottom center in a DEPTH box is (0.5, 0.5, 0),
and the yaw is around the z axis, thus the rotation axis=2.
"""
SMOKE: Single-Stage Monocular 3D Object Detection via Keypoint Estimation 对local yaw给出了示意图:
- globa2local转换
参考fcos3d代码:
def _get_target_single(..):
#...
# change orientation to local yaw
gt_bboxes_3d[..., 6] = -torch.atan2(
gt_bboxes_3d[..., 0], gt_bboxes_3d[..., 2]) + gt_bboxes_3d[..., 6]
https://mmdetection3d.readthedocs.io/en/v1.0.0rc1/tutorials/coord_sys_tutorial.html 中global yaw是相对目标中轴与x轴的夹角。x->y方向为正。
3D Bounding Box Estimation Using Deep Learning and Geometry 中对于yaw有类似的使用:
gpt老师给的解释:
直观理解:
如果把射线当作参考线
先从水平线旋转 θ_ray 得到射线方向
再从射线方向旋转 θ_l 得到物体朝向
那么物体相对于水平线的总旋转角度就是 θ = θ_ray + θ_l
带你玩转 3D 检测和分割(二):核心组件分析之坐标系和 Box 这篇文章则对box3d有更详细的描述。
如上图所示,假设物体的长宽高分别为 l, w, h, 其朝向为如图所示,我们可以根据上述的定义表示该物体 3D 框在各个坐标系下的值,这里我们默认三个坐标系原点是重合的:
1)激光雷达坐标系:该物体底部中心点坐标为 (l/2, w/2, 0) ,朝向和 x 轴的夹角为 0,即 yaw 角为 0,此时沿着 x-y-z 三个轴方向的长度即为 x_size, y_size, z_size 的值,分别为 l、w、h,所以该坐标系下 Box 的值为 (l/2, w/2, 0, l, w, h, 0)。
2)深度坐标系:该物体底部中心点坐标为 (-w/2, l/2, 0),此时朝向和 x 轴的夹角为 90 度,即 yaw 角为 pi/2,需要注意的是,为了获得 x_size, y_size, z_size, 我们需要将物体旋转到和 x 轴平行,此时沿着x-y-z三个轴方向的长度分别为 l、w、h,所以该坐标系下的 Box 的值为 (-w/2, l/2, 0, l, w, h, pi/2)。
3)相机坐标系:该物体底部中心点坐标为(-w/2, 0, l/2), 此时朝向和 x 轴的夹角为 90 度,需要注意的是,根据前面的定义 yaw 角为 -pi/2,同样的为了获得 x_size, y_size, z_size, 我们需要将物体旋转到和 x 轴平行,此时沿着 x-y-z 三个轴方向的长度分别为 l、h、w,所以该坐标系下的 Box 的值为 (-w/2, 0, l/2, l, h, w, -pi/2)。
可以看到,一个物体 3D 框中的 (x_size, y_size, z_size) 在激光雷达坐标系和深度坐标系中为 (l, w, h),而在相机坐标系中为 (l, h, w)。
论文汇总
smoke
img -> (h, w, l, x, y, z, theta), theta:yaw, car’s roll and pitch is 0.
img -> keypoint branch -> (xc, yc), (xc, yc):3d box中心经过内参投影到图像上的2d坐标。
-> regression branch(1/8 featmap). (delta_z, delta_x, delta_y, delta_h, delta_w, delta_l, sin_a, cos_a)
keypoint branch出的xc, yc 再加上regression branch 出的深度和delta_xy, 可以获取目标在相机坐标系下的坐标: (x,y,z) = K^{-1}*(z(x_c +delta_xc), z(y_c + delta_yc), z)
foc3d
regression branch outputs:dx,dy,d,w,l,h,theta,vx,vy, direction_cls, centerness.
cls branch outputs: cls_labels and attribute_labels.
- loss
direction_cls: softmax cls loss.
centerness:BCE
regression outputs: L1.
改动点:
- label assignment:area based -> dist based.
对于某一层featuremap,当一个grid point在多个gt框中时,fcos3d使用dist-based方法(即选择中心点离grind point最近的gt box),而不是传统的area-based(选择面积最小的gt box),
统计显示这种策略对trailer货车这种大的目标有更好的BPR(参考fig4) - centerness. 4个边距离描述->中心点距离描述
fcos2d中centerness=sqrt((min(l,r) * min(t,b)/(max(l,r) * max(t,b)))).通过grid point到gt box的4个边来描述centerness。
但是fcos3d中,centerness=e{-a(dx2+dy^)}, dx2+dy2为到grid point 到3d box中心的2d投影点的距离。它的范围为0-1,所以使用BCE loss。