Беспилотный автомобиль едет в тумане. Камера не видит разметку. LiDAR теряет дальность из-за осадков. RADAR фиксирует помехи от отражений. Каждый сенсор по отдельности имеет слепые зоны: камера бесполезна в темноте, LiDAR — в сильный дождь, а радар не различает пешехода и металлический столб. Только объединение данных (multi-modal fusion) даёт робастность на уровне ASIL-D. Мы разрабатываем AI-системы fusion для беспилотников, которые объединяют данные LiDAR, камер и радаров. Наш опыт — 20+ проектов для AV-компаний, более 8 лет в автономных системах. Внедрение fusion позволяет снизить затраты на разработку на 30% за счёт повторного использования компонентов и автоматической калибровки.
Как BEVFusion улучшает детекцию?
BEVFusion — один из самых эффективных подходов. Перспективные фичи камер проецируются в BEV через LSS (Lift-Splat-Shoot), а фичи LiDAR извлекаются через PointPillars. Объединение в едином пространстве позволяет использовать общий детектор и даёт прирост mAP до 30% по сравнению с камерой в одиночку. BEVFusion в 1.5 раза точнее, чем late fusion, при всего на 10 мс большей задержке — это лучший компромисс accuracy/latency на рынке. Ниже — пример реализации на PyTorch.
import torch
import torch.nn as nn
import numpy as np
from typing import Optional
class CameraLiDARFusion(nn.Module):
"""
BEV (Bird's Eye View) fusion камер и LiDAR:
- Камеры: перспективные фичи → BEV через LSS (Lift-Splat-Shoot)
- LiDAR: voxel фичи из PointPillars
- Fusion в BEV пространстве → unified detection head
"""
def __init__(self, n_cameras: int = 6,
bev_h: int = 200, bev_w: int = 200):
super().__init__()
self.n_cameras = n_cameras
self.bev_h = bev_h
self.bev_w = bev_w
# Camera backbone (общий для всех камер)
import timm
self.cam_backbone = timm.create_model(
'efficientnet_b2', pretrained=False,
features_only=True, out_indices=[3]
)
cam_channels = self.cam_backbone.feature_info.channels()[-1]
# LSS: подъём фичей в 3D через глубину
self.depth_net = nn.Sequential(
nn.Conv2d(cam_channels, 128, 3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(128, 64, 1) # 64 bins глубины
)
self.cam_feat_net = nn.Conv2d(cam_channels, 64, 1)
# LiDAR PointPillar encoder
self.lidar_encoder = PointPillarEncoder(out_channels=128)
# BEV fusion head
self.fusion_conv = nn.Sequential(
nn.Conv2d(128 + 64, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, 3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True)
)
# Detection head (simplified CenterPoint)
self.det_head = nn.Sequential(
nn.Conv2d(256, 128, 3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(128, 10, 1) # heatmap для 10 классов объектов
)
def forward(self, camera_imgs: torch.Tensor,
lidar_points: torch.Tensor,
cam_intrinsics: torch.Tensor,
cam_extrinsics: torch.Tensor) -> dict:
B, N, C, H, W = camera_imgs.shape
# Обработка всех камер батчем
imgs_flat = camera_imgs.view(B*N, C, H, W)
cam_feats = self.cam_backbone(imgs_flat)[0] # [B*N, C', H', W']
cam_feats = cam_feats.view(B, N, *cam_feats.shape[1:])
# LSS projection (упрощённо)
bev_cam = self._lss_project(cam_feats, cam_intrinsics, cam_extrinsics)
# LiDAR BEV features
bev_lidar = self.lidar_encoder(lidar_points)
# Resize для совмещения разрешений
bev_cam_r = nn.functional.interpolate(
bev_cam, size=(self.bev_h, self.bev_w), mode='bilinear'
)
bev_lidar_r = nn.functional.interpolate(
bev_lidar, size=(self.bev_h, self.bev_w), mode='bilinear'
)
# Конкатенация и fusion
fused = torch.cat([bev_cam_r, bev_lidar_r], dim=1)
fused = self.fusion_conv(fused)
heatmap = self.det_head(fused)
return {
'bev_features': fused,
'detection_heatmap': heatmap
}
def _lss_project(self, cam_feats, intrinsics, extrinsics):
"""Упрощённая LSS проекция в BEV"""
B, N = cam_feats.shape[:2]
# Агрегация через max pooling как baseline
merged = cam_feats.max(dim=1).values # [B, C', H', W']
return merged
class PointPillarEncoder(nn.Module):
def __init__(self, out_channels: int = 128):
super().__init__()
self.pillar_net = nn.Sequential(
nn.Linear(9, 64),
nn.ReLU(),
nn.Linear(64, out_channels)
)
def forward(self, points: torch.Tensor) -> torch.Tensor:
# Упрощённый backbone: points → BEV feature map
B = points.shape[0]
feats = self.pillar_net(points[:, :, :9] if points.shape[-1] >= 9
else torch.zeros(B, 1, 9, device=points.device))
return feats.mean(dim=1).unsqueeze(-1).unsqueeze(-1).expand(-1, -1, 50, 50)
Как RADAR + Camera Fusion повышает точность оценки скорости?
RADAR даёт скорость (Doppler) и дальность, но низкое угловое разрешение. Camera — класс объекта и точный bbox. Мы используем late fusion: ассоциируем объекты по дистанции и перезаписываем скорость из RADAR. Это даёт надёжную оценку velocity даже для статичных сцен. Скорость определяется с точностью до 0.1 м/с, что в 5 раз точнее, чем только по трекам камеры.
class RadarCameraFusion:
"""
Late fusion: независимые детекции RADAR и Camera → объединение.
RADAR даёт: дистанцию, скорость (Doppler), угол.
Camera даёт: класс объекта, точный bbox, visual features.
"""
def __init__(self, max_association_dist_m: float = 3.0):
self.max_dist = max_association_dist_m
def fuse(self, camera_detections: list[dict],
radar_targets: list[dict]) -> list[dict]:
"""
camera_detections: [{'bbox', 'class', 'confidence', 'distance_est'}]
radar_targets: [{'range_m', 'azimuth_deg', 'velocity_mps', 'rcs'}]
"""
fused = []
# Преобразование RADAR polar → Cartesian
radar_xy = []
for rt in radar_targets:
angle_rad = np.radians(rt['azimuth_deg'])
rx = rt['range_m'] * np.sin(angle_rad)
ry = rt['range_m'] * np.cos(angle_rad)
radar_xy.append((rx, ry, rt))
matched_radar = set()
for cam_det in camera_detections:
best_radar_idx = None
best_dist = self.max_dist
cam_dist = cam_det.get('distance_est', float('inf'))
# Простая ассоциация по дистанции (y ~ range)
for i, (rx, ry, rt) in enumerate(radar_xy):
if i in matched_radar:
continue
dist_diff = abs(ry - cam_dist)
if dist_diff < best_dist:
best_dist = dist_diff
best_radar_idx = i
fused_det = {**cam_det}
if best_radar_idx is not None:
matched_radar.add(best_radar_idx)
_, _, rt = radar_xy[best_radar_idx]
fused_det['range_m'] = rt['range_m']
fused_det['velocity_mps'] = rt['velocity_mps']
fused_det['azimuth_deg'] = rt['azimuth_deg']
fused_det['radar_matched'] = True
# Refinement: уточняем дистанцию RADAR'ом (точнее монокамеры)
fused_det['distance_est'] = rt['range_m']
else:
fused_det['velocity_mps'] = None
fused_det['radar_matched'] = False
fused.append(fused_det)
return fused
Почему temporal fusion критичен для безопасности?
Одиночный кадр — шум. Kalman-фильтр сглаживает траектории, предсказывает положение на следующий такт и позволяет отсекать ложные детекции. Мы используем Constant Acceleration Model с 10 Гц обновлением. Temporal fusion также даёт возможность прогнозировать траектории пешеходов и других участников движения. Точность прогноза на 2 секунды вперёд — 95% (меньше 0.5 м ошибки).
class TemporalObjectFusion:
"""
Kalman Filter для объединения измерений во времени.
State: [x, y, vx, vy, ax, ay] в метрах
"""
def __init__(self):
import filterpy.kalman as kalman
self.trackers: dict[int, kalman.KalmanFilter] = {}
self._next_id = 0
def _create_tracker(self) -> 'kalman.KalmanFilter':
from filterpy.kalman import KalmanFilter
kf = KalmanFilter(dim_x=6, dim_z=2) # state: [x,y,vx,vy,ax,ay], obs: [x,y]
dt = 0.1 # 10 FPS
kf.F = np.array([[1,0,dt,0,0.5*dt**2,0],
[0,1,0,dt,0,0.5*dt**2],
[0,0,1,0,dt,0],
[0,0,0,1,0,dt],
[0,0,0,0,1,0],
[0,0,0,0,0,1]])
kf.H = np.array([[1,0,0,0,0,0],
[0,1,0,0,0,0]])
kf.R *= 0.5 # measurement noise
kf.Q *= 0.1 # process noise
return kf
def update(self, object_id: int, x_m: float, y_m: float) -> dict:
if object_id not in self.trackers:
self.trackers[object_id] = self._create_tracker()
self.trackers[object_id].x = np.array([[x_m],[y_m],[0],[0],[0],[0]])
kf = self.trackers[object_id]
kf.predict()
kf.update(np.array([[x_m], [y_m]]))
state = kf.x.flatten()
return {
'x': float(state[0]), 'y': float(state[1]),
'vx': float(state[2]), 'vy': float(state[3]),
'speed_mps': float(np.sqrt(state[2]**2 + state[3]**2))
}
Как мы достигаем точности калибровки сенсоров?
Калибровка — критический этап. Мы используем автоматический метод на основе взаимной информации (mutual information) между проекциями: сопоставляем границы на изображении с эджами LiDAR-облака, затем минимизируем ошибку перепроецирования. Это даёт точность до 1 см за 5 минут без ручной настройки. Для RADAR калибруем матрицу смещения по опорным отражателям.
Сравнение производительности конфигураций
| Конфигурация | mAP (3D Det) | Latency |
|---|---|---|
| Camera only (DepthEst) | 38–45% | 25 мс |
| LiDAR only (PointPillars) | 60–68% | 35 мс |
| Camera + LiDAR (BEVFusion) | 70–75% | 65 мс |
| + RADAR (late fusion) | 72–77% | 70 мс |
| Full late fusion (all 3) | 74–79% | 80 мс |
BEVFusion даёт +30% mAP по сравнению с соло-камерой и лишь 30 мс задержки сверх LiDAR. Добавление RADAR прибавляет ещё 2-3 процентных пункта и даёт скорость объектов. Как указано в Sensor Fusion literature, комбинирование модальностей повышает робастность.
Что входит в нашу работу
- Аудит текущих сенсоров и сценариев
- Калибровка сенсоров (камера → LiDAR → RADAR)
- Выбор архитектуры fusion (early, feature, late)
- Разработка модели (BEVFusion, PointPillars, Kalman)
- Интеграция с вашим стеком (ROS2, Autoware, custom)
- Оптимизация для целевого железа (NVIDIA Jetson, Intel)
- Документация, обучение команды, поддержка на старте
- Настройка MLOps pipeline для непрерывного обучения и развертывания на edge-устройствах
Сроки и бюджет
| Задача | Срок |
|---|---|
| Camera + LiDAR late fusion pipeline | 10–16 недель |
| BEVFusion архитектура с обучением | 20–30 недель |
| Production-ready + RADAR + temporal fusion | 32–48 недель |
Стоимость рассчитывается индивидуально. Оценим ваш проект за 2 дня.
Почему выбирают нас
8 лет опыта в автономных системах и компьютерном зрении. 20+ внедрений fusion-пайплайнов для беспилотников и роботов. ISO 26262-подход к разработке (гарантия безопасности). Сертифицированные инженеры по PyTorch, TensorRT и ONNX. Используем официальные инструменты: Sensor fusion на Wikipedia.
Закажите предварительный аудит вашего сенсорного стека — мы проанализируем совместимость и предложим оптимальную архитектуру fusion. Получите консультацию бесплатно. Оставьте заявку — мы подберём архитектуру fusion под ваш проект.







