140 lines
4.5 KiB
Python
140 lines
4.5 KiB
Python
# 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}") |