2025-05-13 10:28:00 +08:00

181 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# cython: language_level=3
import time
import numpy as np
import atexit
from scipy.spatial.transform import Rotation as R
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parent.parent))
from hardware.dobot_nova5 import dobot_nova5
from tools.yaml_operator import *
from hardware.force_sensor import XjcSensor
import os
import math
class Identifier:
"""
reference: https://blog.csdn.net/qq_34935373/article/details/106208345
"""
def __init__(self,arm:dobot_nova5,sensor:XjcSensor,config_path:str,):
self.arm = arm
self.sensor = sensor
self.config_path = config_path
# solve for xyz: M = F @ A, A=[x, y, z, k1, k2, k3]
self.M = None
self.F = None
# solve for F0F1F2: f = R @ B, B=[0, 0, -g, Fx0, Fy0, Fz0]
self.R = None
self.f = None
self.len_data = 0
def __add_data(self, wrench: np.ndarray, ee_pose: np.ndarray):
force = wrench[:3].copy()
torque = wrench[3:].copy()
# import pdb
# pdb.set_trace()
if self.M is None:
self.M = torque.reshape((3, 1))
f_skew = self.__skew_symmetric(force)
self.F = np.hstack((f_skew, np.eye(3)))
else:
self.M = np.vstack((self.M, torque.reshape((3, 1))))
f_skew = self.__skew_symmetric(force)
self.F = np.vstack((self.F, np.hstack((f_skew, np.eye(3)))))
if self.R is None:
self.R = np.hstack((ee_pose.T, np.eye(3)))
self.f = force.reshape((3, 1))
else:
self.R = np.vstack((self.R, np.hstack((ee_pose.T, np.eye(3)))))
self.f = np.vstack((self.f, force.reshape((3, 1))))
self.len_data += 1
def __solve(self):
A, residuals, rank, s = np.linalg.lstsq(self.F, self.M, rcond=None)
B, residuals, rank, s = np.linalg.lstsq(self.R, self.f, rcond=None)
x, y, z, k1, k2, k3 = A.flatten()
Gx, Gy, Gz, Fx0, Fy0, Fz0 = B.flatten()
mass_center = np.array([x, y, z])
F_0 = np.array([Fx0, Fy0, Fz0])
M_x0 = k1 - Fy0 * z + Fz0 * y
M_y0 = k2 - Fz0 * x + Fx0 * z
M_z0 = k3 - Fx0 * y + Fy0 * x
M_0 = np.array([M_x0, M_y0, M_z0])
gravity_base = np.array([Gx, Gy, Gz])
return mass_center, F_0, M_0, gravity_base
def __read_data_once(self,num):
sum = np.zeros(6)
i = 0
# 读取num次数据取平均值
while i < num:
# data = self.sensor.read()
data = self.sensor.read_data_f32()
if data is not None:
sum += data
i += 1
time.sleep(0.2)
return sum/i
def __skew_symmetric(self, v: np.ndarray):
""" 从3维向量生成对应的3x3反对称矩阵 """
if len(v) != 3:
raise ValueError("输入数组的长度必须为3!")
return np.array([[0, v[2], -v[1]],
[-v[2], 0, v[0]],
[v[1], -v[0], 0]])
def set_position(self,x,y,z,roll,pitch,yaw):
'''
x y z : mm
r p y : deg
'''
pose_command = np.concatenate([[x,y,z], [roll,pitch,yaw]])
cur = self.arm.getAngel() # 接口调用: 获取机器人的关节位置
cur_str = "{" + ",".join(map(str, cur)) + "}"
target_J = self.arm.getInverseKin(poses_list=pose_command,useJointNear=1,JointNear=cur_str)
print(target_J)
self.arm.RunPoint_P_inJoint(target_J)
def identify_param_auto(self,ready_pose,cnt,sample_num = 5):
if cnt < 15:
raise ValueError("数据量太少请重新输入至少要有15组数据")
control_pose = np.array(ready_pose)
desired_cart_pose = control_pose.copy()
# move to the initial position
self.set_position(x=desired_cart_pose[0],y=desired_cart_pose[1],z=desired_cart_pose[2],
roll=desired_cart_pose[3],pitch=desired_cart_pose[4],yaw=desired_cart_pose[5])
# self.sensor.set_zero()
time.sleep(1)
delta = 60/cnt*6
quarter = cnt // 6
for i in range(cnt):
if i % 2 == 0:
control_pose[3]+= delta * np.where((i <= quarter) | (3* quarter< i <= 5*quarter) , 1, -1)
else:
control_pose[4]+= delta * np.where((i <= quarter) | (3* quarter< i <= 5*quarter), -1, 1)
desired_cart_pose = control_pose.copy()
print("desired_cart_pose:",desired_cart_pose)
self.set_position(x=desired_cart_pose[0],
y=desired_cart_pose[1],
z=desired_cart_pose[2],
roll=desired_cart_pose[3],
pitch=desired_cart_pose[4],
yaw=desired_cart_pose[5])
print("move done")
time.sleep(1)
wrench = self.__read_data_once(sample_num)
print(f"wrench:{wrench}")
print("read done")
cur_pose = self.arm.getPose()
b_R_e = R.from_euler('xyz', cur_pose[3:], degrees=False).as_matrix()
self.__add_data(wrench, b_R_e)
print(f"=======添加了第{i+1}组数据=======")
time.sleep(0.5)
mass_center, F_0, M_0, gravity_base = self.__solve()
# output all results
print(f"mass_center_position: {mass_center}")
print(f"force_zero: {F_0}")
print(f"torque_zero: {M_0}")
print(f"gravity_base: {gravity_base}")
dict_data = {
'mass_center_position': mass_center.tolist(),
'force_zero': F_0.tolist(),
'torque_zero': M_0.tolist(),
'gravity_base': gravity_base.tolist()
}
confirmation = input("是否确认更新数据?(y/n): ").strip().lower()
if confirmation == 'y' or confirmation == 'Y' or confirmation == 'yes' or confirmation == 'YES':
# 执行更新操作
if not os.path.exists(self.config_path):
# 如果文件不存在则创建一个新的config.yaml文件
with open(self.config_path, 'w') as file:
pass
update_yaml(self.config_path, dict_data)
print("更新操作已完成。")
else:
print("更新操作已取消。")
if __name__ == '__main__':
arm = dobot_nova5()
arm.setEndEffector(i=9,tool_i="x,y,z,rx,ry,rz")
arm.chooseEndEffector(i=9)
arm.init()
sensor = XjcSensor()
sensor.connect()
sensor.disable_active_transmission()
sensor.disable_active_transmission()
sensor.disable_active_transmission()
atexit.register(sensor.disconnect)
identifier = Identifier(arm=arm,sensor=sensor,config_path="/home/jsfb/jsfb_ws/global_config/massage_head/none_playload.yaml")
ready_pose = [-1,-1,-1,-1,-1,-1]
time.sleep(1)
identifier.identify_param_auto(ready_pose,45)