Files
worldmodel/plans/camera/zed2i_stereo_imu_solution.md
T

818 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ZED 2i 双目+IMU 完整解决方案
## 📋 方案概述
本方案基于Stereolabs ZED 2i双目相机,集成IMU传感器,提供实时3D感知和位姿估计能力,是性价比最高的一体化视觉惯性方案。
### 核心优势
- 💰 **成本低**:¥3,999(单设备完整方案)
-**实时性**15-30 FPS深度计算
- 🎯 **精度高**:深度精度±1-2%,位姿精度±0.1%
- 🔧 **易集成**SDK完善,支持多平台
- 🤖 **即插即用**:无需标定,开箱可用
### 适用场景
- ✅ 室内移动机器人导航
- ✅ 实时3D建图与定位
- ✅ AR/VR应用
- ✅ 无人机室内飞行
- ✅ 酒店场景快速扫描
---
## 一、硬件规格详解
### 1.1 ZED 2i 技术参数
```yaml
基本信息:
型号: ZED 2i
制造商: Stereolabs
价格: ¥3,999
重量: 159g
尺寸: 175mm × 30mm × 33mm
双目相机:
传感器: 2× 2.1MP CMOS
分辨率:
- 2.2K: 2208×1242 @ 15fps
- 1080p: 1920×1080 @ 30fps
- 720p: 1280×720 @ 60fps
- WVGA: 1344×376 @ 100fps
基线距离: 120mm
视场角: 110° (H) × 70° (V)
快门: 全局快门(避免运动模糊)
深度感知:
测距范围: 0.3m - 20m
深度精度: ±1-2% @ 1-3m
深度模式:
- PERFORMANCE: 高速
- QUALITY: 高质量
- ULTRA: 超高质量
最小深度: 0.3m (近距离模式)
IMU传感器:
类型: 6轴IMU (加速度计 + 陀螺仪)
采样率: 400 Hz
加速度计范围: ±16g
陀螺仪范围: ±2000 dps
温度补偿:
其他特性:
接口: USB 3.0 Type-C
功耗: 3.5W (典型)
工作温度: 0°C - 45°C
防护等级: IP44 (防尘防溅)
```
### 1.2 与其他双目相机对比
| 型号 | 价格 | 基线 | 分辨率 | IMU | 深度精度 | 推荐度 |
|-----|------|------|--------|-----|---------|--------|
| **ZED 2i** | ¥3,999 | 120mm | 2.2K@15fps | ✅ 400Hz | ±1-2% | ⭐⭐⭐⭐⭐ |
| ZED 2 | ¥2,999 | 120mm | 2.2K@15fps | ✅ 100Hz | ±1-2% | ⭐⭐⭐⭐ |
| ZED X | ¥5,999 | 120mm | 1.2MP@60fps | ✅ 400Hz | ±1% | ⭐⭐⭐⭐ |
| RealSense D455 | ¥2,999 | 95mm | 1280×720 | ❌ | ±2% | ⭐⭐⭐ |
| OAK-D Pro | ¥4,999 | 75mm | 4K@30fps | ✅ | ±1% | ⭐⭐⭐⭐ |
**结论**:ZED 2i在价格、性能、IMU质量的平衡上最优。
---
## 二、软件生态与SDK
### 2.1 ZED SDK
```yaml
支持平台:
- Windows 10/11
- Ubuntu 18.04/20.04/22.04
- Jetson (Nano, Xavier, Orin)
- Docker
编程语言:
- C++
- Python
- C#
- Unity
- Unreal Engine
核心模块:
- Camera: 相机控制
- Depth: 深度计算
- Tracking: 位姿估计
- Spatial Mapping: 3D建图
- Object Detection: 物体检测
- Body Tracking: 人体追踪
ROS支持:
- ROS 1 (Melodic, Noetic)
- ROS 2 (Foxy, Humble)
```
### 2.2 安装配置
```bash
# Ubuntu安装ZED SDK
# 1. 下载SDK
wget https://download.stereolabs.com/zedsdk/4.0/cu118/ubuntu22
# 2. 安装
chmod +x ZED_SDK_Ubuntu22_cuda11.8_v4.0.run
./ZED_SDK_Ubuntu22_cuda11.8_v4.0.run
# 3. 安装Python包
pip install pyzed
# 4. 验证安装
ZED_Explorer
# ROS 2安装
sudo apt install ros-humble-zed-ros2
```
---
## 三、核心功能实现
### 3.1 基础采集
```python
# basic_capture.py
import pyzed.sl as sl
import numpy as np
import cv2
class ZED2iCamera:
"""ZED 2i相机封装"""
def __init__(self):
# 创建相机对象
self.zed = sl.Camera()
# 配置参数
self.init_params = sl.InitParameters()
self.init_params.camera_resolution = sl.RESOLUTION.HD1080
self.init_params.camera_fps = 30
self.init_params.depth_mode = sl.DEPTH_MODE.ULTRA
self.init_params.coordinate_units = sl.UNIT.METER
self.init_params.depth_minimum_distance = 0.3
self.init_params.depth_maximum_distance = 20.0
# 打开相机
err = self.zed.open(self.init_params)
if err != sl.ERROR_CODE.SUCCESS:
print(f"Error opening camera: {err}")
exit(1)
# 创建图像容器
self.image_left = sl.Mat()
self.image_right = sl.Mat()
self.depth_map = sl.Mat()
self.point_cloud = sl.Mat()
# 运行时参数
self.runtime_params = sl.RuntimeParameters()
self.runtime_params.sensing_mode = sl.SENSING_MODE.STANDARD
print("ZED 2i initialized successfully")
def grab_frame(self):
"""采集一帧数据"""
if self.zed.grab(self.runtime_params) == sl.ERROR_CODE.SUCCESS:
# 获取左右图像
self.zed.retrieve_image(self.image_left, sl.VIEW.LEFT)
self.zed.retrieve_image(self.image_right, sl.VIEW.RIGHT)
# 获取深度图
self.zed.retrieve_measure(self.depth_map, sl.MEASURE.DEPTH)
# 获取点云
self.zed.retrieve_measure(self.point_cloud, sl.MEASURE.XYZRGBA)
return True
return False
def get_images(self):
"""获取RGB图像"""
left_img = self.image_left.get_data()
right_img = self.image_right.get_data()
return left_img, right_img
def get_depth(self):
"""获取深度图"""
depth = self.depth_map.get_data()
return depth
def get_pointcloud(self):
"""获取点云"""
pc = self.point_cloud.get_data()
return pc
def close(self):
"""关闭相机"""
self.zed.close()
# 使用示例
camera = ZED2iCamera()
while True:
if camera.grab_frame():
# 获取数据
left, right = camera.get_images()
depth = camera.get_depth()
# 显示
cv2.imshow("Left", left)
cv2.imshow("Depth", depth / 20.0) # 归一化到0-1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
camera.close()
cv2.destroyAllWindows()
```
### 3.2 位姿追踪(Tracking
```python
# tracking.py
import pyzed.sl as sl
import numpy as np
class ZED2iTracking:
"""ZED 2i位姿追踪"""
def __init__(self):
self.zed = sl.Camera()
# 初始化参数
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 60
init_params.coordinate_units = sl.UNIT.METER
init_params.coordinate_system = sl.COORDINATE_SYSTEM.RIGHT_HANDED_Z_UP
# 打开相机
err = self.zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
exit(1)
# 启用位姿追踪
tracking_params = sl.PositionalTrackingParameters()
tracking_params.enable_imu_fusion = True # 启用IMU融合
tracking_params.enable_area_memory = True # 启用区域记忆
err = self.zed.enable_positional_tracking(tracking_params)
if err != sl.ERROR_CODE.SUCCESS:
print(f"Error enabling tracking: {err}")
exit(1)
# 位姿对象
self.camera_pose = sl.Pose()
self.imu_data = sl.SensorsData()
print("Tracking initialized")
def get_pose(self):
"""获取相机位姿"""
if self.zed.grab() == sl.ERROR_CODE.SUCCESS:
# 获取位姿
tracking_state = self.zed.get_position(
self.camera_pose,
sl.REFERENCE_FRAME.WORLD
)
if tracking_state == sl.POSITIONAL_TRACKING_STATE.OK:
# 提取位置和旋转
translation = self.camera_pose.get_translation().get()
rotation = self.camera_pose.get_rotation_matrix().r
# 获取IMU数据
self.zed.get_sensors_data(
self.imu_data,
sl.TIME_REFERENCE.IMAGE
)
imu = self.imu_data.get_imu_data()
return {
'position': translation,
'rotation': rotation,
'tracking_state': tracking_state,
'imu': {
'linear_acceleration': imu.get_linear_acceleration(),
'angular_velocity': imu.get_angular_velocity(),
'orientation': imu.get_pose().get_orientation().get()
}
}
return None
def reset_tracking(self):
"""重置追踪"""
self.zed.reset_positional_tracking(sl.Transform())
def save_area_memory(self, filename):
"""保存区域记忆"""
self.zed.save_area_memory(filename)
def load_area_memory(self, filename):
"""加载区域记忆"""
self.zed.load_area_memory(filename)
# 使用示例
tracker = ZED2iTracking()
trajectory = []
while True:
pose_data = tracker.get_pose()
if pose_data:
pos = pose_data['position']
trajectory.append(pos)
print(f"Position: x={pos[0]:.2f}, y={pos[1]:.2f}, z={pos[2]:.2f}")
print(f"IMU Accel: {pose_data['imu']['linear_acceleration']}")
# 每100帧保存一次轨迹
if len(trajectory) % 100 == 0:
np.save('trajectory.npy', np.array(trajectory))
# 保存区域记忆
tracker.save_area_memory("hotel_room.area")
```
### 3.3 空间建图(Spatial Mapping
```python
# spatial_mapping.py
import pyzed.sl as sl
import numpy as np
class ZED2iMapping:
"""ZED 2i 3D建图"""
def __init__(self):
self.zed = sl.Camera()
# 初始化
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 30
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
init_params.coordinate_units = sl.UNIT.METER
self.zed.open(init_params)
# 启用追踪
tracking_params = sl.PositionalTrackingParameters()
tracking_params.enable_imu_fusion = True
self.zed.enable_positional_tracking(tracking_params)
# 启用空间建图
mapping_params = sl.SpatialMappingParameters()
mapping_params.resolution_meter = 0.05 # 5cm分辨率
mapping_params.range_meter = 10.0 # 10m范围
mapping_params.use_chunk_only = False
mapping_params.save_texture = True
mapping_params.map_type = sl.SPATIAL_MAP_TYPE.FUSED_POINT_CLOUD
self.zed.enable_spatial_mapping(mapping_params)
self.mesh = sl.Mesh()
self.fused_pc = sl.FusedPointCloud()
print("Spatial mapping initialized")
def update_map(self):
"""更新地图"""
if self.zed.grab() == sl.ERROR_CODE.SUCCESS:
# 获取建图状态
mapping_state = self.zed.get_spatial_mapping_state()
return mapping_state
return None
def extract_mesh(self, output_path="room.obj"):
"""提取网格"""
# 停止建图
self.zed.pause_spatial_mapping(True)
# 提取网格
self.zed.extract_whole_spatial_map(self.mesh)
# 过滤网格
self.mesh.filter(
sl.MeshFilterParameters.MESH_FILTER.LOW,
update_chunk_only=False
)
# 保存
self.mesh.save(output_path)
print(f"Mesh saved to {output_path}")
print(f"Vertices: {self.mesh.vertices.shape[0]}")
print(f"Triangles: {self.mesh.triangles.shape[0]}")
# 恢复建图
self.zed.pause_spatial_mapping(False)
def extract_pointcloud(self, output_path="room.ply"):
"""提取点云"""
self.zed.pause_spatial_mapping(True)
# 提取融合点云
self.zed.extract_whole_spatial_map(self.fused_pc)
# 保存
self.fused_pc.save(output_path)
print(f"Point cloud saved to {output_path}")
print(f"Points: {self.fused_pc.vertices.shape[0]}")
self.zed.pause_spatial_mapping(False)
def get_map_statistics(self):
"""获取地图统计信息"""
stats = {
'chunks': self.zed.get_spatial_mapping_state().number_of_chunks,
'vertices': self.mesh.vertices.shape[0] if self.mesh.vertices.size > 0 else 0,
'triangles': self.mesh.triangles.shape[0] if self.mesh.triangles.size > 0 else 0
}
return stats
# 使用示例
mapper = ZED2iMapping()
print("开始建图,请移动相机扫描房间...")
print("'s' 保存地图,按 'q' 退出")
frame_count = 0
while True:
state = mapper.update_map()
if state:
frame_count += 1
if frame_count % 30 == 0: # 每秒显示一次
stats = mapper.get_map_statistics()
print(f"Chunks: {stats['chunks']}, "
f"Vertices: {stats['vertices']}, "
f"Triangles: {stats['triangles']}")
# 键盘控制
key = cv2.waitKey(1) & 0xFF
if key == ord('s'):
print("保存地图...")
mapper.extract_mesh("hotel_room.obj")
mapper.extract_pointcloud("hotel_room.ply")
elif key == ord('q'):
break
print("建图完成")
```
### 3.4 物体检测(Object Detection
```python
# object_detection.py
import pyzed.sl as sl
class ZED2iObjectDetection:
"""ZED 2i物体检测"""
def __init__(self):
self.zed = sl.Camera()
# 初始化
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 30
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
self.zed.open(init_params)
# 启用追踪
tracking_params = sl.PositionalTrackingParameters()
self.zed.enable_positional_tracking(tracking_params)
# 启用物体检测
obj_det_params = sl.ObjectDetectionParameters()
obj_det_params.enable_tracking = True
obj_det_params.enable_segmentation = False
obj_det_params.detection_model = sl.OBJECT_DETECTION_MODEL.MULTI_CLASS_BOX_FAST
self.zed.enable_object_detection(obj_det_params)
self.objects = sl.Objects()
self.obj_runtime_params = sl.ObjectDetectionRuntimeParameters()
self.obj_runtime_params.detection_confidence_threshold = 50
print("Object detection initialized")
def detect_objects(self):
"""检测物体"""
if self.zed.grab() == sl.ERROR_CODE.SUCCESS:
# 检测物体
self.zed.retrieve_objects(
self.objects,
self.obj_runtime_params
)
detected = []
for obj in self.objects.object_list:
detected.append({
'id': obj.id,
'label': obj.label,
'confidence': obj.confidence,
'position': obj.position,
'velocity': obj.velocity,
'bounding_box_2d': obj.bounding_box_2d,
'bounding_box_3d': obj.bounding_box
})
return detected
return []
# 使用示例
detector = ZED2iObjectDetection()
while True:
objects = detector.detect_objects()
for obj in objects:
print(f"Object {obj['id']}: {obj['label']} "
f"({obj['confidence']:.0f}%) "
f"at {obj['position']}")
```
---
## 四、酒店场景应用方案
### 4.1 客房快速扫描
```yaml
应用场景: 5分钟完成客房3D建模
硬件配置:
- ZED 2i相机: ¥3,999
- 手持稳定器: ¥899
- 笔记本电脑: ¥8,000
总成本: ¥12,898
扫描流程:
1. 启动ZED 2i + 空间建图
2. 从门口开始环绕房间
3. 保持1-1.5m距离
4. 移动速度: 0.3-0.5 m/s
5. 重点扫描家具和角落
6. 实时查看建图进度
7. 完成后导出Mesh
输出结果:
- 3D网格模型(OBJ
- 融合点云(PLY
- 相机轨迹(TXT
- 区域记忆(AREA
精度指标:
- 几何精度: ±3-5cm
- 覆盖率: > 90%
- 采集时间: 5-10分钟
- 处理时间: 实时
```
### 4.2 机器人导航
```yaml
应用场景: 服务机器人室内导航
系统架构:
ZED 2i → ROS 2 → Navigation Stack → 机器人控制
ROS 2节点:
- zed_wrapper: 相机驱动
- zed_tracking: 位姿发布
- zed_mapping: 地图构建
- nav2: 导航规划
功能实现:
- 实时SLAM建图
- 自主定位
- 路径规划
- 障碍物避让
- 动态重规划
性能指标:
- 定位精度: ±5cm
- 更新频率: 30 Hz
- 地图分辨率: 5cm
- 最大速度: 1 m/s
```
### 4.3 AR导览系统
```yaml
应用场景: 酒店AR导览与信息叠加
技术栈:
- ZED 2i: 位姿追踪
- Unity: AR渲染
- ZED Unity Plugin: 集成
功能特性:
- 实时位姿追踪
- 虚拟信息叠加
- 空间锚点
- 遮挡处理
应用示例:
- 房间导航箭头
- 设施信息标注
- 虚拟导游
- 互动游戏
```
---
## 五、性能优化
### 5.1 参数调优
```python
# 不同场景的最佳参数
# 场景1:高速移动(机器人)
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 60
init_params.depth_mode = sl.DEPTH_MODE.PERFORMANCE
# 场景2:高质量建图(静态扫描)
init_params.camera_resolution = sl.RESOLUTION.HD1080
init_params.camera_fps = 15
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
# 场景3:实时AR(低延迟)
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 60
init_params.depth_mode = sl.DEPTH_MODE.PERFORMANCE
runtime_params.enable_depth = False # 仅追踪
# 场景4:远距离检测
init_params.depth_minimum_distance = 1.0
init_params.depth_maximum_distance = 20.0
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
```
### 5.2 性能基准
| 配置 | 分辨率 | FPS | 深度模式 | CPU占用 | GPU占用 | 延迟 |
|-----|--------|-----|---------|---------|---------|------|
| 高速 | 720p | 60 | PERFORMANCE | 15% | 20% | 16ms |
| 标准 | 1080p | 30 | QUALITY | 25% | 35% | 33ms |
| 高质量 | 1080p | 15 | ULTRA | 35% | 50% | 66ms |
| 超高质量 | 2.2K | 15 | ULTRA | 45% | 65% | 66ms |
**测试平台**Intel i7-11800H + RTX 3060 Laptop
---
## 六、故障排查
### 6.1 常见问题
```yaml
问题1: 相机无法打开
原因: USB带宽不足
解决: 使用USB 3.0接口,避免USB Hub
问题2: 追踪丢失
原因: 纹理不足或运动过快
解决: 降低移动速度,增加环境纹理
问题3: 深度图有空洞
原因: 反光表面或透明物体
解决: 调整光照,使用ULTRA模式
问题4: IMU数据不稳定
原因: 温度漂移或磁干扰
解决: 预热5分钟,远离磁场
问题5: 建图不完整
原因: 移动过快或覆盖不足
解决: 降低速度,增加重叠率
```
### 6.2 性能优化建议
```python
# 优化技巧
# 1. 降低分辨率提升帧率
init_params.camera_resolution = sl.RESOLUTION.HD720 # 而非HD1080
# 2. 使用GPU加速
init_params.sdk_gpu_id = 0 # 指定GPU
# 3. 禁用不需要的功能
runtime_params.enable_depth = False # 仅需RGB时
# 4. 批量处理
# 每N帧处理一次深度,而非每帧
# 5. 异步处理
# 使用多线程分离采集和处理
```
---
## 七、完整项目示例
### 7.1 酒店客房扫描系统
```python
# hotel_room_scanner.py
import pyzed.sl as sl
import cv2
import numpy as np
import time
class HotelRoomScanner:
"""酒店客房扫描系统"""
def __init__(self, room_id):
self.room_id = room_id
self.zed = sl.Camera()
# 初始化相机
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD1080
init_params.camera_fps = 30
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
init_params.coordinate_units = sl.UNIT.METER
err = self.zed.open(init_params)
if err != sl.ERROR_CODE.SUCCESS:
print(f"Error: {err}")
exit(1)
# 启用追踪
tracking_params = sl.PositionalTrackingParameters()
tracking_params.enable_imu_fusion = True
tracking_params.enable_area_memory = True
self.zed.enable_positional_tracking(tracking_params)
# 启用建图
mapping_params = sl.SpatialMappingParameters()
mapping_params.resolution_meter = 0.05
mapping_params.range_meter = 10.0
mapping_params.save_texture = True
self.zed.enable_spatial_mapping(mapping_params)
# 数据容器
self.image = sl.Mat()
self.depth = sl.Mat()
self.mesh = sl.Mesh()
self.pose = sl.Pose()
# 统计
self.start_time = time.time()
self.frame_count = 0
print(f"Room {room_id} scanner initialized")
def scan(self, duration_seconds=300):
"""扫描房间"""
print(f"开始扫描房间 {self.room_id}")
print(f"扫描时长: {duration_seconds}秒")
print("请缓慢移动相机环绕房间...")
while (time.time() - self.start_time) < duration_seconds:
if self.zed.grab() == sl.ERROR_CODE.SUCCESS:
self.frame_count += 1
# 获取图像和深度
self.zed.retrieve_image(self.image, sl.VIEW.LEFT)
self.zed.retrieve_measure(self.depth, sl.MEASURE.DEPTH)
# 获取位姿
state = self.zed.get_position(self.pose, sl.REFERENCE_FRAME.WORLD)
# 显示进度
if self.frame_count % 30 == 0:
elapsed = time.time() - self.start_time
remaining = duration_seconds - elapsed
stats = self.zed.get_spatial_mapping_state()
print(f"[{elapsed:.0f}s/{duration_seconds}s] "