calibration_tools_v1.0/it.py
2025-02-20 10:45:17 +08:00

202 lines
8.1 KiB
Python

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)