传感器层交互:摄像头、IMU、深度传感器与GPS的集成与应用
传感器层交互:摄像头、IMU、深度传感器与GPS的集成与应用
传感器是现代智能设备感知世界的"眼睛"与"触觉"。本文深入探讨摄像头、IMU(惯性测量单元)、深度传感器和GPS等核心传感器之间的交互机制,以及如何通过它们的协同工作实现更精确的环境感知和定位。
一、传感器基础与原理
1. 摄像头(Camera)
工作原理:
摄像头通过光学系统将光线聚焦到感光元件(通常是CMOS或CCD)上,感光元件将光信号转换为电信号,经过模数转换器(ADC)转换为数字信号,最终形成图像。
主要参数:
- 分辨率:一般以像素表示(例如1920×1080)
- 帧率(FPS):每秒捕获的图像帧数
- 视场角(FOV):表示可见范围的角度大小
- 光圈大小(f值):控制进光量
- 传感器尺寸:影响感光能力和画质
典型输出数据:
{
"timestamp": 1647821023.456,
"frame": [图像数据,通常为RGB或YUV格式],
"resolution": {"width": 1920, "height": 1080},
"format": "RGB24"
}
2. IMU(惯性测量单元)
工作原理:
IMU通常由加速度计、陀螺仪和(有时)磁力计组成,用于测量设备的运动状态。
- 加速度计:测量线性加速度(包括重力)
- 陀螺仪:测量角速度
- 磁力计:测量磁场强度,用于确定朝向
主要参数:
- 采样率:每秒读取的数据点数
- 测量范围:例如±2g、±4g、±8g(加速度计)
- 灵敏度:输出变化与输入变化的比率
- 零偏漂移:静止状态下传感器输出的变化
典型输出数据:
{
"timestamp": 1647821023.456,
"accelerometer": {"x": 0.12, "y": 9.81, "z": 0.34}, // 单位:m/s²
"gyroscope": {"x": 0.01, "y": 0.02, "z": 0.03}, // 单位:rad/s
"magnetometer": {"x": 23.4, "y": -12.1, "z": 42.5} // 单位:μT
}
3. 深度传感器
常见类型:
- 结构光:投射已知图案并分析变形(如iPhone的Face ID)
- ToF(飞行时间):测量光从传感器到物体再返回的时间
- 立体视觉:利用双目或多目相机的视差计算深度
主要参数:
- 深度范围:可测量的最小和最大距离
- 分辨率:深度图像的像素数量
- 帧率:每秒捕获的深度图数量
- 精度:距离测量的准确度
典型输出数据:
{
"timestamp": 1647821023.456,
"depth_map": [深度值矩阵],
"resolution": {"width": 640, "height": 480},
"min_depth": 0.5, // 单位:米
"max_depth": 5.0 // 单位:米
}
4. GPS(全球定位系统)
工作原理:
GPS接收器接收多颗卫星发送的信号,通过测量信号传播时间计算接收器与各卫星的距离,然后通过三角测量法确定接收器的位置。
主要参数:
- 精度:位置测量的准确度(水平和垂直)
- 更新率:位置信息更新的频率
- 捕获时间:首次获取定位所需的时间(冷启动、温启动、热启动)
- 信道数:可同时跟踪的卫星数量
典型输出数据:
{
"timestamp": 1647821023.456,
"latitude": 37.7749, // 单位:度
"longitude": -122.4194, // 单位:度
"altitude": 10.5, // 单位:米
"accuracy": 5.2, // 单位:米
"speed": 1.2, // 单位:m/s
"satellites": 8 // 可见卫星数
}
二、传感器融合基础
1. 传感器融合概念
传感器融合是将多个传感器的数据结合起来,以获得比单个传感器更准确、完整的信息。这种方法能有效补偿各类传感器的局限性,提高系统的鲁棒性和精度。
主要优势:
- 提高测量精度和可靠性
- 扩展感知范围和能力
- 降低单一传感器失效的风险
- 减少测量噪声和不确定性
2. 传感器融合算法
卡尔曼滤波(Kalman Filter)
卡尔曼滤波是一种递归估计算法,特别适合处理有噪声的线性系统。
基本原理:
- 预测步骤:利用系统模型预测当前状态
- 更新步骤:结合测量数据更新状态估计
# 简化版卡尔曼滤波实现
def kalman_filter(z, x, P, F, H, R, Q):
# 预测步骤
x = F @ x # 状态预测
P = F @ P @ F.T + Q # 协方差预测
# 更新步骤
y = z - H @ x # 测量残差
S = H @ P @ H.T + R # 残差协方差
K = P @ H.T @ np.linalg.inv(S) # 卡尔曼增益
x = x + K @ y # 状态更新
P = (np.eye(len(x)) - K @ H) @ P # 协方差更新
return x, P
扩展卡尔曼滤波(Extended Kalman Filter, EKF)
EKF是卡尔曼滤波的非线性扩展版本,适用于非线性系统,通过在工作点处的线性化处理来近似非线性系统。
粒子滤波(Particle Filter)
粒子滤波是一种基于Monte Carlo方法的非参数贝叶斯滤波技术,特别适合处理非线性、非高斯系统。
# 简化版粒子滤波实现
def particle_filter(particles, weights, z, motion_model, measurement_model):
# 预测
particles = [motion_model(p) for p in particles]
# 更新权重
weights = [measurement_model(p, z) * w for p, w in zip(particles, weights)]
weights = weights / np.sum(weights) # 归一化
# 重采样
indices = np.random.choice(len(particles), size=len(particles), p=weights)
particles = [particles[i] for i in indices]
weights = np.ones(len(particles)) / len(particles)
return particles, weights
互补滤波(Complementary Filter)
互补滤波是一种简单但有效的方法,特别适用于IMU数据处理,它结合了加速度计的长期稳定性和陀螺仪的短期准确性。
# 互补滤波简单实现
def complementary_filter(angle, gyro, accel, dt, alpha=0.98):
# 角度 = 高通滤波(陀螺仪) + 低通滤波(加速度计)
angle = alpha * (angle + gyro * dt) + (1 - alpha) * accel
return angle
三、摄像头与IMU融合
1. 视觉惯性里程计(Visual-Inertial Odometry, VIO)
VIO结合摄像头和IMU数据实现精确的运动跟踪,特别适用于AR/VR、无人机和机器人领域。
主要类型:
- 松耦合(Loosely-coupled):分别处理视觉和惯性数据,再进行融合
- 紧耦合(Tightly-coupled):在一个统一框架内同时处理视觉和惯性测量
典型算法:
- MSCKF(Multi-State Constraint Kalman Filter)
- VINS(Visual-Inertial Navigation System)
- OKVIS(Open Keyframe-based Visual-Inertial SLAM)
2. 实现示例:基于EKF的VIO系统
import numpy as np
import cv2
class VIO_System:
def __init__(self):
# 状态向量: [位置, 速度, 姿态, 加速度计偏差, 陀螺仪偏差]
self.state = np.zeros(16)
self.P = np.eye(16) # 协方差矩阵
# 设置相机参数
self.camera_matrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])
self.dist_coeffs = np.zeros(4) # 假设无畸变
# 特征跟踪器
self.feature_tracker = cv2.FastFeatureDetector_create(threshold=25)
self.lk_params = dict(winSize=(21, 21),
maxLevel=3,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 30, 0.01))
self.prev_frame = None
self.prev_features = None
def process_imu_data(self, acc, gyro, dt):
"""处理IMU数据,通过EKF预测步骤更新状态"""
# 提取当前状态
pos = self.state[0:3]
vel = self.state[3:6]
quat = self.state[6:10] # 四元数表示姿态
acc_bias = self.state[10:13]
gyro_bias = self.state[13:16]
# 校正IMU读数
acc_corrected = acc - acc_bias
gyro_corrected = gyro - gyro_bias
# 更新姿态(基于四元数积分)
quat_new = self._integrate_rotation(quat, gyro_corrected, dt)
# 考虑重力并更新速度
gravity = np.array([0, 0, 9.81]) # 地球重力
rot_matrix = self._quaternion_to_rotation_matrix(quat)
acc_global = rot_matrix @ acc_corrected - gravity
vel_new = vel + acc_global * dt
# 更新位置
pos_new = pos + vel * dt + 0.5 * acc_global * dt * dt
# 更新状态
self.state[0:3] = pos_new
self.state[3:6] = vel_new
self.state[6:10] = quat_new
# 更新状态协方差(完整EKF实现需要雅可比矩阵)
# 简化版:仅添加过程噪声
process_noise = np.eye(16) * 0.01
self.P = self.P + process_noise * dt
def process_camera_frame(self, frame):
"""处理摄像头帧,更新视觉测量"""
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if self.prev_frame is None:
# 首次处理,仅检测特征点
self.prev_features = self.feature_tracker.detect(gray)
self.prev_features = np.array([x.pt for x in self.prev_features], dtype=np.float32)
else:
# 计算光流以跟踪特征点
new_features, status, _ = cv2.calcOpticalFlowPyrLK(
self.prev_frame, gray, self.prev_features, None, **self.lk_params)
# 过滤有效跟踪点
good_old = self.prev_features[status == 1]
good_new = new_features[status == 1]
if len(good_old) > 8: # 至少需要8个点计算本质矩阵
# 计算本质矩阵
E, mask = cv2.findEssentialMat(good_new, good_old, self.camera_matrix,
method=cv2.RANSAC, prob=0.999, threshold=1.0)
# 从本质矩阵恢复相机运动
_, R, t, _ = cv2.recoverPose(E, good_new, good_old, self.camera_matrix, mask=mask)
# 通过EKF更新阶段整合视觉测量
self._update_with_visual_measurement(R, t)
# 更新特征点
self.prev_features = good_new.reshape(-1, 1, 2)
self.prev_frame = gray
def _integrate_rotation(self, quat, gyro, dt):
"""通过角速度积分更新四元数"""
# 简化实现,实际应考虑四元数的正确积分
return quat + dt * 0.5 * self._quaternion_multiply(quat, np.array([0, gyro[0], gyro[1], gyro[2]]))
def _quaternion_to_rotation_matrix(self, q):
"""四元数转旋转矩阵"""
# 简化实现
return np.eye(3) # 实际需要计算正确的旋转矩阵
def _quaternion_multiply(self, q1, q2):
"""四元数乘法"""
# 简化实现
return q1 # 实际需要正确计算四元数乘积
def _update_with_visual_measurement(self, R, t):
"""使用视觉估计的旋转和平移更新EKF"""
# 构建测量模型和雅可比矩阵(简化)
H = np.zeros((6, 16))
H[0:3, 0:3] = np.eye(3) # 位置对应
H[3:6, 6:9] = np.eye(3) # 姿态对应(简化)
# 转换视觉估计为合适的格式
visual_measurement = np.zeros(6)
visual_measurement[0:3] = t.flatten()
# 从旋转矩阵提取欧拉角(简化)
visual_measurement[3:6] = np.array([0, 0, 0]) # 实际应提取正确的欧拉角
# 测量噪声
R_noise = np.eye(6) * 0.1
# 卡尔曼增益
K = self.P @ H.T @ np.linalg.inv(H @ self.P @ H.T + R_noise)
# 更新状态
innovation = visual_measurement - H @ self.state
self.state = self.state + K @ innovation
# 更新协方差
self.P = (np.eye(16) - K @ H) @ self.P
3. 应用案例:AR中的姿态跟踪
在增强现实(AR)应用中,准确的姿态跟踪是实现虚拟内容稳定叠加的关键。摄像头与IMU融合能在快速运动或光照变化时保持跟踪稳定性。
// Unity C#中的简化AR姿态跟踪示例
using UnityEngine;
using UnityEngine.XR.ARFoundation;
public class ARPoseTracker : MonoBehaviour
{
[SerializeField]
private ARSession arSession;
[SerializeField]
private GameObject virtualObject;
private ARCameraManager cameraManager;
private bool isTrackingStable = false;
// 滤波参数
private Vector3 filteredPosition;
private Quaternion filteredRotation;
private float positionSmoothFactor = 0.1f;
private float rotationSmoothFactor = 0.1f;
private void Start()
{
cameraManager = arSession.GetComponentInChildren<ARCameraManager>();
cameraManager.frameReceived += OnCameraFrameReceived;
// 初始化滤波值
filteredPosition = transform.position;
filteredRotation = transform.rotation;
}
private void OnCameraFrameReceived(ARCameraFrameEventArgs args)
{
// 检查跟踪状态
if (args.displayMatrix.HasValue)
{
isTrackingStable = true;
}
else
{
isTrackingStable = false;
}
}
private void Update()
{
if (isTrackingStable)
{
// 获取当前相机姿态(基于相机和IMU融合)
Vector3 currentPosition = Camera.main.transform.position;
Quaternion currentRotation = Camera.main.transform.rotation;
// 应用互补滤波减少抖动
filteredPosition = Vector3.Lerp(filteredPosition, currentPosition, positionSmoothFactor);
filteredRotation = Quaternion.Slerp(filteredRotation, currentRotation, rotationSmoothFactor);
// 更新虚拟对象位置
if (virtualObject != null)
{
// 根据相机姿态放置虚拟对象
virtualObject.transform.position = filteredPosition + filteredRotation * Vector3.forward * 2.0f;
virtualObject.transform.rotation = filteredRotation;
}
}
else
{
Debug.Log("AR跟踪不稳定");
}
}
private void OnDestroy()
{
if (cameraManager != null)
cameraManager.frameReceived -= OnCameraFrameReceived;
}
}
四、深度传感器与摄像头融合
1. RGB-D融合基础
RGB-D融合将普通RGB图像与深度信息结合,可创建详细的3D场景重建,并增强物体识别和分割能力。
主要应用:
- 3D场景重建与建模
- 物体检测与分割
- 手势识别
- 增强现实中的遮挡处理
2. 点云生成与处理
import numpy as np
import cv2
import open3d as o3d
def create_point_cloud_from_rgbd(rgb_image, depth_image, camera_intrinsics):
"""从RGB和深度图像创建点云"""
# 转换图像格式
rgb = np.asarray(rgb_image)
depth = np.asarray(depth_image).astype(np.float32) / 1000.0 # 假设深度单位为毫米,转换为米
# 创建Open3D图像对象
color_o3d = o3d.geometry.Image(rgb)
depth_o3d = o3d.geometry.Image(depth)
# 创建RGBD图像
rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth(
color_o3d, depth_o3d,
depth_scale=1.0, # 因为已经转换为米
depth_trunc=3.0, # 最大深度,超过该值的点将被忽略
convert_rgb_to_intensity=False
)
# 设置相机内参
intrinsic = o3d.camera.PinholeCameraIntrinsic()
intrinsic.set_intrinsics(
width=rgb.shape[1],
height=rgb.shape[0],
fx=camera_intrinsics[0, 0],
fy=camera_intrinsics[1, 1],
cx=camera_intrinsics[0, 2],
cy=camera_intrinsics[1, 2]
)
# 创建点云
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intrinsic)
# 转换坐标系(相机坐标系->世界坐标系)
pcd.transform([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
return pcd
def process_point_cloud(pcd, voxel_size=0.02):
"""处理点云:降采样、去除离群点、估计法线"""
# 体素降采样
pcd_down = pcd.voxel_down_sample(voxel_size)
# 估计法线
pcd_down.estimate_normals(
search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30)
)
# 移除离群点
cl, ind = pcd_down.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
pcd_filtered = pcd_down.select_by_index(ind)
return pcd_filtered
def reconstruct_mesh_from_point_cloud(pcd):
"""从点云重建3D网格"""
# 计算点云密度以确定适当的半径
distances = pcd.compute_nearest_neighbor_distance()
avg_dist = np.mean(distances)
radius = 3 * avg_dist
# 泊松表面重建
mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
pcd, depth=9, width=0, scale=1.1, linear_fit=False
)
# 移除低密度顶点
vertices_to_remove = densities < np.quantile(densities, 0.1)
mesh.remove_vertices_by_mask(vertices_to_remove)
return mesh
3. 深度增强的人脸识别
深度信息可以显著提高人脸识别的安全性,防止照片欺骗。
import cv2
import numpy as np
import dlib
class DepthEnhancedFaceRecognition:
def __init__(self):
# 加载人脸检测器和特征提取器
self.detector = dlib.get_frontal_face_detector()
self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
self.face_rec = dlib.face_recognition_model_v1("dlib_face_recognition_resnet_model_v1.dat")
# 人脸编码数据库
self.known_face_encodings = []
self.known_face_names = []
def register_face(self, rgb_image, depth_image, name):
"""注册新面孔到数据库"""
# 检测人脸
faces = self.detector(rgb_image)
if len(faces) == 0:
return False, "没有检测到人脸"
face = faces[0] # 取第一个人脸
# 检查是否为真实人脸(通过深度一致性)
if not self._check_face_depth_consistency(depth_image, face):
return False, "可能是平面照片,深度不一致"
# 提取人脸特征
landmarks = self.predictor(rgb_image, face)
face_encoding = self.face_rec.compute_face_descriptor(rgb_image, landmarks)
# 添加到数据库
self.known_face_encodings.append(np.array(face_encoding))
self.known_face_names.append(name)
return True, "成功注册人脸"
def identify_face(self, rgb_image, depth_image, tolerance=0.6):
"""识别人脸并进行活体检测"""
# 检测人脸
faces = self.detector(rgb_image)
if len(faces) == 0:
return None, "没有检测到人脸"
face = faces[0] # 取第一个人脸
# 活体检测
if not self._check_face_depth_consistency(depth_image, face):
return None, "活体检测失败,可能是照片"
# 人脸特征提取
landmarks = self.predictor(rgb_image, face)
face_encoding = np.array(self.face_rec.compute_face_descriptor(rgb_image, landmarks))
if len(self.known_face_encodings) == 0:
return None, "数据库中没有注册的人脸"
# 人脸匹配
face_distances = np.linalg.norm(self.known_face_encodings - face_encoding, axis=1)
best_match_index = np.argmin(face_distances)
if face_distances[best_match_index] <= tolerance:
return self.known_face_names[best_match_index], "识别成功"
else:
return None, "未找到匹配的人脸"
def _check_face_depth_consistency(self, depth_image, face):
"""检查人脸深度是否一致,用于活体检测"""
# 提取人脸区域的深度值
face_depth = depth_image[face.top():face.bottom(), face.left():face.right()]
# 计算深度方差,平面照片的方差应该很小
depth_variance = np.var(face_depth[face_depth > 0]) # 忽略深度为0的点
# 计算深度梯度
if face_depth.size > 0:
dx = cv2.Sobel(face_depth, cv2.CV_64F, 1, 0, ksize=3)
dy = cv2.Sobel(face_depth, cv2.CV_64F, 0, 1, ksize=3)
gradient_magnitude = np.sqrt(dx**2 + dy**2)
avg_gradient = np.mean(gradient_magnitude)
# 真实人脸应有一定的深度变化
return depth_variance > 50.0 and avg_gradient > 5.0
return False
五、IMU与GPS融合
1. 定位精度提升
IMU与GPS融合可以在GPS信号较弱或短时间丢失时保持定位,并提高整体定位精度和稳定性。
主要优势:
- 提高高动态情况下的定位精度
- 弥补GPS信号受阻时的定位能力
- 增强导航系统的可靠性和连续性
2. 卡尔曼滤波实现GPS-IMU融合
import numpy as np
from scipy.linalg import block_diag
class GPS_IMU_Fusion:
def __init__(self):
# 状态向量: [x, y, z, vx, vy, vz]
self.state = np.zeros(6)
# 状态协方差矩阵
self.P = np.eye(6) * 100 # 初始不确定性较大
# 过程噪声协方差
self.Q = np.diag([0.01, 0.01, 0.01, 0.1, 0.1, 0.1])
# GPS测量噪声协方差
self.R_gps = np.diag([10.0, 10.0, 15.0]) # 位置精度(米)
# 状态转移矩阵
self.F = np.eye(6)
# 上次更新时间
self.last_time = None
# 是否有效初始化
self.initialized = False
def initialize(self, gps_data):
"""使用GPS数据初始化滤波器"""
lat, lon, alt = gps_data['latitude'], gps_data['longitude'], gps_data['altitude']
# 转换为ECEF或ENU坐标系(简化版:假设已转换)
x, y, z = self._geodetic_to_local(lat, lon, alt)
# 初始化状态
self.state[0:3] = [x, y, z]
self.last_time = gps_data['timestamp']
self.initialized = True
def predict(self, imu_data):
"""使用IMU数据预测状态"""
if not self.initialized:
return
# 计算时间增量
current_time = imu_data['timestamp']
if self.last_time is None:
self.last_time = current_time
return
dt = current_time - self.last_time
if dt <= 0:
return
# 更新状态转移矩阵
self.F[0, 3] = dt
self.F[1, 4] = dt
self.F[2, 5] = dt
# 获取加速度并校正重力
acc = np.array([imu_data['accelerometer']['x'],
imu_data['accelerometer']['y'],
imu_data['accelerometer']['z']])
# 简化版:假设已知方向,可以移除重力影响(实际需要姿态估计)
# 加速度直接积分得到速度增量(简化)
acc_without_gravity = acc - np.array([0, 0, 9.81]) # 简单移除重力
velocity_increment = acc_without_gravity * dt
# 预测状态
self.state = self.F @ self.state
# 添加加速度影响(简化)
self.state[3:6] += velocity_increment
# 预测协方差
self.P = self.F @ self.P @ self.F.T + self.Q * dt
# 更新时间
self.last_time = current_time
def update_with_gps(self, gps_data):
"""使用GPS测量更新状态"""
if not self.initialized:
self.initialize(gps_data)
return
# 转换GPS数据到局部坐标
lat, lon, alt = gps_data['latitude'], gps_data['longitude'], gps_data['altitude']
x, y, z = self._geodetic_to_local(lat, lon, alt)
# 测量值
z = np.array([x, y, z])
# 测量矩阵 H: 只观测位置
H = np.zeros((3, 6))
H[0, 0] = H[1, 1] = H[2, 2] = 1.0
# 计算卡尔曼增益
S = H @ self.P @ H.T + self.R_gps
K = self.P @ H.T @ np.linalg.inv(S)
# 更新状态
innovation = z - H @ self.state
self.state = self.state + K @ innovation
# 更新协方差
self.P = (np.eye(6) - K @ H) @ self.P
def _geodetic_to_local(self, lat, lon, alt):
"""将GPS经纬度转换为局部坐标系(ENU)
实际应用中应使用专业的坐标转换库
这里仅作示意,返回简化值
"""
# 此处需要实际的坐标转换
# 简化示例,假设已转换
return lat * 111320.0, lon * 111320.0 * np.cos(np.radians(lat)), alt
3. 应用案例:自动驾驶中的定位系统
在自动驾驶中,精确定位至关重要。结合IMU和GPS可以在城市峡谷或隧道等GPS信号不佳的环境中保持准确定位。
// C++自动驾驶定位系统示例
#include <Eigen/Dense>
#include <vector>
#include <chrono>
#include <iostream>
class AutoDriveLocalization {
private:
// 状态向量及协方差
Eigen::VectorXd state; // [x, y, heading, velocity, angular_velocity]
Eigen::MatrixXd P; // 状态协方差
// 系统矩阵
Eigen::MatrixXd F; // 状态转移矩阵
Eigen::MatrixXd Q; // 过程噪声
// 传感器数据历史
struct SensorData {
double timestamp;
Eigen::Vector3d acceleration; // IMU加速度
Eigen::Vector3d gyro; // IMU角速度
bool has_gps;
Eigen::Vector3d gps_position; // GPS位置
double gps_accuracy; // GPS精度
};
std::vector<SensorData> sensor_history;
// 上次更新时间
double last_timestamp;
// 地图匹配功能
bool use_map_matching;
// 添加地图相关参数...
public:
AutoDriveLocalization(bool enable_map_matching = true) : use_map_matching(enable_map_matching) {
// 初始化状态向量 [x, y, heading, velocity, angular_velocity]
state = Eigen::VectorXd::Zero(5);
P = Eigen::MatrixXd::Identity(5, 5) * 100.0; // 较大的初始不确定性
// 初始化系统矩阵
F = Eigen::MatrixXd::Identity(5, 5);
Q = Eigen::MatrixXd::Identity(5, 5) * 0.01;
last_timestamp = 0.0;
}
void initialize_with_gps(const Eigen::Vector3d& gps_position, double heading = 0.0) {
state(0) = gps_position(0); // x
state(1) = gps_position(1); // y
state(2) = heading; // 朝向角
std::cout << "系统初始化,位置: (" << state(0) << ", " << state(1) << "), 朝向: " << state(2) << std::endl;
}
void process_imu_data(double timestamp, const Eigen::Vector3d& acceleration, const Eigen::Vector3d& gyro) {
// 计算时间增量
double dt = 0.0;
if (last_timestamp > 0.0) {
dt = timestamp - last_timestamp;
}
last_timestamp = timestamp;
if (dt <= 0.0) return;
// 记录传感器数据
SensorData data;
data.timestamp = timestamp;
data.acceleration = acceleration;
data.gyro = gyro;
data.has_gps = false;
sensor_history.push_back(data);
if (sensor_history.size() > 1000) {
sensor_history.erase(sensor_history.begin());
}
// 更新状态转移矩阵
F(0, 3) = dt * std::cos(state(2)); // x位置随速度的变化
F(1, 3) = dt * std::sin(state(2)); // y位置随速度的变化
F(2, 4) = dt; // 朝向随角速度的变化
// 预测步骤
// 由IMU加速度估计线性加速度(简化)
double accel_magnitude = acceleration.norm() - 9.81; // 简化:移除重力
// 使用当前角速度直接代替状态中的角速度
state(4) = gyro(2); // 假设z轴为垂直轴
// 更新速度
state(3) += accel_magnitude * dt;
// 预测位置和朝向
state(0) += state(3) * dt * std::cos(state(2));
state(1) += state(3) * dt * std::sin(state(2));
state(2) += state(4) * dt;
// 规范化朝向角到[-π, π]
state(2) = std::atan2(std::sin(state(2)), std::cos(state(2)));
// 更新协方差
P = F * P * F.transpose() + Q * dt;
std::cout << "IMU更新 - 位置: (" << state(0) << ", " << state(1)
<< "), 朝向: " << state(2) << ", 速度: " << state(3) << std::endl;
}
void process_gps_data(double timestamp, const Eigen::Vector3d& gps_position, double accuracy) {
// 记录GPS数据
if (!sensor_history.empty() && sensor_history.back().timestamp == timestamp) {
sensor_history.back().has_gps = true;
sensor_history.back().gps_position = gps_position;
sensor_history.back().gps_accuracy = accuracy;
} else {
SensorData data;
data.timestamp = timestamp;
data.has_gps = true;
data.gps_position = gps_position;
data.gps_accuracy = accuracy;
data.acceleration = Eigen::Vector3d::Zero();
data.gyro = Eigen::Vector3d::Zero();
sensor_history.push_back(data);
}
// 设置测量矩阵(仅测量位置)
Eigen::MatrixXd H = Eigen::MatrixXd::Zero(2, 5);
H(0, 0) = 1.0; // 测量x位置
H(1, 1) = 1.0; // 测量y位置
// 设置测量噪声
Eigen::MatrixXd R = Eigen::MatrixXd::Identity(2, 2) * (accuracy * accuracy);
// 实际测量值
Eigen::VectorXd z(2);
z << gps_position(0), gps_position(1);
// 预测的测量值
Eigen::VectorXd z_pred = H * state;
// 创新(残差)
Eigen::VectorXd y = z - z_pred;
// 创新协方差
Eigen::MatrixXd S = H * P * H.transpose() + R;
// 卡尔曼增益
Eigen::MatrixXd K = P * H.transpose() * S.inverse();
// 更新状态
state = state + K * y;
// 更新状态协方差
P = (Eigen::MatrixXd::Identity(5, 5) - K * H) * P;
std::cout << "GPS更新 - 位置: (" << state(0) << ", " << state(1)
<< "), 精度: " << accuracy << std::endl;
// 如果启用地图匹配,使用道路网络进一步优化位置
if (use_map_matching) {
map_matching();
}
}
void map_matching() {
// 在实际应用中,这里会实现地图匹配算法
// 将估计位置吸附到最可能的道路上
std::cout << "执行地图匹配..." << std::endl;
// 简化示例:仅打印日志
std::cout << "地图匹配后位置: (" << state(0) << ", " << state(1) << ")" << std::endl;
}
Eigen::Vector3d get_current_position() const {
return Eigen::Vector3d(state(0), state(1), 0.0);
}
double get_current_heading() const {
return state(2);
}
double get_current_velocity() const {
return state(3);
}
Eigen::MatrixXd get_position_covariance() const {
// 返回位置的协方差矩阵(2x2)
Eigen::MatrixXd pos_cov(2, 2);
pos_cov << P(0, 0), P(0, 1),
P(1, 0), P(1, 1);
return pos_cov;
}
};
六、多传感器综合融合
1. 视觉-惯性-深度-GPS集成系统
整合所有传感器可以构建全面的环境感知和定位系统,每种传感器均可弥补其他传感器的不足。
融合策略:
- 多级融合:先进行底层传感器融合(如视觉-惯性),再进行高层融合
- 松耦合vs紧耦合:根据应用需求选择融合紧密程度
- 异步融合:处理不同传感器的不同采样率
2. 机器人SLAM系统示例
import numpy as np
import cv2
import g2o
import time
class MultiSensorSLAM:
def __init__(self):
# 初始化SLAM系统
self.poses = [] # 机器人位姿历史
self.landmarks = {} # 特征点/路标点
# 初始化优化器
self.optimizer = g2o.SparseOptimizer()
solver = g2o.BlockSolverSE3(g2o.LinearSolverEigenSE3())
solver = g2o.OptimizationAlgorithmLevenberg(solver)
self.optimizer.set_algorithm(solver)
# 传感器参数
self.camera_matrix = np.array([[720.0, 0, 320.0], [0, 720.0, 240.0], [0, 0, 1]])
# 特征提取与跟踪
self.orb = cv2.ORB_create()
self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 上一帧数据
self.prev_frame = None
self.prev_kp = None
self.prev_des = None
# 当前位姿(初始位置是原点)
self.current_pose = np.eye(4)
# 是否初始化
self.is_initialized = False
def initialize(self, rgb_image, depth_image, imu_data, gps_data=None):
"""初始化SLAM系统"""
# 提取特征
gray = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2GRAY)
kp, des = self.orb.detectAndCompute(gray, None)
# 保存第一帧
self.prev_frame = gray
self.prev_kp = kp
self.prev_des = des
# 设置初始位置
if gps_data is not None:
# 如果有GPS,用GPS初始化位置(简化表示)
self.current_pose[0, 3] = gps_data['x']
self.current_pose[1, 3] = gps_data['y']
self.current_pose[2, 3] = gps_data['z']
# 添加初始位姿到优化器
pose_vertex = g2o.VertexSE3Expmap()
pose_vertex.set_id(0)
pose_vertex.set_estimate(self._pose_to_g2o(self.current_pose))
pose_vertex.set_fixed(True) # 固定第一帧
self.optimizer.add_vertex(pose_vertex)
# 记录位姿
self.poses.append(self.current_pose.copy())
# 初始化完成
self.is_initialized = True
print("SLAM系统初始化完成")
def process_frame(self, rgb_image, depth_image, imu_data, gps_data=None):
"""处理新的数据帧"""
if not self.is_initialized:
self.initialize(rgb_image, depth_image, imu_data, gps_data)
return
# 提取当前帧特征
gray = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2GRAY)
curr_kp, curr_des = self.orb.detectAndCompute(gray, None)
# 特征匹配
matches = self.bf.match(self.prev_des, curr_des)
matches = sorted(matches, key=lambda x: x.distance)
# 用前30%的匹配点计算相机运动
num_good_matches = int(len(matches) * 0.3)
good_matches = matches[:num_good_matches]
# 提取匹配点
prev_pts = np.float32([self.prev_kp[m.queryIdx].pt for m in good_matches])
curr_pts = np.float32([curr_kp[m.trainIdx].pt for m in good_matches])
# 使用PnP(Perspective-n-Point)估计相机运动
# 首先获取特征点的3D位置(使用深度图)
pts_3d = []
for pt in prev_pts:
u, v = int(pt[0]), int(pt[1])
if 0 <= u < depth_image.shape[1] and 0 <= v < depth_image.shape[0]:
z = depth_image[v, u] / 1000.0 # 假设深度单位是毫米,转换为米
if z > 0:
x = (u - self.camera_matrix[0, 2]) * z / self.camera_matrix[0, 0]
y = (v - self.camera_matrix[1, 2]) * z / self.camera_matrix[1, 1]
pts_3d.append([x, y, z])
pts_3d = np.array(pts_3d)
if len(pts_3d) >= 6: # 需要至少6个点
# 使用PnP算法估计相机位姿
_, rvec, tvec, inliers = cv2.solvePnPRansac(
pts_3d, curr_pts[:len(pts_3d)], self.camera_matrix, None)
# 将旋转向量转换为旋转矩阵
rmat, _ = cv2.Rodrigues(rvec)
# 构建变换矩阵
transform = np.eye(4)
transform[:3, :3] = rmat
transform[:3, 3] = tvec.squeeze()
# 获取IMU数据估计的位姿变化(简化)
imu_delta_pose = self._estimate_pose_from_imu(imu_data)
# 融合视觉和IMU的位姿估计(简单加权平均)
vision_weight = 0.7
imu_weight = 0.3
delta_pose = np.eye(4)
delta_pose[:3, :3] = vision_weight * transform[:3, :3] + imu_weight * imu_delta_pose[:3, :3]
delta_pose[:3, 3] = vision_weight * transform[:3, 3] + imu_weight * imu_delta_pose[:3, 3]
# 如果有GPS数据,进一步融合
if gps_data is not None:
self._fuse_with_gps(delta_pose, gps_data)
# 更新当前位姿
self.current_pose = self.current_pose @ delta_pose
# 添加新的位姿顶点到优化器
pose_id = len(self.poses)
pose_vertex = g2o.VertexSE3Expmap()
pose_vertex.set_id(pose_id)
pose_vertex.set_estimate(self._pose_to_g2o(self.current_pose))
self.optimizer.add_vertex(pose_vertex)
# 添加位姿间的边(约束)
edge = g2o.EdgeSE3Expmap()
edge.set_vertex(0, self.optimizer.vertex(pose_id - 1))
edge.set_vertex(1, self.optimizer.vertex(pose_id))
edge.set_measurement(self._pose_to_g2o(delta_pose))
# 设置信息矩阵(协方差的逆)
information = np.eye(6) * 100 # 简化版,实际应根据匹配质量和IMU可靠性调整
edge.set_information(information)
self.optimizer.add_edge(edge)
# 处理观测到的特征点/路标
self._process_landmarks(pts_3d, curr_pts, pose_id)
# 优化图(每10帧执行一次全局优化)
if pose_id % 10 == 0:
self.optimizer.initialize_optimization()
self.optimizer.optimize(10) # 10次迭代
# 更新位姿
for i in range(len(self.poses)):
vertex = self.optimizer.vertex(i)
self.poses[i] = self._g2o_to_pose(vertex.estimate())
self.current_pose = self.poses[-1]
# 记录当前位姿
self.poses.append(self.current_pose.copy())
# 更新上一帧数据
self.prev_frame = gray
self.prev_kp = curr_kp
self.prev_des = curr_des
def _estimate_pose_from_imu(self, imu_data):
"""从IMU数据估计位姿变化"""
# 简化的IMU处理,实际应用中需要更复杂的处理
# 从加速度和角速度积分得到位置和姿态变化
dt = 0.1 # 假设时间间隔
acc = np.array([imu_data['accelerometer']['x'],
imu_data['accelerometer']['y'],
imu_data['accelerometer']['z']])
gyro = np.array([imu_data['gyroscope']['x'],
imu_data['gyroscope']['y'],
imu_data['gyroscope']['z']])
# 简化:假设绕z轴旋转
theta = gyro[2] * dt
# 构建旋转矩阵(简化为2D平面旋转)
R = np.eye(3)
R[0, 0] = np.cos(theta)
R[0, 1] = -np.sin(theta)
R[1, 0] = np.sin(theta)
R[1, 1] = np.cos(theta)
# 计算位移(简化)
acc_without_gravity = acc - np.array([0, 0, 9.81]) # 移除重力(简化)
displacement = 0.5 * acc_without_gravity * dt * dt
# 构建变换矩阵
transform = np.eye(4)
transform[:3, :3] = R
transform[:3, 3] = displacement
return transform
def _fuse_with_gps(self, delta_pose, gps_data):
"""融合GPS数据进一步优化位姿"""
# 简化的GPS融合
# 实际应用中需要坐标系转换和卡尔曼滤波融合
# 根据GPS数据调整delta_pose中的位移部分
gps_weight = 0.2 # GPS权重
# 假设GPS数据已转换为相同坐标系
gps_position = np.array([gps_data['x'], gps_data['y'], gps_data['z']])
# 预测的新位置
predicted_position = self.current_pose[:3, 3] + delta_pose[:3, 3]
# 计算GPS与预测位置的差值
position_error = gps_position - predicted_position
# 根据GPS调整位移
delta_pose[:3, 3] += gps_weight * position_error
def _process_landmarks(self, pts_3d, curr_pts, pose_id):
"""处理观测到的特征点/路标"""
for i in range(len(pts_3d)):
# 将3D点从相机坐标系转换到世界坐标系
pt_world = self.current_pose @ np.append(pts_3d[i], 1)
# 生成唯一ID
pt_id = len(self.landmarks) + 1000
# 添加路标点到优化器(如果是新点)
if pt_id not in self.landmarks:
landmark = g2o.VertexPointXYZ()
landmark.set_id(pt_id)
landmark.set_estimate(pt_world[:3])
self.optimizer.add_vertex(landmark)
self.landmarks[pt_id] = pt_world[:3]
# 添加相机-路标观测边
edge = g2o.EdgeSE3PointXYZ()
edge.set_vertex(0, self.optimizer.vertex(pose_id))
edge.set_vertex(1, self.optimizer.vertex(pt_id))
# 设置测量值(在相机坐标系中的3D点)
edge.set_measurement(pts_3d[i])
# 设置信息矩阵
information = np.eye(3) * 1000 # 简化,实际应根据深度不确定性设置
edge.set_information(information)
self.optimizer.add_edge(edge)
def _pose_to_g2o(self, pose):
"""将4x4变换矩阵转换为g2o SE3Quat"""
R = pose[:3, :3]
t = pose[:3, 3]
# 转换为g2o::SE3Quat
# 实际代码需要根据g2o接口进行正确转换
return g2o.SE3Quat(R, t)
def _g2o_to_pose(self, g2o_pose):
"""将g2o SE3Quat转换为4x4变换矩阵"""
# 从g2o::SE3Quat获取旋转和平移
# 实际代码需要根据g2o接口进行正确转换
R = g2o_pose.rotation().matrix()
t = g2o_pose.translation()
# 构建4x4变换矩阵
pose = np.eye(4)
pose[:3, :3] = R
pose[:3, 3] = t
return pose
def get_current_pose(self):
"""获取当前位姿"""
return self.current_pose
def get_trajectory(self):
"""获取完整轨迹"""
return self.poses
def get_map_points(self):
"""获取地图点"""
return self.landmarks
3. 自动驾驶感知系统
// C++自动驾驶感知系统示例
#include <vector>
#include <unordered_map>
#include <memory>
#include <iostream>
// 前向声明
class SensorData;
class Object;
class Camera;
class IMU;
class LiDAR;
class GPS;
class Fusion;
// 传感器数据基类
class SensorData {
public:
double timestamp;
SensorData(double time) : timestamp(time) {}
virtual ~SensorData() = default;
};
// 相机数据
class CameraData : public SensorData {
public:
// 简化:假设图像数据是一个指针
void* image_data;
int width, height, channels;
CameraData(double time, void* data, int w, int h, int c)
: SensorData(time), image_data(data), width(w), height(h), channels(c) {}
};
// IMU数据
class IMUData : public SensorData {
public:
double acc_x, acc_y, acc_z; // 加速度
double gyro_x, gyro_y, gyro_z; // 角速度
IMUData(double time, double ax, double ay, double az, double gx, double gy, double gz)
: SensorData(time), acc_x(ax), acc_y(ay), acc_z(az), gyro_x(gx), gyro_y(gy), gyro_z(gz) {}
};
// LiDAR数据
class LiDARData : public SensorData {
public:
// 简化:假设点云是3D点的vector
std::vector<std::array<float, 3>> point_cloud;
LiDARData(double time, const std::vector<std::array<float, 3>>& points)
: SensorData(time), point_cloud(points) {}
};
// GPS数据
class GPSData : public SensorData {
public:
double latitude, longitude, altitude;
double accuracy;
GPSData(double time, double lat, double lon, double alt, double acc)
: SensorData(time), latitude(lat), longitude(lon), altitude(alt), accuracy(acc) {}
};
// 检测到的物体类
class Object {
public:
enum class Type {
UNKNOWN,
VEHICLE,
PEDESTRIAN,
CYCLIST,
TRAFFIC_SIGN
};
Type type;
std::array<float, 3> position; // 世界坐标中的位置
std::array<float, 3> dimensions; // 长宽高
float orientation; // 朝向角
float confidence; // 检测置信度
// 目标的ID,用于跟踪
int tracking_id;
// 目标的速度
std::array<float, 3> velocity;
Object(Type t, const std::array<float, 3>& pos, const std::array<float, 3>& dim,
float orient, float conf)
: type(t), position(pos), dimensions(dim), orientation(orient), confidence(conf),
tracking_id(-1), velocity({0.0f, 0.0f, 0.0f}) {}
};
// 相机传感器
class Camera {
private:
int id;
std::array<double, 9> intrinsics; // 内参矩阵
std::array<double, 6> extrinsics; // 外参(位置和姿态)
public:
Camera(int camera_id, const std::array<double, 9>& intr, const std::array<double, 6>& extr)
: id(camera_id), intrinsics(intr), extrinsics(extr) {}
std::vector<Object> detect_objects(const CameraData& data) {
// 在实际应用中,这里会调用计算机视觉算法进行物体检测
std::cout << "相机 " << id << " 正在检测物体..." << std::endl;
// 简化:返回一些模拟的检测结果
std::vector<Object> detected_objects;
// 模拟检测到一辆车
detected_objects.emplace_back(
Object::Type::VEHICLE,
std::array<float, 3>{10.0f, 0.0f, 0.0f}, // 位置
std::array<float, 3>{4.5f, 2.0f, 1.5f}, // 尺寸
0.0f, // 朝向
0.95f // 置信度
);
// 模拟检测到一个行人
detected_objects.emplace_back(
Object::Type::PEDESTRIAN,
std::array<float, 3>{5.0f, -2.0f, 0.0f}, // 位置
std::array<float, 3>{0.5f, 0.5f, 1.8f}, // 尺寸
0.0f, // 朝向
0.85f // 置信度
);
std::cout << "相机检测到 " << detected_objects.size() << " 个物体" << std::endl;
return detected_objects;
}
const std::array<double, 9>& get_intrinsics() const {
return intrinsics;
}
const std::array<double, 6>& get_extrinsics() const {
return extrinsics;
}
};
// IMU传感器
class IMU {
private:
std::array<double, 3> position; // IMU在车辆上的位置
std::array<double, 3> orientation; // IMU的朝向
public:
IMU(const std::array<double, 3>& pos, const std::array<double, 3>& orient)
: position(pos), orientation(orient) {}
std::array<double, 6> get_motion(const IMUData& data) {
// 处理IMU数据,估计运动
std::cout << "处理IMU数据..." << std::endl;
// 简化:返回线性加速度和角速度
std::array<double, 6> motion = {
data.acc_x, data.acc_y, data.acc_z, // 线性加速度
data.gyro_x, data.gyro_y, data.gyro_z // 角速度
};
return motion;
}
};
// LiDAR传感器
class LiDAR {
private:
int id;
std::array<double, 6> extrinsics; // 外参(位置和姿态)
public:
LiDAR(int lidar_id, const std::array<double, 6>& extr)
: id(lidar_id), extrinsics(extr) {}
std::vector<Object> detect_objects(const LiDARData& data) {
// 在实际应用中,这里会处理点云数据并检测物体
std::cout << "LiDAR " << id << " 正在检测物体,点云包含 "
<< data.point_cloud.size() << " 个点" << std::endl;
// 简化:返回一些模拟的检测结果
std::vector<Object> detected_objects;
// 模拟检测到一辆车
detected_objects.emplace_back(
Object::Type::VEHICLE,
std::array<float, 3>{12.0f, 0.5f, 0.0f}, // 位置
std::array<float, 3>{4.6f, 2.1f, 1.6f}, // 尺寸
0.1f, // 朝向
0.98f // 置信度
);
// 模拟检测到一个骑车人
detected_objects.emplace_back(
Object::Type::CYCLIST,
std::array<float, 3>{8.0f, -3.0f, 0.0f}, // 位置
std::array<float, 3>{1.8f, 0.8f, 1.7f}, // 尺寸
0.2f, // 朝向
0.92f // 置信度
);
std::cout << "LiDAR检测到 " << detected_objects.size() << " 个物体" << std::endl;
return detected_objects;
}
const std::array<double, 6>& get_extrinsics() const {
return extrinsics;
}
};
// GPS传感器
class GPS {
private:
std::array<double, 3> position; // GPS天线在车辆上的位置
public:
GPS(const std::array<double, 3>& pos) : position(pos) {}
std::array<double, 3> get_position(const GPSData& data) {
// 处理GPS数据,获取全球位置
std::cout << "处理GPS数据..." << std::endl;
// 简化:将经纬度转换为局部坐标(实际应使用专业库)
// 这里只是一个非常简化的示例
double x = data.longitude * 111320.0; // 约为每度经度的米数(赤道附近)
double y = data.latitude * 110540.0; // 约为每度纬度的米数
double z = data.altitude;
std::cout << "GPS位置: (" << x << ", " << y << ", " << z << ")" << std::endl;
return {x, y, z};
}
};
// 传感器融合系统
class Fusion {
private:
std::shared_ptr<Camera> camera;
std::shared_ptr<IMU> imu;
std::shared_ptr<LiDAR> lidar;
std::shared_ptr<GPS> gps;
// 物体跟踪状态
std::unordered_map<int, Object> tracked_objects;
int next_tracking_id = 0;
// 当前位置和姿态估计
std::array<double, 3> position = {0.0, 0.0, 0.0};
std::array<double, 3> orientation = {0.0, 0.0, 0.0};
std::array<double, 3> velocity = {0.0, 0.0, 0.0};
double last_update_time = 0.0;
public:
Fusion(std::shared_ptr<Camera> cam, std::shared_ptr<IMU> imu_sensor,
std::shared_ptr<LiDAR> lidar_sensor, std::shared_ptr<GPS> gps_sensor)
: camera(cam), imu(imu_sensor), lidar(lidar_sensor), gps(gps_sensor) {}
void process_camera_data(const CameraData& data) {
// 处理相机数据
std::vector<Object> camera_objects = camera->detect_objects(data);
// 更新跟踪状态(简化)
for (auto& obj : camera_objects) {
update_tracking(obj, data.timestamp);
}
}
void process_imu_data(const IMUData& data) {
// 处理IMU数据
std::array<double, 6> motion = imu->get_motion(data);
// 更新位置和姿态估计
if (last_update_time > 0.0) {
double dt = data.timestamp - last_update_time;
// 更新姿态(简化)
orientation[0] += motion[3] * dt; // roll
orientation[1] += motion[4] * dt; // pitch
orientation[2] += motion[5] * dt; // yaw
// 更新速度(简化)
velocity[0] += motion[0] * dt;
velocity[1] += motion[1] * dt;
velocity[2] += (motion[2] - 9.81) * dt; // 减去重力
// 更新位置(简化)
position[0] += velocity[0] * dt;
position[1] += velocity[1] * dt;
position[2] += velocity[2] * dt;
}
last_update_time = data.timestamp;
}
void process_lidar_data(const LiDARData& data) {
// 处理LiDAR数据
std::vector<Object> lidar_objects = lidar->detect_objects(data);
// 更新跟踪状态
for (auto& obj : lidar_objects) {
update_tracking(obj, data.timestamp);
}
}
void process_gps_data(const GPSData& data) {
// 处理GPS数据
std::array<double, 3> gps_position = gps->get_position(data);
// 融合GPS位置与当前估计(简化的卡尔曼滤波)
double gps_weight = 0.3; // GPS权重
double imu_weight = 0.7; // IMU权重
// 简单加权平均
position[0] = imu_weight * position[0] + gps_weight * gps_position[0];
position[1] = imu_weight * position[1] + gps_weight * gps_position[1];
position[2] = imu_weight * position[2] + gps_weight * gps_position[2];
}
void update_tracking(Object& obj, double timestamp) {
// 简化的目标跟踪逻辑
// 寻找最近的已跟踪目标
int best_match_id = -1;
float min_distance = 5.0f; // 最大匹配距离阈值
for (const auto& pair : tracked_objects) {
const Object& tracked_obj = pair.second;
// 如果类型不同,跳过
if (tracked_obj.type != obj.type) continue;
// 计算距离
float dx = tracked_obj.position[0] - obj.position[0];
float dy = tracked_obj.position[1] - obj.position[1];
float dz = tracked_obj.position[2] - obj.position[2];
float distance = std::sqrt(dx*dx + dy*dy + dz*dz);
if (distance < min_distance) {
min_distance = distance;
best_match_id = pair.first;
}
}
if (best_match_id >= 0) {
// 找到匹配的已跟踪目标,更新
Object& tracked_obj = tracked_objects[best_match_id];
// 计算速度(简化)
float dt = timestamp - last_update_time;
if (dt > 0) {
tracked_obj.velocity[0] = (obj.position[0] - tracked_obj.position[0]) / dt;
tracked_obj.velocity[1] = (obj.position[1] - tracked_obj.position[1]) / dt;
tracked_obj.velocity[2] = (obj.position[2] - tracked_obj.position[2]) / dt;
}
// 更新位置和其他属性
tracked_obj.position = obj.position;
tracked_obj.dimensions = obj.dimensions;
tracked_obj.orientation = obj.orientation;
tracked_obj.confidence = std::max(tracked_obj.confidence, obj.confidence);
// 设置跟踪ID
obj.tracking_id = best_match_id;
} else {
// 创建新的跟踪目标
obj.tracking_id = next_tracking_id++;
tracked_objects[obj.tracking_id] = obj;
}
}
const std::unordered_map<int, Object>& get_tracked_objects() const {
return tracked_objects;
}
std::array<double, 3> get_position() const {
return position;
}
std::array<double, 3> get_orientation() const {
return orientation;
}
std::array<double, 3> get_velocity() const {
return velocity;
}
};
int main() {
// 创建传感器对象
auto camera = std::make_shared<Camera>(
0, // ID
std::array<double, 9>{720.0, 0.0, 320.0, 0.0, 720.0, 240.0, 0.0, 0.0, 1.0}, // 内参
std::array<double, 6>{0.0, 0.0, 0.0, 0.0, 0.0, 0.0} // 外参
);
auto imu = std::make_shared<IMU>(
std::array<double, 3>{0.0, 0.0, 0.0}, // 位置
std::array<double, 3>{0.0, 0.0, 0.0} // 朝向
);
auto lidar = std::make_shared<LiDAR>(
0, // ID
std::array<double, 6>{0.0, 0.0, 1.8, 0.0, 0.0, 0.0} // 外参
);
auto gps = std::make_shared<GPS>(
std::array<double, 3>{0.0, 0.0, 1.5} // 位置
);
// 创建融合系统
Fusion fusion(camera, imu, lidar, gps);
// 模拟传感器数据
double timestamp = 0.0;
// 模拟相机数据
CameraData camera_data(timestamp, nullptr, 640, 480, 3);
// 模拟IMU数据
IMUData imu_data(timestamp, 0.1, 0.0, 9.81, 0.01, 0.02, 0.005);
// 模拟LiDAR数据
std::vector<std::array<float, 3>> point_cloud;
// 添加一些模拟的点云数据
for (int i = 0; i < 1000; ++i) {
point_cloud.push_back({float(i % 20), float((i / 20) % 20 - 10), float(i % 5)});
}
LiDARData lidar_data(timestamp, point_cloud);
// 模拟GPS数据
GPSData gps_data(timestamp, 37.7749, -122.4194, 10.0, 2.5);
// 处理数据
fusion.process_camera_data(camera_data);
fusion.process_imu_data(imu_data);
fusion.process_lidar_data(lidar_data);
fusion.process_gps_data(gps_data);
// 获取跟踪的物体
const auto& tracked_objects = fusion.get_tracked_objects();
std::cout << "跟踪物体数量: " << tracked_objects.size() << std::endl;
for (const auto& pair : tracked_objects) {
const Object& obj = pair.second;
std::cout << "物体ID: " << obj.tracking_id << ", 类型: ";
switch (obj.type) {
case Object::Type::VEHICLE:
std::cout << "车辆";
break;
case Object::Type::PEDESTRIAN:
std::cout << "行人";
break;
case Object::Type::CYCLIST:
std::cout << "骑车人";
break;
case Object::Type::TRAFFIC_SIGN:
std::cout << "交通标志";
break;
default:
std::cout << "未知";
}
std::cout << ", 位置: (" << obj.position[0] << ", " << obj.position[1] << ", " << obj.position[2] << ")"
<< ", 速度: (" << obj.velocity[0] << ", " << obj.velocity[1] << ", " << obj.velocity[2] << ")"
<< ", 置信度: " << obj.confidence
<< std::endl;
}
// 获取自身位置和姿态
auto position = fusion.get_position();
auto orientation = fusion.get_orientation();
auto velocity = fusion.get_velocity();
std::cout << "自身位置: (" << position[0] << ", " << position[1] << ", " << position[2] << ")" << std::endl;
std::cout << "自身姿态: (" << orientation[0] << ", " << orientation[1] << ", " << orientation[2] << ")" << std::endl;
std::cout << "自身速度: (" << velocity[0] << ", " << velocity[1] << ", " << velocity[2] << ")" << std::endl;
return 0;
}
七、传感器同步与校准
1. 时间同步策略
为了准确融合多传感器数据,必须解决不同传感器的时间戳不同步问题。
主要方法:
- 硬件同步:通过硬件触发信号确保同步采集
- 时间戳对齐:将不同传感器数据根据时间戳插值或重采样
- 滑动窗口方法:在时间窗口内收集传感器数据,然后一起处理
import numpy as np
from scipy.interpolate import interp1d
class SensorSynchronizer:
def __init__(self, buffer_size=200):
# 数据缓冲区
self.camera_buffer = [] # [(timestamp, data), ...]
self.imu_buffer = [] # [(timestamp, data), ...]
self.lidar_buffer = [] # [(timestamp, data), ...]
self.gps_buffer = [] # [(timestamp, data), ...]
# 最大缓冲区大小
self.buffer_size = buffer_size
# 上次处理的时间戳
self.last_processed_time = 0
def add_camera_data(self, timestamp, data):
"""添加相机数据到缓冲区"""
self._add_to_buffer(self.camera_buffer, timestamp, data)
def add_imu_data(self, timestamp, data):
"""添加IMU数据到缓冲区"""
self._add_to_buffer(self.imu_buffer, timestamp, data)
def add_lidar_data(self, timestamp, data):
"""添加LiDAR数据到缓冲区"""
self._add_to_buffer(self.lidar_buffer, timestamp, data)
def add_gps_data(self, timestamp, data):
"""添加GPS数据到缓冲区"""
self._add_to_buffer(self.gps_buffer, timestamp, data)
def _add_to_buffer(self, buffer, timestamp, data):
"""添加数据到指定缓冲区"""
buffer.append((timestamp, data))
buffer.sort(key=lambda x: x[0]) # 按时间戳排序
# 保持缓冲区大小
if len(buffer) > self.buffer_size:
buffer.pop(0)
def get_synchronized_data(self, target_time=None, max_time_diff=0.1):
"""获取同步的传感器数据
Args:
target_time: 目标时间戳,如果为None则使用最新数据时间
max_time_diff: 允许的最大时间差(秒)
Returns:
同步后的传感器数据字典,如果某传感器数据不可用则为None
"""
# 如果未指定目标时间,使用最新数据的时间
if target_time is None:
timestamps = []
if self.camera_buffer: timestamps.append(self.camera_buffer[-1][0])
if self.imu_buffer: timestamps.append(self.imu_buffer[-1][0])
if self.lidar_buffer: timestamps.append(self.lidar_buffer[-1][0])
if self.gps_buffer: timestamps.append(self.gps_buffer[-1][0])
if not timestamps:
return None # 没有可用数据
target_time = min(timestamps) # 使用最早的最新数据时间
# 确保目标时间比上次处理的时间更新
if target_time <= self.last_processed_time:
return None
self.last_processed_time = target_time
# 获取同步数据
camera_data = self._get_data_at_time(self.camera_buffer, target_time, max_time_diff)
imu_data = self._interpolate_imu_data(target_time, max_time_diff)
lidar_data = self._get_data_at_time(self.lidar_buffer, target_time, max_time_diff)
gps_data = self._get_data_at_time(self.gps_buffer, target_time, max_time_diff)
return {
'timestamp': target_time,
'camera': camera_data,
'imu': imu_data,
'lidar': lidar_data,
'gps': gps_data
}
def _get_data_at_time(self, buffer, target_time, max_time_diff):
"""获取指定时间的数据,如果时间差太大则返回None"""
if not buffer:
return None
# 找到最接近目标时间的数据
closest_data = None
min_time_diff = float('inf')
for timestamp, data in buffer:
time_diff = abs(timestamp - target_time)
if time_diff < min_time_diff:
min_time_diff = time_diff
closest_data = data
# 如果时间差太大,返回None
if min_time_diff > max_time_diff:
return None
return closest_data
def _interpolate_imu_data(self, target_time, max_time_diff):
"""插值计算目标时间的IMU数据"""
if len(self.imu_buffer) < 2:
return self._get_data_at_time(self.imu_buffer, target_time, max_time_diff)
# 找到目标时间附近的两个IMU数据点
before_idx = None
after_idx = None
for i, (timestamp, _) in enumerate(self.imu_buffer):
if timestamp <= target_time:
before_idx = i
if timestamp >= target_time and after_idx is None:
after_idx = i
# 检查是否有足够数据进行插值
if before_idx is None or after_idx is None or before_idx == after_idx:
return self._get_data_at_time(self.imu_buffer, target_time, max_time_diff)
t1, data1 = self.imu_buffer[before_idx]
t2, data2 = self.imu_buffer[after_idx]
# 检查时间差是否在可接受范围内
if target_time - t1 > max_time_diff or t2 - target_time > max_time_diff:
return self._get_data_at_time(self.imu_buffer, target_time, max_time_diff)
# 线性插值
alpha = (target_time - t1) / (t2 - t1)
# 假设IMU数据有acc和gyro字段
interpolated_data = {
'accelerometer': {
'x': data1['accelerometer']['x'] * (1 - alpha) + data2['accelerometer']['x'] * alpha,
'y': data1['accelerometer']['y'] * (1 - alpha) + data2['accelerometer']['y'] * alpha,
'z': data1['accelerometer']['z'] * (1 - alpha) + data2['accelerometer']['z'] * alpha
},
'gyroscope': {
'x': data1['gyroscope']['x'] * (1 - alpha) + data2['gyroscope']['x'] * alpha,
'y': data1['gyroscope']['y'] * (1 - alpha) + data2['gyroscope']['y'] * alpha,
'z': data1['gyroscope']['z'] * (1 - alpha) + data2['gyroscope']['z'] * alpha
}
}
return interpolated_data
def clear_old_data(self, time_threshold):
"""清除早于指定时间的数据"""
self._clear_old_data_from_buffer(self.camera_buffer, time_threshold)
self._clear_old_data_from_buffer(self.imu_buffer, time_threshold)
self._clear_old_data_from_buffer(self.lidar_buffer, time_threshold)
self._clear_old_data_from_buffer(self.gps_buffer, time_threshold)
def _clear_old_data_from_buffer(self, buffer, time_threshold):
"""从指定缓冲区清除早于阈值的数据"""
while buffer and buffer[0][0] < time_threshold:
buffer.pop(0)
2. 空间校准方法
不同传感器之间需要进行空间校准,确定它们之间的位置和方向关系,这种关系通常用外参(extrinsic parameters)表示。
主要校准方法:
- 目标板校准:使用特殊标定板同时被多个传感器观测
- 相互信息最大化:通过最大化不同传感器数据的相互信息获取变换
- 手眼校准:机器人领域常用的校准方法
import numpy as np
import cv2
class SensorCalibrator:
def __init__(self):
# 校准结果
self.camera_to_lidar = None # 相机到LiDAR的变换
self.camera_to_imu = None # 相机到IMU的变换
self.imu_to_gps = None # IMU到GPS的变换
def calibrate_camera_lidar(self, image_points, lidar_points):
"""校准相机与LiDAR
Args:
image_points: 图像中检测到的特征点(2D)
lidar_points: 对应的LiDAR点(3D)
Returns:
变换矩阵,从LiDAR坐标系到相机坐标系
"""
assert len(image_points) == len(lidar_points), "点对数量不匹配"
assert len(image_points) >= 6, "需要至少6个点对进行PnP求解"
# 假设相机内参已知
camera_matrix = np.array([[720.0, 0, 320.0], [0, 720.0, 240.0], [0, 0, 1]])
dist_coeffs = np.zeros(4) # 假设无畸变
# 使用PnP算法求解相机位姿
_, rvec, tvec = cv2.solvePnP(
np.array(lidar_points, dtype=np.float32),
np.array(image_points, dtype=np.float32),
camera_matrix, dist_coeffs
)
# 将旋转向量转换为旋转矩阵
R, _ = cv2.Rodrigues(rvec)
# 构建变换矩阵
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = tvec.squeeze()
self.camera_to_lidar = T
return T
def calibrate_camera_imu(self, camera_poses, imu_poses):
"""校准相机与IMU
Args:
camera_poses: 相机位姿列表 [T1, T2, ...]
imu_poses: 对应的IMU位姿列表 [T1, T2, ...]
Returns:
从IMU坐标系到相机坐标系的变换矩阵
"""
# 使用手眼校准方法
# 实际应用中应使用更复杂的算法和多次采样优化
# 简化版本:使用平均变换
transforms = []
for cam_pose, imu_pose in zip(camera_poses, imu_poses):
# 计算从IMU到相机的变换
# T_cam = T_imu * T_imu_to_cam
# 因此 T_imu_to_cam = inv(T_imu) * T_cam
T_imu_to_cam = np.linalg.inv(imu_pose) @ cam_pose
transforms.append(T_imu_to_cam)
# 计算平均变换(简化)
# 实际应该使用SE3流形上的平均
avg_transform = np.mean(np.array(transforms), axis=0)
# 确保旋转部分是有效的旋转矩阵
U, _, Vt = np.linalg.svd(avg_transform[:3, :3])
avg_transform[:3, :3] = U @ Vt
self.camera_to_imu = avg_transform
return avg_transform
def calibrate_imu_gps(self, imu_trajectory, gps_trajectory):
"""校准IMU与GPS
Args:
imu_trajectory: IMU轨迹点列表 [(x1,y1,z1), (x2,y2,z2), ...]
gps_trajectory: 对应的GPS轨迹点列表 [(x1,y1,z1), (x2,y2,z2), ...]
Returns:
从GPS坐标系到IMU坐标系的变换
"""
# 使用Umeyama算法求解刚体变换
# 在实际应用中,GPS和IMU轨迹需要先在时间上同步
imu_points = np.array(imu_trajectory)
gps_points = np.array(gps_trajectory)
# 计算质心
imu_centroid = np.mean(imu_points, axis=0)
gps_centroid = np.mean(gps_points, axis=0)
# 去中心化
imu_centered = imu_points - imu_centroid
gps_centered = gps_points - gps_centroid
# 计算协方差矩阵
H = imu_centered.T @ gps_centered
# SVD分解
U, _, Vt = np.linalg.svd(H)
# 计算旋转矩阵
R = U @ Vt
# 确保是有效的旋转矩阵(行列式为1)
if np.linalg.det(R) < 0:
Vt[-1, :] *= -1
R = U @ Vt
# 计算平移向量
t = gps_centroid - R @ imu_centroid
# 构建变换矩阵
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = t
self.imu_to_gps = T
return T
def transform_point(self, point, source_frame, target_frame):
"""将点从源坐标系转换到目标坐标系
Args:
point: 3D点坐标 [x, y, z]
source_frame: 源坐标系名称 ('camera', 'lidar', 'imu', 'gps')
target_frame: 目标坐标系名称 ('camera', 'lidar', 'imu', 'gps')
Returns:
目标坐标系中的点坐标
"""
# 确保所有校准都已完成
if self.camera_to_lidar is None or self.camera_to_imu is None or self.imu_to_gps is None:
raise ValueError("未完成所有传感器校准")
# 创建从每个坐标系到基准坐标系(这里选相机)的变换
transforms = {
'camera': np.eye(4),
'lidar': np.linalg.inv(self.camera_to_lidar),
'imu': np.linalg.inv(self.camera_to_imu),
'gps': np.linalg.inv(self.camera_to_imu) @ np.linalg.inv(self.imu_to_gps)
}
# 将点转换为齐次坐标
p_homogeneous = np.append(point, 1)
# 从源坐标系转换到基准坐标系(相机)
p_camera = transforms[source_frame] @ p_homogeneous
# 从基准坐标系转换到目标坐标系
p_target = np.linalg.inv(transforms[target_frame]) @ p_camera
# 返回非齐次坐标
return p_target[:3]
八、实际应用案例
1. 移动机器人导航系统
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import math
import time
class NavigationSystem:
def __init__(self):
# 状态向量 [x, y, theta, v, omega]
self.state = np.zeros(5)
# 状态协方差
self.P = np.diag([1.0, 1.0, 0.1, 0.1, 0.1])
# 地图(简化为障碍物列表)
self.obstacles = []
# 规划的路径
self.path = []
# 最后更新时间
self.last_update_time = None
# 传感器融合系统
self.sensor_fusion = SensorFusion()
# 路径规划器
self.path_planner = PathPlanner()
# 控制器
self.controller = Controller()
# 历史轨迹
self.trajectory = []
def update_map(self, obstacles):
"""更新地图"""
self.obstacles = obstacles
def set_goal(self, goal_position):
"""设置目标位置"""
# 使用路径规划器规划路径
self.path = self.path_planner.plan_path(
self.state[:2], # 当前位置
goal_position, # 目标位置
self.obstacles # 地图障碍物
)
def process_sensor_data(self, camera_data=None, imu_data=None, lidar_data=None, gps_data=None):
"""处理传感器数据"""
# 更新时间
current_time = time.time()
dt = 0.0
if self.last_update_time is not None:
dt = current_time - self.last_update_time
self.last_update_time = current_time
# 传感器融合
pose_update = self.sensor_fusion.update(
camera_data, imu_data, lidar_data, gps_data, dt)
# 更新状态
if pose_update is not None:
self.state[:3] = pose_update[:3] # 更新位置和朝向
self.state[3:] = pose_update[3:] # 更新速度和角速度
# 更新协方差
self.P = self.sensor_fusion.get_covariance()
# 记录轨迹
self.trajectory.append(self.state[:2].copy())
def update_control(self):
"""更新控制指令"""
if not self.path:
return 0.0, 0.0 # 没有路径,停止
# 获取当前位置和朝向
x, y, theta = self.state[:3]
# 找到当前路径上的目标点
target_point = self._get_current_target()
# 计算控制指令
v, omega = self.controller.compute_control(
np.array([x, y]),
theta,
target_point,
self.state[3], # 当前线速度
self.state[4] # 当前角速度
)
return v, omega
def _get_current_target(self):
"""获取当前跟踪的路径点"""
if not self.path:
return None
# 简单策略:选择路径上距离当前位置一定距离的点
# 更复杂的策略可以基于时间或曲率等因素
current_pos = self.state[:2]
# 找到最近的路径点
distances = [np.linalg.norm(current_pos - np.array(p)) for p in self.path]
min_idx = np.argmin(distances)
# 选择前方一定距离的点作为目标
lookahead_distance = 1.0 # 前视距离
for i in range(min_idx, len(self.path)):
dist = np.linalg.norm(current_pos - np.array(self.path[i]))
if dist >= lookahead_distance:
return np.array(self.path[i])
# 如果没有符合条件的点,返回终点
return np.array(self.path[-1])
def visualize(self):
"""可视化当前状态"""
plt.figure(figsize=(10, 8))
# 绘制障碍物
for obs in self.obstacles:
circle = plt.Circle((obs[0], obs[1]), obs[2], color='red', alpha=0.5)
plt.gca().add_artist(circle)
# 绘制路径
if self.path:
path_x = [p[0] for p in self.path]
path_y = [p[1] for p in self.path]
plt.plot(path_x, path_y, 'g--', label='Planned Path')
# 绘制历史轨迹
if self.trajectory:
traj_x = [p[0] for p in self.trajectory]
traj_y = [p[1] for p in self.trajectory]
plt.plot(traj_x, traj_y, 'b-', linewidth=2, label='Trajectory')
# 绘制当前位置
x, y, theta = self.state[:3]
plt.plot(x, y, 'ko', markersize=10, label='Robot')
# 画出方向箭头
arrow_length = 0.5
plt.arrow(x, y, arrow_length * math.cos(theta), arrow_length * math.sin(theta),
head_width=0.2, head_length=0.2, fc='k', ec='k')
# 绘制不确定性椭圆
cov = self.P[:2, :2] # 位置的协方差
eigenvals, eigenvecs = np.linalg.eig(cov)
# 椭圆参数
angle = np.degrees(np.arctan2(eigenvecs[1, 0], eigenvecs[0, 0]))
width, height = 2 * np.sqrt(eigenvals) # 95%置信区间
ellipse = Ellipse(xy=(x, y), width=width, height=height, angle=angle,
alpha=0.3, color='blue', label='Uncertainty')
plt.gca().add_artist(ellipse)
# 设置图表
plt.axis('equal')
plt.grid(True)
plt.legend()
plt.title('Robot Navigation Visualization')
plt.xlabel('X (m)')
plt.ylabel('Y (m)')
plt.show()
class SensorFusion:
def __init__(self):
# 状态向量 [x, y, theta, v, omega]
self.state = np.zeros(5)
# 状态协方差
self.P = np.diag([1.0, 1.0, 0.1, 0.1, 0.1])
# 过程噪声
self.Q = np.diag([0.01, 0.01, 0.01, 0.1, 0.1])
# 初始化滤波器(使用EKF)
self.initialize_ekf()
def initialize_ekf(self):
"""初始化EKF滤波器参数"""
pass # 此处简化,实际应包含雅可比矩阵计算等
def update(self, camera_data, imu_data, lidar_data, gps_data, dt):
"""更新状态估计"""
# 预测步骤
self._predict(dt)
# 更新步骤 - 处理各传感器数据
if camera_data is not None:
self._update_with_camera(camera_data)
if imu_data is not None:
self._update_with_imu(imu_data)
if lidar_data is not None:
self._update_with_lidar(lidar_data)
if gps_data is not None:
self._update_with_gps(gps_data)
return self.state
def _predict(self, dt):
"""EKF预测步骤"""
# 简化的运动模型
x, y, theta, v, omega = self.state
# 预测状态
x_new = x + v * math.cos(theta) * dt
y_new = y + v * math.sin(theta) * dt
theta_new = theta + omega * dt
# 简化:保持速度不变
v_new = v
omega_new = omega
self.state = np.array([x_new, y_new, theta_new, v_new, omega_new])
# 计算雅可比矩阵(简化)
F = np.eye(5)
F[0, 2] = -v * math.sin(theta) * dt
F[0, 3] = math.cos(theta) * dt
F[1, 2] = v * math.cos(theta) * dt
F[1, 3] = math.sin(theta) * dt
F[2, 4] = dt
# 更新协方差
self.P = F @ self.P @ F.T + self.Q * dt
def _update_with_camera(self, camera_data):
"""使用相机数据更新"""
# 实现相机观测模型和EKF更新
pass # 此处简化
def _update_with_imu(self, imu_data):
"""使用IMU数据更新"""
# 提取加速度和角速度
acc = np.array([imu_data['accelerometer']['x'],
imu_data['accelerometer']['y'],
imu_data['accelerometer']['z']])
gyro = np.array([imu_data['gyroscope']['x'],
imu_data['gyroscope']['y'],
imu_data['gyroscope']['z']])
# 构建观测矩阵(简化)
H = np.zeros((2, 5))
H[0, 3] = 1.0 # 速度
H[1, 4] = 1.0 # 角速度
# 观测值
z = np.array([np.linalg.norm(acc) - 9.81, gyro[2]]) # 简化
# 预测的观测值
z_pred = np.array([self.state[3], self.state[4]])
# 观测噪声
R = np.diag([0.5, 0.1])
# 计算卡尔曼增益
S = H @ self.P @ H.T + R
K = self.P @ H.T @ np.linalg.inv(S)
# 更新状态
innovation = z - z_pred
self.state = self.state + K @ innovation
# 更新协方差
self.P = (np.eye(5) - K @ H) @ self.P
def _update_with_lidar(self, lidar_data):
"""使用LiDAR数据更新"""
# 实现LiDAR观测模型和EKF更新
pass # 此处简化
def _update_with_gps(self, gps_data):
"""使用GPS数据更新"""
# 提取位置
gps_position = np.array([gps_data['x'], gps_data['y'], 0])
# 构建观测矩阵
H = np.zeros((2, 5))
H[0, 0] = 1.0 # x位置
H[1, 1] = 1.0 # y位置
# 观测值
z = gps_position[:2]
# 预测的观测值
z_pred = self.state[:2]
# 观测噪声(根据GPS精度)
R = np.eye(2) * (gps_data['accuracy'] ** 2)
# 计算卡尔曼增益
S = H @ self.P @ H.T + R
K = self.P @ H.T @ np.linalg.inv(S)
# 更新状态
innovation = z - z_pred
self.state = self.state + K @ innovation
# 更新协方差
self.P = (np.eye(5) - K @ H) @ self.P
def get_covariance(self):
"""获取当前状态协方差"""
return self.P
class PathPlanner:
def __init__(self):
pass
def plan_path(self, start, goal, obstacles):
"""规划从起点到终点的路径,避开障碍物
这里实现一个简化版的RRT(Rapidly-exploring Random Tree)算法
"""
# 初始化RRT树
tree = {tuple(start): None} # 节点: 父节点
# 最大迭代次数
max_iterations = 1000
# 步长
step_size = 0.5
# 搜索
for _ in range(max_iterations):
# 生成随机点(有概率直接取目标点)
if np.random.random() < 0.1:
random_point = goal
else:
random_point = np.random.uniform(
low=[start[0] - 10, start[1] - 10],
high=[start[0] + 10, start[1] + 10],
size=2
)
# 找到树中最近的节点
nearest_node = self._find_nearest(tree, random_point)
# 计算新节点
direction = random_point - np.array(nearest_node)
norm = np.linalg.norm(direction)
if norm > 0:
direction = direction / norm
new_node = np.array(nearest_node) + direction * step_size
# 检查新节点是否有效(无碰撞)
if self._is_valid(nearest_node, new_node, obstacles):
tree[tuple(new_node)] = nearest_node
# 检查是否可以连接到目标
if np.linalg.norm(new_node - goal) < step_size and \
self._is_valid(new_node, goal, obstacles):
tree[tuple(goal)] = tuple(new_node)
break
# 提取路径
path = self._extract_path(tree, start, goal)
# 平滑路径
smoothed_path = self._smooth_path(path, obstacles)
return smoothed_path
def _find_nearest(self, tree, point):
"""找到树中距离给定点最近的节点"""
return min(tree.keys(), key=lambda node: np.linalg.norm(np.array(node) - point))
def _is_valid(self, node1, node2, obstacles):
"""检查两节点之间的路径是否无碰撞"""
# 简化:检查线段是否与任何障碍物相交
for obs in obstacles:
obs_pos = np.array([obs[0], obs[1]])
obs_radius = obs[2]
# 使用点到线段的最小距离
p1 = np.array(node1)
p2 = np.array(node2)
# 计算线段长度
segment_length = np.linalg.norm(p2 - p1)
if segment_length == 0:
# 如果两点重合,直接计算点到障碍物的距离
distance = np.linalg.norm(p1 - obs_pos)
if distance < obs_radius:
return False
continue
# 计算投影点
t = max(0, min(1, np.dot(obs_pos - p1, p2 - p1) / (segment_length ** 2)))
projection = p1 + t * (p2 - p1)
# 计算距离
distance = np.linalg.norm(obs_pos - projection)
# 检查碰撞
if distance < obs_radius:
return False
return True
def _extract_path(self, tree, start, goal):
"""从树中提取路径"""
path = [goal]
current = tuple(goal)
while current != tuple(start):
current = tree[current]
path.append(current)
return path[::-1] # 反转路径,从起点到终点
def _smooth_path(self, path, obstacles):
"""平滑路径"""
# 简化:删除冗余点
i = 0
smoothed_path = [path[0]]
while i < len(path) - 1:
current = path[i]
# 尝试连接当前点与更远的点
for j in range(len(path) - 1, i, -1):
if self._is_valid(current, path[j], obstacles):
smoothed_path.append(path[j])
i = j
break
else:
i += 1
smoothed_path.append(path[i])
return smoothed_path
class Controller:
def __init__(self):
# 控制器参数
self.linear_gain = 0.5
self.angular_gain = 1.0
self.max_linear_speed = 1.0
self.max_angular_speed = 0.5
def compute_control(self, current_pos, current_theta, target_pos, current_v, current_omega):
"""计算控制指令"""
# 计算到目标的距离
distance = np.linalg.norm(target_pos - current_pos)
# 计算目标朝向
target_theta = math.atan2(target_pos[1] - current_pos[1], target_pos[0] - current_pos[0])
# 计算朝向误差(处理角度循环)
angle_error = target_theta - current_theta
angle_error = math.atan2(math.sin(angle_error), math.cos(angle_error))
# 简单的P控制器
v = self.linear_gain * distance
omega = self.angular_gain * angle_error
# 限制速度
v = max(0.0, min(v, self.max_linear_speed))
omega = max(-self.max_angular_speed, min(omega, self.max_angular_speed))
return v, omega
# 使用示例
def main():
# 创建导航系统
nav_system = NavigationSystem()
# 设置地图(简化为圆形障碍物:[x, y, radius])
obstacles = [
[5, 5, 1.0],
[8, 3, 1.5],
[2, 7, 0.8]
]
nav_system.update_map(obstacles)
# 设置目标
goal = [10, 10]
nav_system.set_goal(goal)
# 模拟传感器数据和导航过程
for t in range(100):
# 模拟传感器数据
camera_data = None # 简化示例不使用相机
imu_data = {
'accelerometer': {'x': 0.1, 'y': 0.05, 'z': 9.81},
'gyroscope': {'x': 0.01, 'y': 0.02, 'z': 0.03}
}
lidar_data = None # 简化示例不使用激光雷达
gps_data = {
'x': nav_system.state[0] + np.random.normal(0, 0.3),
'y': nav_system.state[1] + np.random.normal(0, 0.3),
'accuracy': 0.5
}
# 处理传感器数据
nav_system.process_sensor_data(camera_data, imu_data, lidar_data, gps_data)
# 计算控制指令
v, omega = nav_system.update_control()
# 模拟机器人运动(简化)
dt = 0.1 # 时间步长
nav_system.state[0] += v * math.cos(nav_system.state[2]) * dt
nav_system.state[1] += v * math.sin(nav_system.state[2]) * dt
nav_system.state[2] += omega * dt
nav_system.state[3] = v
nav_system.state[4] = omega
# 检查是否到达目标
if np.linalg.norm(nav_system.state[:2] - np.array(goal)) < 0.5:
print("到达目标!")
break
# 可视化结果
nav_system.visualize()
if __name__ == "__main__":
main()
2. AR/VR 头显定位系统
#include <iostream>
#include <vector>
#include <array>
#include <deque>
#include <chrono>
#include <cmath>
#include <Eigen/Dense>
// 简化版AR/VR头显定位系统
class HMDTrackingSystem {
private:
// 状态变量
Eigen::Vector3d position; // 位置
Eigen::Quaterniond orientation; // 朝向(四元数)
Eigen::Vector3d velocity; // 速度
Eigen::Vector3d angular_velocity; // 角速度
// 协方差矩阵
Eigen::MatrixXd P;
// 传感器误差
double camera_noise; // 相机定位噪声(米)
double imu_accel_noise; // IMU加速度噪声(m/s^2)
double imu_gyro_noise; // IMU角速度噪声(rad/s)
// IMU偏差
Eigen::Vector3d accel_bias; // 加速度计偏差
Eigen::Vector3d gyro_bias; // 陀螺仪偏差
// 历史数据缓冲
struct TimeStampedPose {
double timestamp;
Eigen::Vector3d position;
Eigen::Quaterniond orientation;
};
std::deque<TimeStampedPose> pose_history;
double pose_history_max_age = 0.5; // 最长保存0.5秒
// 上次更新时间
double last_update_time;
// 是否已初始化
bool initialized = false;
// 跟踪状态
enum class TrackingState {
TRACKING_GOOD, // 跟踪良好
TRACKING_LIMITED, // 跟踪受限(仅IMU)
TRACKING_LOST // 跟踪丢失
};
TrackingState tracking_state = TrackingState::TRACKING_LOST;
// 帧计数和统计
int frame_count = 0;
double total_processing_time = 0.0;
public:
HMDTrackingSystem()
: position(Eigen::Vector3d::Zero()),
orientation(Eigen::Quaterniond::Identity()),
velocity(Eigen::Vector3d::Zero()),
angular_velocity(Eigen::Vector3d::Zero()),
camera_noise(0.01),
imu_accel_noise(0.05),
imu_gyro_noise(0.01),
accel_bias(Eigen::Vector3d::Zero
更多推荐
所有评论(0)