机械臂正逆运动学-----数值解

机械臂正逆运动学-----数值解


机械臂的运动学包括正运动学和逆运动学,其雅可比矩阵代表速度级的正逆运动学,机械臂的逆运动学数值解采用雅可比矩阵在求取速度级的逆运动学,然后采用迭代法求取位置级逆运动学。

建立DH坐标系

源代码:

#DH参数
DH0_armc = np.array([[0, -pi/2, 0, 0.248],
					[0,  pi/2, 0, 0    ],
					[0, -pi/2, 0, 0.305],
					[0,  pi/2, 0, 0    ],
					[0, -pi/2, 0, 0.306],
					[0,  pi/2, 0, 0    ],
					[0,  0,    0, 0.213]])
#关节极限
q_min_armc = np.array([-180, -95, -180, -95, -180, -95, -180])*pi/180
q_max_armc = np.array([180, 95, 180, 95, 180, 95, 180])*pi/180

求正运动学

单关节齐次传递矩阵

源代码:

#单关节传递矩阵
def  trans(theta,alpha,a,d):
	'''
		本函数用于求取n*度机械臂正运动学
		输入参数为DH参数,角度单位为rad,长度单位为mm
		参数分别为theta,alpha,a,d,为0维常数值
		返回齐次传递函数矩阵
	'''
	T = np.array([[math.cos(theta), -math.sin(theta)*math.cos(alpha), math.sin(theta)*math.sin(alpha),  a*math.cos(theta)],
		[math.sin(theta), math.cos(theta)*math.cos(alpha),  -math.cos(theta)*math.sin(alpha), a*math.sin(theta)],
		[0,               math.sin(alpha),                  math.cos(alpha),                  d                ],
		[0,               0,                                0,                                  1              ]])
	return T

正运动学:返回齐次矩阵

源代码:

#返回齐次矩阵的正运动学
def fkine(theta,alpha,a,d):
	'''
		本函数用于求取n*度机械臂正运动学
		输入参数为DH参数,角度单位为rad,长度单位为mm
		参数分别为theta,alpha,a,d,为1维常数值
		返回齐次传递函数矩阵 
	'''
	#关节*度
	n = len(theta)
	#建立4×4的齐次传递矩阵,定义为numpy类型
	An = np.eye(4)
	for i  in range(n):
		T =  trans(theta[i],alpha[i],a[i],d[i])
		An = np.dot(An,T)  #末端到惯性坐标系传递矩阵
	return An

正运动学:返回欧拉角向量

源代码:

#输入初始时刻DH_0和相对转角,输出六维末端位姿
def fkine_euler(DH_0,qr):
	'''
		本函数用于求取n*度机械臂正运动学
		输入参数为DH参数,角度单位为rad,长度单位为mm
		参数分别为theta,alpha,a,d,为1维常数值
		返回齐次传递函数矩阵
	'''
	#DH参数
	theta = DH_0[:, 0] + qr
	alpha = DH_0[:, 1]
	a = DH_0[:, 2]
	d = DH_0[:, 3]
	#关节*度
	n = len(theta)
	xe = np.zeros(6)
	#建立4×4的齐次传递矩阵,定义为numpy类型
	An = np.eye(4)
	for i  in range(n):
		T =  trans(theta[i],alpha[i],a[i],d[i]) #需要加入该函数:单关节齐次矩阵
		An = np.dot(An,T)  #末端到惯性坐标系传递矩阵
	xe[0:3] = An[0:3,3]
	xe[3:6] = rot2euler_zyx(An[0:3,0:3]) #需要加入该函数:欧拉角转换函数

	return xe

旋转矩阵与欧拉角转换:

#旋转矩阵转变为ZYX欧拉角
def rot2euler_zyx(Re):
	'''
		ZYX欧拉角速度变为姿态角速度转化矩阵
		input:旋转矩阵
		output:欧拉角[alpha,beta,gamma]
	'''
	euler_zyx = np.zeros(3)
	if(abs(abs(Re[2, 0]) - 1) < math.pow(10, -6)):
		if(Re[2,0] < 0):
			beta = pi/2
			alpha = np.arctan2(-Re[1,2],Re[1,1])
			gamma = 0
		else:
			beta = -pi/2
			alpha = -np.arctan2(-Re[1, 2], Re[1, 1])
			gamma = 0
	else:
		p_beta = math.asin(-Re[2,0])
		cb = np.cos(p_beta)
		alpha = math.atan2(Re[1,0]*cb,Re[0,0]*cb)
		gamma = math.atan2(Re[2,1]*cb,Re[2,2]*cb)
		if((math.sin(gamma)*Re[2,1]) < 0):
			beta = pi - p_beta
		else:
			beta = p_beta
	euler_zyx[0] = alpha
	euler_zyx[1] = beta
	euler_zyx[2] = gamma
	for i in range(3):
		if(euler_zyx[i]>=3.14 or euler_zyx[i]<=-3.14):
			euler_zyx[i] = 0.0
	return euler_zyx

求雅可比矩阵

源代码:
(1) 便于理解版

#构造法求雅克比矩阵,时间0.3ms
def jacobian(DH_0,qr):
	'''
		本函数用于求取机械臂的雅克比矩阵
		input:DH_0参数,长度单位mm,角度单位red
			  qr,相对初始位置的转角
		output:J,该位置点的雅克比矩阵
	'''
	n = len(qr)
	theta = DH_0[:,0] + qr
	alpha = DH_0[:,1]
	a = DH_0[:,2]
	d = DH_0[:,3]
	
	#求取末端位置
	An = fkine(theta,alpha,a,d)  #正运动学函数
	p_n = An[0:3,3]
	J = np.zeros([6,n])
	J[3:6,n-1] = An[0:3,2]
	
	#求取其余转轴方向及位置点
	Ai = np.eye(4)
	for i in range(n-1):
		z_i = Ai[0:3,2]
		p_i = Ai[0:3,3]
		p_in = p_n - p_i
		J[0:3,i] = np.cross(z_i,p_in)
		J[3:6,i] = z_i
		Ai = np.dot(Ai, trans(theta[i], alpha[i], a[i], d[i]))
	return J

(2) 高速计算版

#运行时间更快0.1ms
def jeco_0(DH_0, qr):
	'''
		本函数基于雅克比迭代求解n*度机械臂逆运动学方程
		input:DH_0 = [q_init,alpha,a,d];
			 q_ready是上一时刻的位置,单位:弧度;
		     T0e为DH坐标系确定的DH{0}坐标系与DH{6}之间的关系(目标矩阵);
		     efs求解误差阀值,默认值10^(-10)
			 i_limit迭代最大次数,默认值1000			  
		output:qq为相对与DH_q0的转动角度,单位:弧度;已处理到[-pi, pi] 之间
	'''
	#建立初时刻迭代初值
	q = DH_0[:,0] + qr
	alpha = DH_0[:,1]
	a = DH_0[:,2]
	d = DH_0[:,3]
	
	#计数及标签
	n = len(q)
						  
	#计算雅克比矩阵
	U = np.eye(4)
	Jn = np.zeros([6,n])
	T = np.zeros([4,4,n])
	for i in range(n):
		i = n - i - 1
		T[:,:,i] = trans(q[i],alpha[i],a[i],d[i])#单关节传递函数
		U = np.dot(T[:,:,i],U)
		dd = np.array([-U[0,0]*U[1,3] + U[1,0]*U[0,3],
						-U[0,1]*U[1,3] + U[1,1]*U[0,3],
						-U[0,2]*U[1,3] + U[1,2]*U[0,3]])
		Jn[0:3,i] = dd
		Jn[3:6,i] = U[2,0:3] 
	
	An = fkine(q,alpha,a,d)		#正运动学函数
	R = An[0:3,0:3]
	J_R = np.zeros([6,6])
	J_R[0:3,0:3] = R
	J_R[3:6,3:6] = R
		
	J0 = np.dot(J_R,Jn)
	return J0

求机械臂逆运动学

源代码:

#***基于雅克比矩阵迭代求解逆运动学***#
def iterate_ikine(DH_0, q_ready, T0e, efs = pow(10,-12), i_max = 1000):
	'''
		本函数基于雅克比迭代求解n*度机械臂逆运动学方程
		input:DH_0 = [q_init,alpha,a,d];
			 q_ready是上一时刻的位置,单位:弧度;
		     T0e为DH坐标系确定的DH{0}坐标系与DH{6}之间的关系(目标矩阵);
		     efs求解误差阀值,默认值10^(-10)
			 i_limit迭代最大次数,默认值1000			  
		output:qq为相对与DH_q0的转动角度,单位:弧度;已处理到[-pi, pi] 之间
	'''
	#建立初时刻迭代初值
	q_r = DH_0[:,0] + q_ready
	alpha = DH_0[:,1]
	a = DH_0[:,2]
	d = DH_0[:,3]
	
	#计数及标签
	n = len(q_r)
	deltaQ = 1  
	temp_count = 0
	
	#迭代循环求解
	while (deltaQ > efs):
		
		#求解正运动学
		An = np.eye(4)
		T = np.zeros([4,4,n])
		
		for i in range(n):
			T[:,:,i] = trans(q_r[i],alpha[i],a[i],d[i])
			An = np.dot(An,T[:,:,i])
			
		#计算末端误差
		dA = np.zeros(6)
		dA[0:3] = T0e[0:3,3] - An[0:3,3]
		dA[3:6] = 0.5*(np.cross(An[0:3,0],T0e[0:3,0]) + np.cross(An[0:3,1],T0e[0:3,1]) 
				+ np.cross(An[0:3,2],T0e[0:3,2]))
	
		#print dA						  
		#计算雅克比矩阵
		U = np.eye(4)
		Jn = np.zeros([6,n])
		for i in range(n):
			i = n - i - 1
			U = np.dot(T[:,:,i],U)
			
			dd = np.array([ -U[0,0]*U[1,3] + U[1,0]*U[0,3],
							-U[0,1]*U[1,3] + U[1,1]*U[0,3],
							-U[0,2]*U[1,3] + U[1,2]*U[0,3]])
			Jn[0:3,i] = dd
			Jn[3:6,i] = U[2,0:3]
			
		R = An[0:3,0:3]
		J_R = np.zeros([6,6])
		J_R[0:3,0:3] = R
		J_R[3:6,3:6] = R
		
		J0 = np.dot(J_R,Jn)
		#求取关节角关节角度偏差值
		dq = np.dot(np.linalg.pinv(J0),dA)
		q_r = q_r + dq
		deltaQ = np.linalg.norm(dq)
		temp_count =temp_count + 1
		if (temp_count > i_max):
			print("Solution wouldn't converge")
			return q_ready

	q_tmp = q_r - DH_0[:,0]		
	q = bf.qq_choose(q_tmp) #选择函数
	return q

关节角选择函数:

#将关节角计算到正负pi
def qq_choose(qq):
	'''
		本函数用于选着关节角范围
		input:qq为计算出的关节角
		output:q关节角范围[-pi,pi]
	'''
	n = len(qq)
	q = np.copy(qq)
	for i in range(n):
		while (q[i] > pi):
			q[i] = q[i] - 2*pi
		while (q[i] < - pi):
			q[i] = q[i] + 2*pi
	return q

合成通用运动学类

源代码:

#==========================通用运动学类======================#
class GeneralKinematic(object):
	'''
	函数依赖math和numpy
	'''
	def __init__(self, DH_0,q_min=rp.q_min, q_max=rp.q_max):
		self.DH_0 = DH_0
		self.theta = DH_0[:, 0]
		self.alpha = DH_0[:, 1]
		self.a = DH_0[:, 2]
		self.d = DH_0[:, 3]
		self.q_min = q_min
		self.q_max = q_max

		self.n = len(self.theta)

	#相邻关节传递矩阵
	def trans(self, theta, alpha, a, d):
		T = np.array([[math.cos(theta), -math.sin(theta) * math.cos(alpha),
					   math.sin(theta) * math.sin(alpha), a * math.cos(theta)],
					  [math.sin(theta), math.cos(theta) * math.cos(alpha),
					   -math.cos(theta) * math.sin(alpha), a * math.sin(theta)],
					  [0, math.sin(alpha), math.cos(alpha), d],
					  [0, 0, 0, 1]])
		return T

	# ZYX欧拉角转变为旋转矩阵
	def euler_zyx2rot(self,phi):
		'''
			ZYX欧拉角转变为旋转矩阵
			input:欧拉角
			output:旋转矩阵
		'''
		R = np.array([[np.cos(phi[0]) * np.cos(phi[1]),np.cos(phi[0]) * np.sin(phi[1]) * np.sin(phi[2]) - np.sin(phi[0]) * np.cos(phi[2]),
					   np.cos(phi[0]) * np.sin(phi[1]) * np.cos(phi[2]) + np.sin(phi[0]) * np.sin(phi[2])],
					  [np.sin(phi[0]) * np.cos(phi[1]),np.sin(phi[0]) * np.sin(phi[1]) * np.sin(phi[2]) + np.cos(phi[0]) * np.cos(phi[2]),
					   np.sin(phi[0]) * np.sin(phi[1]) * np.cos(phi[2]) - np.cos(phi[0]) * np.sin(phi[2])],
					  [-np.sin(phi[0]), np.cos(phi[1]) * np.sin(phi[2]), np.cos(phi[1]) * np.cos(phi[2])]])
		return R

	# 旋转矩阵转变为ZYX欧拉角
	def rot2euler_zyx(self, Re):
		'''
			ZYX欧拉角速度变为姿态角速度转化矩阵
			input:旋转矩阵
			output:欧拉角[alpha,beta,gamma]
		'''
		euler_zyx = np.zeros(3)
		if(abs(abs(Re[2, 0]) - 1) < math.pow(10, -6)):
			if(Re[2,0] < 0):
				beta = pi/2
				alpha = np.arctan2(-Re[1,2],Re[1,1])
				gamma = 0
			else:
				beta = -pi/2
				alpha = -np.arctan2(-Re[1, 2], Re[1, 1])
				gamma = 0
		else:
			p_beta = math.asin(-Re[2,0])
			cb = np.cos(p_beta)
			alpha = math.atan2(Re[1,0]*cb,Re[0,0]*cb)
			gamma = math.atan2(Re[2,1]*cb,Re[2,2]*cb)
			if((math.sin(gamma)*Re[2,1]) < 0):
				beta = pi - p_beta
			else:
				beta = p_beta
		euler_zyx[0] = alpha
		euler_zyx[1] = beta
		euler_zyx[2] = gamma
		for i in range(3):
			if(euler_zyx[i]>=3.14 or euler_zyx[i]<=-3.14):
				euler_zyx[i] = 0.0
		return euler_zyx

	# 将关节角计算到正负pi
	def qq_choose(self, qq):
		'''
			本函数用于选着关节角范围
			input:qq为计算出的关节角
			output:q关节角范围[-pi,pi]
		'''
		q = np.copy(qq)
		for i in range(self.n):
			while (q[i] > math.pi):
				q[i] = q[i] - 2 * math.pi
			while (q[i] < - math.pi):
				q[i] = q[i] + 2 * math.pi
		return q

	#正运动学,返回齐次矩阵
	def fkine(self, qr):
		An = np.eye(4)
		for i in range(self.n):
			T = self.trans(self.theta[i] + qr[i], self.alpha[i], self.a[i], self.d[i])
			An = np.dot(An, T)  # 末端到惯性坐标系传递矩阵
		return An

	#正运动学,输出六维末端位姿,姿态用zyx欧拉角表示
	def fkine_euler(self, qr):
		xe = np.zeros(6)
		An = np.eye(4)
		for i in range(self.n):
			T = self.trans(self.theta[i] + qr[i], self.alpha[i], self.a[i], self.d[i])
			An = np.dot(An, T)  # 末端到惯性坐标系传递矩阵
		xe[0:3] = An[0:3, 3]
		xe[3:6] = self.rot2euler_zyx(An[0:3, 0:3])
		return xe
		
	#求取雅克比矩阵
	def jeco(self, qr):
		# 计算雅克比矩阵
		U = np.eye(4)
		Jn = np.zeros([6, self.n])
		T = np.zeros([4, 4, self.n])
		for i in range(self.n):
			i = self.n - i - 1
			T[:, :, i] = self.trans(self.theta[i] + qr[i], self.alpha[i], self.a[i], self.d[i])
			U = np.dot(T[:, :, i], U)
			dd = np.array([-U[0, 0] * U[1, 3] + U[1, 0] * U[0, 3],
						   -U[0, 1] * U[1, 3] + U[1, 1] * U[0, 3],
						   -U[0, 2] * U[1, 3] + U[1, 2] * U[0, 3]])
			Jn[0:3, i] = dd
			Jn[3:6, i] = U[2, 0:3]

		An = self.fkine(qr)
		R = An[0:3, 0:3]
		J_R = np.zeros([6, 6])
		J_R[0:3, 0:3] = R
		J_R[3:6, 3:6] = R

		J0 = np.dot(J_R, Jn)
		return J0

	# ***基于雅克比矩阵迭代求解逆运动学***#
	def iterate_ikine(self, q_guess, Te, efs=pow(10, -12), i_max=1000):
		'''
			本函数基于雅克比迭代求解n*度机械臂逆运动学方程
				 q_ready是上一时刻的位置,单位:弧度;
			     T0e为DH坐标系确定的DH{0}坐标系与DH{6}之间的关系(目标矩阵);
			     efs求解误差阀值,默认值10^(-10)
				 i_limit迭代最大次数,默认值1000
			output:qq为相对与DH_q0的转动角度,单位:弧度;已处理到[-pi, pi] 之间
		'''
		# 建立初时刻迭代初值
		q_r =self.theta + q_guess

		# 计数及标签
		deltaQ = 1
		temp_count = 0

		# 迭代循环求解
		while (deltaQ > efs):

			# 求解正运动学
			An = np.eye(4)
			T = np.zeros([4, 4, self.n])

			for i in range(self.n):
				T[:, :, i] = self.trans(q_r[i], self.alpha[i], self.a[i], self.d[i])
				An = np.dot(An, T[:, :, i])

			# 计算末端误差
			dA = np.zeros(6)
			dA[0:3] = Te[0:3, 3] - An[0:3, 3]
			dA[3:6] = 0.5 * (np.cross(An[0:3, 0], Te[0:3, 0]) + np.cross(An[0:3, 1], Te[0:3, 1])
							 + np.cross(An[0:3, 2], Te[0:3, 2]))

			# 计算雅克比矩阵
			U = np.eye(4)
			Jn = np.zeros([6, self.n])
			for i in range(self.n):
				i = self.n - i - 1
				U = np.dot(T[:, :, i], U)

				dd = np.array([-U[0, 0] * U[1, 3] + U[1, 0] * U[0, 3],
							   -U[0, 1] * U[1, 3] + U[1, 1] * U[0, 3],
							   -U[0, 2] * U[1, 3] + U[1, 2] * U[0, 3]])
				Jn[0:3, i] = dd
				Jn[3:6, i] = U[2, 0:3]

			R = An[0:3, 0:3]
			J_R = np.zeros([6, 6])
			J_R[0:3, 0:3] = R
			J_R[3:6, 3:6] = R

			J0 = np.dot(J_R, Jn)
			# 求取关节角关节角度偏差值
			dq = np.dot(np.linalg.pinv(J0), dA)
			q_r = q_r + dq
			deltaQ = np.linalg.norm(dq)
			temp_count = temp_count + 1
			if (temp_count > i_max):
				print("Solution wouldn't converge")
				return q_guess

		q_tmp = q_r - self.theta
		q = self.qq_choose(q_tmp)
		return q

	#机械臂关节极限判断,返回值为0或1
	def exceed_joint_limit(self, qq, q_min, q_max):
		'''
			判断关节角是否超出限制
			input:关节角,关节角范围
			outpu:0,未超出,1超出
		'''
		n = len(qq)
		limit = False
		for i in range(n):
			if((qq[i] < q_min[i]) or (qq[i] > q_max[i])):
				print "第", i+1, "关节超出极限:", qq[i]*180/np.pi
				limit = True
				break
		return limit
	#带关节限制
	def iterate_ikine_limit_xyz(self, q_guess, Xe):
		Te = np.eye(4)
		Te[0:3, 0:3] = self.euler_zyx2rot(Xe[3:])
		Te[0:3, 3] = Xe[:3]
		print "Te:", Te
		qr = self.iterate_ikine(q_guess, Te)
		flag = self.exceed_joint_limit(qr ,self.q_min, self.q_max)
		if(flag):
			#print "flag:", flag
			qr = np.copy(q_guess)
		return qr

	# 带关节限制
	def iterate_ikine_limit(self, q_guess, Te):
		qr = self.iterate_ikine(q_guess, Te)
		flag = self.exceed_joint_limit(qr, self.q_min, self.q_max)
		if (flag):
			# print "flag:", flag
			qr = np.copy(q_guess)
		return qr
上一篇:eclipse实现可认证的DH密钥交换协议


下一篇:CGB2111-Day01