import numpy as np from scipy.spatial.transform import Rotation as R from scipy.linalg import svd import os import utils import config def rotation_matrix_to_rpy(R): # 确保输入是一个有效的旋转矩阵 if not np.allclose(np.dot(R, R.T), np.eye(3)) or not np.isclose(np.linalg.det(R), 1.0): raise ValueError("输入不是一个有效的旋转矩阵") _pitch = -np.arcsin(R[2, 0]) if np.abs(_pitch - np.pi / 2) < 1e-6: # 处理接近90度的情况 _yaw = 0 _roll = np.arctan2(R[0, 1], R[0, 2]) elif np.abs(_pitch + np.pi / 2) < 1e-6: # 处理接近-90度的情况 _yaw = 0 _roll = -np.arctan2(R[0, 1], R[0, 2]) else: _yaw = np.arctan2(R[1, 0], R[0, 0]) _roll = np.arctan2(R[2, 1], R[2, 2]) return _roll, _pitch, _yaw def average_rotation_matrices(rotation_matrices, weights=None): # 将旋转矩阵转换为四元数 quaternions = [R.from_matrix(matrix).as_quat() for matrix in rotation_matrices] if weights is None: # 如果没有提供权重,则使用等权重 weights = np.ones(len(quaternions)) / len(quaternions) else: # 确保权重之和为1 weights = np.array(weights) / np.sum(weights) # 对四元数进行加权平均 avg_quaternion = np.zeros(4) for quat, weight in zip(quaternions, weights): avg_quaternion += weight * quat # 归一化四元数以确保它是单位四元数 avg_quaternion /= np.linalg.norm(avg_quaternion) # 将平均后的四元数转换回旋转矩阵 avg_rotation_matrix = R.from_quat(avg_quaternion).as_matrix() return avg_rotation_matrix def addQ(q1, q2): return np.quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w) def calc_RT(lidar, camera, MAX_ITERS, lidarToCamera): global iteration_counter, pkg_loc, translation_sum, rotation_sum, rotation_avg_by_mult, rmse_avg if iteration_counter == 0: with open(pkg_loc + "/log/avg_values.txt", "w") as clean_file: pass translation_sum = np.zeros((3, 1), dtype=float) rotation_sum = np.ones(4) rotation_avg_by_mult = np.eye(3, dtype=float) rmse_avg = np.ones(3) num_points = lidar.shape[1] print("Number of points:", num_points) mu_lidar = np.mean(lidar, axis=0) mu_camera = np.mean(camera, axis=0) if iteration_counter == 0: print("mu_lidar:\n", mu_lidar) print("mu_camera:\n", mu_camera) # lidar_centered = lidar - mu_lidar[:, np.newaxis] # camera_centered = camera - mu_camera[:, np.newaxis] lidar_centered = lidar - mu_lidar camera_centered = camera - mu_camera if iteration_counter == 0: print("lidar_centered:\n", lidar_centered) print("camera_centered:\n", camera_centered) # cov = camera_centered @ lidar_centered.T ERROR H = np.dot(lidar_centered.T, camera_centered) print(H) U, _, Vt = np.linalg.svd(H) # rotation = U @ Vt ERROR rotation = np.dot(Vt, U.T) if np.linalg.det(rotation) < 0: # rotation = U @ np.diag([1, 1, -1]) @ Vt rotation = np.dot(Vt, U.T) # translation = mu_camera - rotation @ mu_lidar translation = mu_camera.reshape(3, 1) - np.dot(rotation, mu_lidar.reshape(3, 1)) # averaging translation and rotation translation_sum += translation temp_q = R.from_matrix(rotation).as_quat() if iteration_counter == 0: rotation_sum = temp_q else: rotation_sum = rotation_sum + temp_q # averaging rotations by multiplication if iteration_counter == 0: rotation_avg_by_mult = rotation else: rotation_avg_by_mult = average_rotation_matrices([rotation_avg_by_mult, rotation]) # ea = R.from_matrix(rotation).as_euler('zyx') ea = R.from_matrix(rotation).as_euler('xyz', degrees=False) combine_ea = utils.combine_rpy(config.roll, config.pitch, config.yaw, ea[0], ea[1], ea[2]) print("Rotation matrix:\n", rotation) print("Rotation in Euler :\n", ea) print("Combin Rotation in Euler :\n", combine_ea) print("Translation:\n", translation) # eltwise_error = np.sum((camera - (rotation @ lidar + translation[:, np.newaxis])) ** 2, axis=0) ERROR eltwise_error = np.sum((camera - (np.dot(lidar, rotation) + translation[:, np.newaxis])) ** 2, axis=0) error = np.sqrt(np.mean(eltwise_error)) print("RMSE:", error) rmse_avg += error T = np.eye(4) T[:3, :3] = rotation T[:3, 3] = np.squeeze(translation) print("Rigid-body transformation:\n", T) iteration_counter += 1 if iteration_counter % 1 == 0: with open(pkg_loc + "/log/avg_values.txt", "a") as log_avg_values: print("--------------------------------------------------------------------") print(f"After {iteration_counter} iterations") print("--------------------------------------------------------------------") print("Average translation is:\n", translation_sum / iteration_counter) log_avg_values.write(f"{iteration_counter}\n") log_avg_values.write(str(translation_sum / iteration_counter) + "\n") # rotation_sum_normalized = np.array([getattr(rotation_sum, dim) for dim in ['x', 'y', 'z', 'w']]) / iteration_counter rotation_sum_normalized = rotation_sum / iteration_counter mag = np.linalg.norm(rotation_sum_normalized) rotation_sum_normalized /= mag rotation_avg = R.from_quat(rotation_sum_normalized).as_matrix() print("Average rotation is:\n", rotation_avg) # final_rotation = rotation_avg @ lidarToCamera ERROR final_rotation = np.dot(lidarToCamera, rotation_avg) final_angles = R.from_matrix(final_rotation).as_euler('xyz') combine_final_angles = utils.combine_rpy(config.roll, config.pitch, config.yaw, \ final_angles[0], final_angles[1], final_angles[2]) log_avg_values.write("{:.8f} {:.8f} {:.8f}\n".format(*rotation_avg[0])) log_avg_values.write("{:.8f} {:.8f} {:.8f}\n".format(*rotation_avg[1])) log_avg_values.write("{:.8f} {:.8f} {:.8f}\n".format(*rotation_avg[2])) T_avg = np.eye(4) T_avg[:3, :3] = rotation_avg T_avg[:3, 3] = np.squeeze(translation_sum / iteration_counter) print("Average transformation is:\n", T_avg) print("Final rotation is:\n", final_rotation) print("Final ypr is:\n", final_angles) print("Final Combine ypr is:\n", combine_final_angles) print(f"Average RMSE is: {rmse_avg / iteration_counter}") # eltwise_error_temp = np.sum((camera - (rotation_avg @ lidar + (translation_sum / iteration_counter)[:, np.newaxis])) ** 2, axis=0) eltwise_error_temp = np.sum((camera - (np.dot(lidar, rotation_avg) + (translation_sum / iteration_counter)[:, np.newaxis])) ** 2, axis=0) error_temp = np.sqrt(np.mean(eltwise_error_temp)) print(f"RMSE on average transformation is: {error_temp}") log_avg_values.write("{:.8f}\n".format(error_temp)) return T # Global variables initialization iteration_counter = 0 pkg_loc = "./record" translation_sum = np.zeros((3, 1), dtype=float) rotation_sum = np.ones(4) rotation_avg_by_mult = np.eye(3, dtype=float) rmse_avg = np.ones(3) if __name__ == "__main__": all_points = utils.read_from_json(os.path.dirname(__file__) + '\\record\\points2.json') # records = utils.read_from_json(os.path.dirname(__file__) + '\\record\\record.json') carmera = np.asarray(all_points[0]) lidar = np.asarray(all_points[1]) lidarToCamera = R.from_euler('xyz', [config.roll, config.pitch, config.yaw], degrees=False).as_matrix() calc_RT(lidar, carmera, 50, lidarToCamera)