标定实现
机器人工具坐标系标定就是确定工具坐标系相对于末端连杆坐标系的变换矩阵
TCP位置标定
标定步骤
- 控制机械臂移动工具从不同方位触碰空间中某个固定点,记录N组数据();
- 计算获得工具末端点相对机械臂末端点的位置变换;
TCF姿态标定
标定步骤
- 完成位置标定;
- 控制工具末端点分别沿x方向和z方向移动一定距离,工具末端点只在该方向上有移动,其它方向上无位移,同时固定初始姿态保持不变。实际操作上可以设置三个固定点(三个固定点满足上述要求,点2和点3相对点1只有一个方向上的移动),使工具末端点分别触碰这三个点然后记录下机械臂末端位姿;
- 计算获得工具坐标系相对机械臂末端坐标系的姿态变换;
原理分析
末端连杆坐标系{E}到基坐标系{B}的位姿关系为:
可通过运动学正解获得,为末端连杆坐标系{E}相对于基坐标系{B}的旋转矩阵,为末端连杆坐标系{E}原点相对于基坐标系{B}的位置矢量。
工具坐标系{T}到基坐标系{B}的位姿关系为:
TCP位置标定分析
在TCP位置标定过程中,第i个标定点的末端连杆坐标系到基坐标系的变换矩阵,可由机器人正解得到,代入\式(2)可得:
写成分块形式为:
令等式两边第4列对应相等,则得:
因工作坐标系{T}相对末端连杆坐标系{E}的位置关系为固定,因此为确定值。所以,对于所有标定点有如下关系:
写成矩阵形式:
上式的未知量含三个元素,包含个方程,系数矩阵为矩阵。
当时系数矩阵秩一般等于3,为列满秩矩阵,式(4)为不相容方程组,只能求最佳最小二乘解。
非奇异的"方阵"才能叫"可逆矩阵",奇异的"方阵"叫"不可逆矩阵"。
现代应用中为了解决各种线性方程组(系数矩阵是非方阵和方阵为奇异),将逆矩阵的概念推广到"不可逆方阵"和"长方形矩阵"上,从而产生了"广义逆矩阵"的概念
若A为行满秩,则
若A为列满秩,则
为系数矩阵的加号广义逆,因系数矩阵为列满秩,满足,则:
根据求得的可计算标定误差
TCF姿态标定分析
已知TCF状态标定2个标定点的末端连杆坐标系到基坐标系的变换矩阵,可求得工具中心点相对于基坐标系的位置:
- 中心点和x方向偏移点
- 中心点和z方向偏移点
这两点所决定的向量为:
为x方向标定点处工具坐标系在+X方向上的向量,即:
x轴相对于末端连杆坐标系{E}的方向余弦为:
为z方向标定点处工具坐标系在+Z方向上的向量,z轴相对于末端连杆坐标系{E}的方向余弦为:
工具坐标系{T}的Y轴相对于末端连杆坐标系{E}的方向余弦为:
因x方向和z方向为手动移动确定,无法保证两个方向相互垂直,因此需要对其中一个轴(x或者z)进行修正,确保坐标系主矢量的正交性,此处调整z轴:
程序实现
import numpy as np
import pandas as pd
import pysnooper
import math
from scipy.spatial.transform import Rotation as R
class tool_cal():
def __init__(self):
"""
load data from csv
tool_points(0~5) : use robot effectors to touch the same points in the world
and record the pos
tool_poses_tran(0~3):count tanslation
tool_poses_rot(3~5):count rotation
"""
with open("tool_data.csv") as file:
tool_poses = pd.read_csv(file, header = None)
tool_poses = np.array(tool_poses)
# cal translation
self.tran_tran=[]
self.tran_rotm=[]
tool_poses_tran = tool_poses[0:4,:]
for pose in tool_poses_tran:
# set translation
self.tran_tran.append(np.array([[pose[0]],[pose[1]],[pose[2]]]))
# set rotation
r = R.from_euler('xyz', np.array([pose[3], pose[4], pose[5]]), degrees=True)
self.tran_rotm.append(r.as_matrix())
tool_tran=self.cal_tran()
# cal rotation
self.rot_tran=[]
self.rot_rotm=[]
tool_poses_rot = tool_poses[3:6,:]
for pose in tool_poses_rot:
# set translation
self.rot_tran.append(np.array([[pose[0]],[pose[1]],[pose[2]]]))
# set rotation
r = R.from_euler('xyz', np.array([pose[3], pose[4], pose[5]]), degrees=True)
self.rot_rotm.append(r.as_matrix())
tool_rot=self.cal_rotm(tool_tran)
# get transformation
tool_T = np.array(np.zeros((4,4)))
tool_T[0:3,0:3] = tool_rot
tool_T[0:3,3:] = tool_tran
tool_T[3:,:] = [0,0,0,1]
print(tool_T)
# change into quat
q = R.from_matrix(tool_rot)
print(q.as_quat())
def cal_tran(self):
# 公式五,拆分后计算
# tran_data=[]
# rotm_data=[]
# for i in range(len(self.tran_tran)-1):
# tran_data.append(self.tran_tran[i+1] - self.tran_tran[i])
# rotm_data.append(self.tran_rotm[i] - self.tran_rotm[i+1])
# L = np.array(np.zeros((3,3)))
# R = np.array(np.zeros((3,1)))
# for i in range(len(tran_data)):
# L = L + np.dot(rotm_data[i],rotm_data[i])
# R = R + np.dot(rotm_data[i],tran_data[i])
# print(np.linalg.inv(L).dot(R))
# tool_tran = np.linalg.inv(L).dot(R)
# 构建Ax=B
A = self.tran_rotm[0] - self.tran_rotm[1]
B = self.tran_tran[1] - self.tran_tran[0]
for i in range(1,len(self.tran_tran)-1):
A = np.vstack((A,self.tran_rotm[i] - self.tran_rotm[i+1]))
B = np.vstack((B,self.tran_tran[i+1] - self.tran_tran[i]))
# 广义逆
tool_tran = np.linalg.pinv(A).dot(B)
# svd
u,s,v = np.linalg.svd(A,0)
c = np.dot(u.T,B)
w = np.linalg.solve(np.diag(s),c)
tool_tran = np.dot(v.T,w)
return tool_tran
def cal_rotm(self, tran):
# centre
P_otcp_To_B = np.dot(self.rot_rotm[0],tran)+self.rot_tran[0]
# cal the dircction vector of x
P_xtcp_To_B = np.dot(self.rot_rotm[1],tran)+self.rot_tran[1]
vector_X = P_xtcp_To_B - P_otcp_To_B
dire_vec_x_o = np.linalg.inv(self.rot_rotm[0]).dot(vector_X) / np.linalg.norm(vector_X)
# cal the dircction vector of z
P_ztcp_To_B = np.dot(self.rot_rotm[2],tran)+self.rot_tran[2]
vector_Z = P_ztcp_To_B - P_otcp_To_B
dire_vec_z_o = np.linalg.inv(self.rot_rotm[0]).dot(vector_Z) / np.linalg.norm(vector_Z)
# cal the dircction vector of y
dire_vec_y_o = np.cross(dire_vec_z_o.T,dire_vec_x_o.T)
# modify the dircction vector of z
dire_vec_z_o = np.cross(dire_vec_x_o.T,dire_vec_y_o)
# cal rotation matrix
tool_rot = np.array(np.zeros((3,3)))
tool_rot[:,0] = dire_vec_x_o.T
tool_rot[:,1] = dire_vec_y_o
tool_rot[:,2] = dire_vec_z_o
return tool_rot
if __name__ == "__main__":
tool_cal()