cmvr-es/python/hand_eye_calibration/compute_in_hand.py

140 lines
4.5 KiB
Python
Raw Normal View History

2025-08-21 13:56:39 +08:00
# coding=utf-8
"""
眼在手上使用采集的图片信息和机械臂位姿信息计算
相机坐标系相对于机械臂末端坐标系的旋转矩阵和平移向量
"""
import os
import logging
import yaml
import cv2
import numpy as np
from scipy.spatial.transform import Rotation as R
np.set_printoptions(precision=8, suppress=True)
current_path = os.path.dirname(os.path.abspath(__file__))
images_path = os.path.join(current_path, "data/20250804-1331")
# 加载标定板参数
with open("./config/config.yaml", 'r', encoding='utf-8') as file:
data = yaml.safe_load(file)
XX = data.get("checkerboard_args").get("XX")
YY = data.get("checkerboard_args").get("YY")
L = data.get("checkerboard_args").get("L")
def gen_T_txt():
joint_record_file = os.path.join(images_path, "joints_record.txt")
with open(joint_record_file, "r") as f:
lines = f.readlines()
for line in lines:
temp = line.replace("\n", "").split(" ")
index = temp[0]
joints = temp[2:]
cmd = " ".join(["fk_example", "right"] + joints) + f" -o ./temp/T_ee_{index}.txt"
print(cmd)
os.system(cmd)
print("gen_T_txt finished")
def load_robot_poses_from_fk(folder):
"""
从fk_example输出的多个T_ee_{index}.txt文件构建RobotToolPose矩阵
输出: 3x(4*N) numpy矩阵
"""
files = [f for f in os.listdir(folder) if f.startswith("T_ee_") and f.endswith(".txt")]
if not files:
raise RuntimeError("未找到任何 T_ee_xxx.txt 文件")
# 根据index排序
files.sort(key=lambda x: int(x.split('_')[-1].split('.')[0]))
poses = []
for fname in files:
path = os.path.join(folder, fname)
data = np.loadtxt(path, delimiter=',')
if data.shape != (12,):
raise RuntimeError(f"{fname} 格式错误期望12个值(r11,r12,r13,tx,...,tz)")
# 3x4矩阵
mat = data.reshape(3, 4)
poses.append(mat)
# 拼接成 3x(4*N)
robot_pose_matrix = np.hstack(poses)
out_path = os.path.join(folder, "../RobotToolPose.csv")
np.savetxt(out_path, robot_pose_matrix, delimiter=',', fmt='%.8f')
print(f"RobotToolPose.csv 已生成: {out_path}")
return out_path
def func():
path = os.path.dirname(__file__)
# 设置寻找亚像素角点的参数
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
# 获取标定板角点的位置
objp = np.zeros((XX * YY, 3), np.float32)
objp[:, :2] = np.mgrid[0:XX, 0:YY].T.reshape(-1, 2)
objp = L * objp
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images_num = [f for f in os.listdir(images_path) if f.endswith('.png')]
for i in range(1, len(images_num) + 1):
image_file = os.path.join(images_path, f"img_{i}.png")
if os.path.exists(image_file):
print(f'{image_file}')
img = cv2.imread(image_file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (XX, YY), None)
if ret:
cv2.drawChessboardCorners(img, (XX, YY), corners, ret)
# 显示图像窗口
save_path = os.path.join(current_path, "temp", f"res_{i}.jpg")
cv2.imwrite(save_path, img)
obj_points.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)
img_points.append(corners2 if corners2 is not None else corners)
N = len(img_points)
# 标定得到图案在相机坐标系下的位姿
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
print("-----------------------------------------------------")
# === 新增读取fk_example结果构建RobotToolPose ===
csv_file = load_robot_poses_from_fk(os.path.join(path, "temp"))
tool_pose = np.loadtxt(csv_file, delimiter=',')
R_tool = []
t_tool = []
for i in range(int(N)):
R_tool.append(tool_pose[0:3, 4*i:4*i+3])
t_tool.append(tool_pose[0:3, 4*i+3])
R_, t_ = cv2.calibrateHandEye(R_tool, t_tool, rvecs, tvecs, cv2.CALIB_HAND_EYE_TSAI)
return R_, t_
if __name__ == '__main__':
gen_T_txt()
rotation_matrix, translation_vector = func()
rotation = R.from_matrix(rotation_matrix)
quaternion = rotation.as_quat()
print(f"旋转矩阵是:\n {rotation_matrix}")
print(f"平移向量是:\n {translation_vector}")
print(f"四元数是:\n {quaternion}")