import os import sys current_file_path = os.path.abspath(__file__) dobot_nova5_path = os.path.dirname(os.path.dirname(os.path.dirname(current_file_path))) print(dobot_nova5_path) # 添加目标文件夹到系统路径 sys.path.append(dobot_nova5_path) try: from dobot_api import DobotApiFeedBack,DobotApiDashboard except: from hardware.dobot_api import DobotApiFeedBack,DobotApiDashboard try: from .dobot_message import * except: from dobot_message import * import threading from time import sleep,time import re from tools.log import CustomLogger import copy import numpy as np from scipy.spatial.transform import Rotation as R import math import time import requests import atexit ''' MODE DESCRIPTION CN 1 INIT 初始化 2 BREAK_OPEN 抱闸松开 3 POWER_OFF 本体下电 4 DISABLED 未使能(抱闸未松) 5 ENABLE 使能(空闲) 6 BACKDRIVE 拖拽 7 RUNNING 运行状态(脚本/TCP) 8 SINGLE_MOVE 单次运动(点动) 9 ERROR 错误状态 10 PAUSE 暂停状态 11 COLLISION 碰撞状态 MODE RANK 报错 I 下电 II 碰撞 III 开抱闸 IV ''' class dobot_nova5: def __init__(self,arm_ip = '192.168.5.1',dashboardPort = 29999,feedFourPort = 30004): self.ip = arm_ip ## 机械臂IP地址 self.dashboardPort = dashboardPort ## 一发一收-设置-控制及运动端口号 self.feedFourPort = feedFourPort ## 第四实时反馈(8ms)反馈数据端口号 self.dashboard = None ## 初始化控制及运动端口实例 self.feedFour = None ## 初始化数据反馈端口实例 self.feedInfo = [] self.__globalLockValue = threading.Lock() # 多线程安全 class feedbackItem: def __init__(self): self.robotMode = -1 self.robotCurrentCommandID = 0 self.MessageSize = -1 # self.DigitalInputs = -1 # self.DigitalOutputs = -1 self.EnableState = False self.QActual = -1 self.ActualQuaternion = -1 self.ToolVectorActual = -1 # ... 自定义反馈数据 ''' self.UserValue = -1 self.ToolValue = -1 ''' self.feedbackData = feedbackItem() # 自定义反馈数据结构体 self.logger = CustomLogger('Dobot_nova5') # try: # self.dashboard = DobotApiDashboard(self.ip, self.dashboardPort) # if not self.dashboard.connect(): # print(f"[ERROR] 无法连接 Dashboard {self.ip}:{self.dashboardPort}") # self.dashboard = None # else: # print("[INFO] Dashboard 连接成功") # except Exception as e: # print(f"[ERROR] Dashboard 初始化失败: {e}") # self.dashboard = None # 尝试连接 Dashboard 控制接口(默认 29999 端口) try: self.dashboard = DobotApiDashboard(self.ip, self.dashboardPort) if self.dashboard.is_connected(): print("[INFO] Dashboard 连接成功") else: print("[ERROR] Dashboard 未连接成功") self.dashboard = None except Exception as e: print(f"[ERROR] Dashboard 连接失败: {e}") self.dashboard = None # 尝试连接 Feedback 接口(默认 30003 端口) try: self.feedFour = DobotApiFeedBack(self.ip, self.feedFourPort) if self.feedFour.is_connected(): print("[INFO] Feedback 连接成功") else: print("[ERROR] Feedback 未连接成功") self.feedFour = None except Exception as e: print(f"[ERROR] Feedback 连接失败: {e}") self.feedFour = None # self.dashboard = DobotApiDashboard(self.ip,self.dashboardPort) # self.feedFour = DobotApiFeedBack(self.ip,self.feedFourPort) self.stop_feedback = threading.Event() self.stop_feedback.clear() # 状态反馈线程 self.feedback_thread = threading.Thread( target=self.GetFeedback # 机器状态反馈线程 ) self.feedback_thread.daemon = True self.feedback_thread.start() time.sleep(1) self.dashboard.RequestControl() max_retry_num = 5 #尝试连接次数 cnt_num = 0 while cnt_num < max_retry_num: if self.feedbackData.robotMode != -1: self.dashboard.EmergencyStop(mode=0) # 松开急停 self.clearError() # 清除报警 # stopId = self.dashboard.Stop() time.sleep(0.5) print("self.self.feedbackData.robotMode:",self.feedbackData.robotMode) if (self.feedbackData.robotMode == 3 or self.feedbackData.robotMode == 4): #and self.parseResultId(stopId)[0] == 0: # self.dashboard.RequestControl() if self.feedbackData.robotMode == 3: self.dashboard.PowerOn() # 复位 # self.dashboard.Stop() # 停止 if self.start_up(): # 启动 self.logger.log_info(f"连接机械臂成功") break else: self.logger.log_error(f"启动失败,尝试第{cnt_num+1}次连接") cnt_num += 1 time.sleep(1) else: if self.feedbackData.robotMode == 9:# and self.parseResultId(stopId)[0] == -3: print(robot_mode_types.get(self.feedbackData.robotMode, "未知模式")) requests.post( "http://127.0.0.1:5000/on_message", data={"message": "机械臂处于锁定状态
请解除锁定后再使用"} ) instruction = { "is_massaging": False, "massage_service_started": False } requests.post( "http://127.0.0.1:5000/update_massage_status", data=instruction ) sys.exit(0) self.disableRobot() self.clearError() # 清除报警 cnt_num += 1 time.sleep(1) else: self.logger.log_error(f"连接机械臂失败,尝试第{cnt_num+1}次连接") cnt_num += 1 time.sleep(1) atexit.register(self.exit_task) self.init_pos = [-45.0009079*np.pi/180,55.5785141*np.pi/180,-120.68821716*np.pi/180,5.11103201*np.pi/180,90.00195312*np.pi/180,-90.00085449*np.pi/180] # 为状态管理而封装的初始化函数 def init(self): self.is_exit = False self.traj_list = None self.last_pose_command = np.zeros(6) self.last_input_command = None self.tcp_offset = None self.filter_matirx = np.zeros((1,7)) # 位姿伺服用 self.filter_matrix = np.zeros((1,6)) # 角度伺服用 sleep(1) self.is_standby = False code = self.move_joint(self.init_pos) if code == 0: if self.parseResultId(self.dashboard.SetCollisionLevel(level=1))[0] == 0: self.logger.log_info("机械臂初始化") else: time.sleep(0.2) max_retries = 5 retry_count = 0 while retry_count < max_retries: if self.parseResultId(self.dashboard.SetCollisionLevel(level=1))[0] == 0: print("retry_count",retry_count) break else: time.sleep(0.1) if retry_count == max_retries: self.logger.log_error("机械臂初始化失败") requests.post( "http://127.0.0.1:5000/on_message", data={"message": "机械臂初始化失败"} ) self.is_exit = True sys.exit(0) else: self.logger.log_error("机械臂初始化失败") requests.post( "http://127.0.0.1:5000/on_message", data={"message": "机械臂初始化失败"} ) self.is_exit = True sys.exit(0) self.last_print_time=0 self.last_reocrd_time =0 def exit_task(self): self.logger.log_yellow("退出任务") # self.disable() instruction = { "is_massaging": False, "massage_service_started": False } requests.post( "http://127.0.0.1:5000/update_massage_status", data=instruction ) time.sleep(1) # self.move_joint(self.off_pos) def parseResultId(self,valueRecv): # 解析返回值 if "Not Tcp" in valueRecv: print("Control Mode is not TCP") print("机械臂不在二次开发模式下") return [1] return [int(num) for num in re.findall(r'-?\d+',valueRecv)] or [2] def GetFeedback(self): while self.stop_feedback.is_set() == False: feedInfo = self.feedFour.feedBackData() with self.__globalLockValue: if feedInfo is not None: if hex((feedInfo['TestValue'][0]))=='0x123456789abcdef': ## DOBOT官方的数据魔数验证 # 基础字段 self.feedbackData.MessageSize = feedInfo['len'][0] self.feedbackData.robotMode = feedInfo['RobotMode'][0] # self.feedbackData.DigitalInputs = feedInfo['DigitalInputs'][0] # self.feedbackData.DigitalOutputs = feedInfo['DigitalOutputs'][0] self.feedbackData.EnableState = feedInfo['EnableStatus'][0] self.feedbackData.robotCurrentCommandID = feedInfo['CurrentCommandId'][0] self.feedbackData.QActual = feedInfo['QActual'][0] self.feedbackData.ActualQuaternion = feedInfo['ActualQuaternion'][0] self.feedbackData.ToolVectorActual = feedInfo['ToolVectorActual'][0] # 自定义添加所需反馈数据 ''' self.feedData.DigitalOutputs = int(feedInfo['DigitalOutputs'][0]) self.feedData.RobotMode = int(feedInfo['RobotMode'][0]) self.feedData.TimeStamp = int(feedInfo['TimeStamp'][0]) self.feedbackData.UserValue = feedInfo['UserValue[6]'][0] self.feedbackData.ToolValue = feedInfo['ToolValue[6]'][0] ''' def start_up(self): max_try = 3 # 尝试次数 cnt = 0 # 尝试次数计数 while cnt < max_try: cnt += 1 # 关闭碰撞检测 # if self.parseResultId(self.dashboard.SetCollisionLevel(level=0))[0] == 0: time.sleep(0.1) self.logger.log_info("关闭碰撞检测成功") code = self.parseResultId(self.dashboard.EnableRobot())[0] if code == 0: while 1: if self.feedbackData.robotMode in [3, 6, 7, 8, 9, 10, 11]: self.logger.log_error(f"使能失败,机械臂异常:{self.feedbackData.robotMode}") return False elif self.feedbackData.robotMode == 5: self.logger.log_info("机械臂使能成功") return True time.sleep(0.1) # def disable_servo(self): # pass def enable_servo(self): pass def waitArrival(self, command): sendCommandID = self.parseResultId(command)[1] while True: if self.feedbackData.robotMode in [3, 4, 6, 8, 9, 10, 11]: self.logger.log_error(f"机械臂异常:{self.feedbackData.robotMode}") print(robot_mode_types.get(self.feedbackData.robotMode, "未知模式")) self.dashboard.Stop() self.dashboard.EmergencyStop(mode=1) return -1 if self.feedbackData.EnableState: if self.feedbackData.robotCurrentCommandID > sendCommandID: break else: isFinsh = (self.feedbackData.robotMode == 5) if self.feedbackData.robotCurrentCommandID == sendCommandID and isFinsh: break sleep(0.1) return 0 def move_joint(self,q,**params): ''' RUNPOINT_P 以关节运动方式运动到目标点 MovJ flag = 1 通过关节角度走点,输入为[J1 J2 J3 J4 J5 J6] unit: rad ''' # 单位 rad -> deg max_retry_count = params.get('max_retry_count', 3) cnt = 0 joints_list_deg = self.__transform_rad_2_deg(q) while cnt < max_retry_count: cnt += 1 command = self.dashboard.MovJ(*joints_list_deg,1) is_arrived = self.waitArrival(command) if is_arrived == 0: print(f'Arrived at the joint position: {q}') return 0 else: print(f'Failed to arrive at the joint position: {q}') return -1 print(f'After {cnt} retries, still failed to arrive at the joint position: {q}') return -1 def ServoJoint(self,joints_list): ''' 关节角度伺服 输入为[J1 J2 J3 J4 J5 J6] unit: deg ''' tcp_command = f"servoj({joints_list[0]},{joints_list[1]},{joints_list[2]},{joints_list[3]},{joints_list[4]},{joints_list[5]}, t=0.01, aheadtime=20, gain=200)" self.dashboard.send_data(tcp_command) # 通过反馈状态来进行异常处理,故这里只需要发,不需要收 print("ServoJ:",tcp_command) if self.feedbackData.robotMode == 9 or self.feedbackData.robotMode == 11: self.dashboard.Stop() self.dashboard.EmergencyStop(mode=1) print("当前工作停止,机械臂处于急停状态") return -1 if self.feedbackData.robotMode == 10: # 10 当前工程暂停 print("机械臂为暂停状态") res = self.dashboard.Continue() code = self.parseResultId(res)[0] if code == 0: print("机械臂继续已暂停的运动指令") return 0 def ServoJoint_RAD(self,joints_list): ''' 关节角度伺服 输入为[J1 J2 J3 J4 J5 J6] unit: rad ''' # 单位转换 rad -> deg joints_list_deg = self.__transform_rad_2_deg(joints_list) # 构造字符串 tcp_command = f"servoj({joints_list_deg[0]},{joints_list_deg[1]},{joints_list_deg[2]},{joints_list_deg[3]},{joints_list_deg[4]},{joints_list_deg[5]}, t=0.01, aheadtime=20, gain=200)" self.dashboard.send_data(tcp_command) # 通过反馈状态来进行异常处理,故这里只需要发,不需要收 print("ServoJ:",tcp_command) if self.feedbackData.robotMode == 9 or self.feedbackData.robotMode == 11: self.dashboard.Stop() self.dashboard.EmergencyStop(mode=1) print("当前工作停止,机械臂处于急停状态") return -1 if self.feedbackData.robotMode == 10: # 10 当前工程暂停 print("机械臂为暂停状态") res = self.dashboard.Continue() code = self.parseResultId(res)[0] if code == 0: print("机械臂继续已暂停的运动指令") return 0 def ServoPose(self,poses_list): ''' 位姿伺服 输入为[X Y Z RX RY RZ] unit: mm/deg ''' poses_list = list(map(float, poses_list)) tcp_command = f"ServoP({poses_list[0]},{poses_list[1]},{poses_list[2]},{poses_list[3]},{poses_list[4]},{poses_list[5]}, t=0.01, aheadtime=20, gain=300)" # res = self.dashboard.sendRecvMsg(tcp_command) # 通过反馈状态来进行异常处理,故这里只需要发,不需要收 self.dashboard.send_data(tcp_command) # print("ServoP msg:",res) if self.feedbackData.robotMode == 9 or self.feedbackData.robotMode == 11: self.dashboard.Stop() self.dashboard.EmergencyStop(mode=1) print("当前工作停止,机械臂处于急停状态") if self.feedbackData.robotMode == 10: # 10 当前工程暂停 print("机械臂为暂停状态") res = self.dashboard.Continue() code = self.parseResultId(res)[0] if code == 0: print("机械臂继续已暂停的运动指令") def ServoPose_M_RAD(self,poses_list): ''' 位姿伺服 输入为[X Y Z RX RY RZ] unit: M/RAD ''' # 单位 m/rad -> mm/deg poses_list_mm_deg = self.__transform_m_rad_2_mm_deg(poses_list) # 确保数据类型正确 poses_list_mm_deg = list(map(float, poses_list_mm_deg)) # 构造字符串 tcp_command = f"ServoP({poses_list[0]},{poses_list[1]},{poses_list[2]},{poses_list[3]},{poses_list[4]},{poses_list[5]}, t=0.01, aheadtime=20, gain=200)" res = self.dashboard.sendRecvMsg(tcp_command) # 通过反馈状态来进行异常处理,故这里只需要发,不需要收 print("ServoP msg:",res) if self.feedbackData.robotMode == 9 or self.feedbackData.robotMode == 11: self.dashboard.Stop() self.dashboard.EmergencyStop(mode=1) print("当前工作停止,机械臂处于急停状态") if self.feedbackData.robotMode == 10: # 10 当前工程暂停 print("机械臂为暂停状态") res = self.dashboard.Continue() code = self.parseResultId(res)[0] if code == 0: print("机械臂继续已暂停的运动指令") def setEndEffector(self,i,tool_i): ''' 设置工具坐标系在索引i. i!=0 tool_i 需要以字符串形式输入 "{x,y,z,rx,ry,rz}" ''' if i == 0: print("不可修改工具0(初始法兰坐标系)") return if i not in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}: print("工具坐标系序号超出范围") return res = self.dashboard.SetTool(i,tool_i) print(res) code = self.parseResultId(res)[0] if code == 0: print(f"设置工具坐标系{i}的偏移量成功") else: print(f"设置工具坐标系{i}失败") return def chooseEndEffector(self,i): ''' 切换工具坐标系, 0~9 ''' if i not in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}: print("工具坐标系序号超出范围") return res = self.dashboard.Tool(i) print(res) code = self.parseResultId(res)[0] if code == 0: print(f"切换为工具坐标系{i}成功") else: print("切换工具坐标系失败") return def setBaseFrame(self,i,base_i): ''' 设置工具坐标系在索引i. i!=0 base_i 修改后的用户坐标系(非偏移量!) 需要以字符串形式输入 "{x,y,z,rx,ry,rz}" ''' if i == 0: print("不可修改用户坐标系0(初始基坐标系)") return if i not in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}: print("用户坐标系序号超出范围") return res = self.dashboard.SetUser(i,base_i) code = self.parseResultId(res)[0] if code == 0: print(f"设置用户坐标系{i}成功") else: print(f"设置用户坐标系{i}失败") return def chooseBaseFrame(self,i): ''' 切换用户基坐标系, 0~9 ''' if i not in {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}: print("工具坐标系序号超出范围") return res = self.dashboard.User(i) code = self.parseResultId(res)[0] if code == 0: print(f"切换为用户基坐标系{i}成功") else: print("切换用户基坐标系失败") return def getPose(self,user=-1,tool=-1): if (user == -1) != (tool == -1): ## XOR 必须同时设置或者同时不设置 print("必须同时设置或同时不设置坐标系参数") res = self.dashboard.GetPose(user,tool) # 返回字符串 print("get pose",res) code = self.parseResultId(res)[0] if code == 0: pass # print("success") else: print("获取位姿失败") return # 处理字符串 start = res.find("{")+1 end = res.find("}") pose_str = res[start:end] pose = [float(x.strip()) for x in pose_str.split(",")] return pose def getAngel(self): res = self.dashboard.GetAngle() code = self.parseResultId(res)[0] if code == 0: pass # print("success") else: print("获取关节角度失败") return # 处理字符串 start = res.find("{")+1 end = res.find("}") angle_str = res[start:end] print("res:",res) angle = [float(x.strip()) for x in angle_str.split(",")] return angle def getForwardKin(self,joints_list,user=-1,tool=-1): res = self.dashboard.PositiveKin(*joints_list,user,tool) code = self.parseResultId(res)[0] if code == 0: pass # print("success") else: print("获取正向运动学解失败") return # 处理字符串 start = res.find("{")+1 end = res.find("}") pose_str = res[start:end] pose = [float(x.strip()) for x in pose_str.split(",")] return pose def getInverseKin(self,poses_list,user=-1,tool=-1,useJointNear=-1,JointNear=''): ''' poses_list X Y Z RX RY RZ useJointNear 0 或不携带 表示 JointNear无效 1 表示根据JointNear就近选解 jointNear string "jointNear={j1,j2,j3,j4,j5,j6}" ''' res = self.dashboard.InverseKin(*poses_list,user,tool,useJointNear,JointNear) print(res) code = self.parseResultId(res)[0] if code == 0: pass # print("success") else: print("获取逆向运动学解失败") return # 处理字符串 start = res.find("{")+1 end = res.find("}") joint_str = res[start:end] joint = [float(x.strip()) for x in joint_str.split(",")] return joint def disableRobot(self): res = self.dashboard.DisableRobot() code = self.parseResultId(res)[0] if code == 0: print("下使能机械臂成功") else: print("下使能机械臂失败") return def clearError(self): res = self.dashboard.ClearError() code = self.parseResultId(res)[0] if code == 0: print("清楚报警成功") else: print("清楚报警失败") return def start_drag(self): res = self.dashboard.StartDrag() code = self.parseResultId(res)[0] if code == 0: print("拖拽模式开启成功") else: print("拖拽模式开启失败") return def stop_drag(self): res = self.dashboard.StopDrag() code = self.parseResultId(res)[0] if code == 0: print("拖拽模式关闭成功") else: print("拖拽模式关闭失败") return def stop_motion(self): res = self.dashboard.Stop() code = self.parseResultId(res)[0] if code == 0: print("机械臂停止下发运动指令队列") else: print("停止下发运动失败,需立即拍下急停") return def __del__(self): del self.dashboard del self.feedFour ## 内部工具函数 def __transform_m_rad_2_mm_deg(self,input_list): ''' 接受列表 [X Y Z RX RY RZ] 将单位 m/rad 转为 mm/deg ''' if len(input_list) != 6: raise ValueError("Input list must contain exactly 6 elements [X, Y, Z, RX, RY, RZ]") # 转换位置单位:m -> mm (乘以 1000) x_mm = input_list[0] * 1000.0 y_mm = input_list[1] * 1000.0 z_mm = input_list[2] * 1000.0 # 转换角度单位:rad -> deg (乘以 180/pi) rx_deg = math.degrees(input_list[3]) ry_deg = math.degrees(input_list[4]) rz_deg = math.degrees(input_list[5]) return [x_mm, y_mm, z_mm, rx_deg, ry_deg, rz_deg] def __transform_rad_2_deg(self,input_list): ''' 接受列表 [J1 J2 J3 J4 J5 J6] 将单位rad转为deg ''' if len(input_list) != 6: raise ValueError("Input list must contain exactly 6 elements [J1, J2, J3, J4, J5, J6]") # 转换角度单位: rad -> deg J_deg = [] for i in range(6): J_deg.append(math.degrees(input_list[i])) return J_deg ## 为反馈线程封装 def Get_feedInfo_angle(self): # print("反馈当前角度",self.feedbackData.QActual) return self.feedbackData.QActual def Get_feedInfo_pose(self): # print("反馈当前TCP位置",self.feedbackData.ToolVectorActual) return self.feedbackData.ToolVectorActual ## 适配中间层的再封装接口 # # 为状态管理而封装的初始化函数 # def init(self): # self.is_exit = False # self.traj_list = None # self.last_pose_command = np.zeros(6) # self.last_input_command = None # self.tcp_offset = None # self.init_J = [-45.0009079,55.5785141,-120.68821716,5.11103201,90.00195312,-90.00085449] # self.filter_matirx = np.zeros((1,7)) # 位姿伺服用 # self.filter_matrix = np.zeros((1,6)) # 角度伺服用 # sleep(1) # self.is_standby = False # code = self.RunPoint_P_inJoint(self.init_J) # if code == 0: # print("机械臂初始化成功且到达初始位置") # else: # print("机械臂初始化失败") def get_pose_at_flange(self): ''' 输出pos,quat pos: [x,y,z] in m quat: [qx,qy,qz,qw] 四元数 ''' pose = self.getPose(user=0, tool=0) # 获取初始法兰坐标系下的位姿 x,y,z,rx,ry,rz = pose # x y z in mm; rx ry rz in degree x /= 1000 y /= 1000 z /= 1000 pos = np.array([x,y,z]) quat_rot = R.from_euler('xyz', [rx, ry, rz], degrees=True).as_quat() return pos,quat_rot def get_end_position(self,tool=1): ''' 工具默认为1 输出pos,quat pos: [x,y,z] in m quat: [qx,qy,qz,qw] 四元数 ''' pose = self.getPose(user=0, tool=tool) x,y,z,rx,ry,rz = pose # x y z in mm; rx ry rz in degree x /= 1000 y /= 1000 z /= 1000 pos = np.array([x,y,z]) quat_rot = R.from_euler('xyz', [rx, ry, rz], degrees=True).as_quat() return pos,quat_rot def get_arm_position(self): ''' 直接获得当前user/tool坐标系下的机械臂位置 ''' ''' 输出pos,quat pos: [x,y,z] in m quat: [qx,qy,qz,qw] 四元数 ''' pose = self.Get_feedInfo_pose() # print("pose",pose) x,y,z,rx,ry,rz = pose # x y z in mm; rx ry rz in degree x /= 1000 y /= 1000 z /= 1000 pos = np.array([x,y,z]) quat_rot = R.from_euler('xyz', [rx, ry, rz], degrees=True).as_quat() return pos,quat_rot def __circular_mean(self,angles): # 将角度转换为复数单位圆上的点,再计算均值 # 为处理周期性问题 angles_rad = np.deg2rad(angles) x = np.mean(np.cos(angles_rad)) y = np.mean(np.sin(angles_rad)) return np.rad2deg(np.arctan2(y, x)) def process_command_angle(self,arm_joint_command): if (not self.is_exit) and (not self.is_standby): current_joint = np.asarray(arm_joint_command) # degree # FIFO self.filter_matrix = np.append(self.filter_matrix,[current_joint],axis=0) if len(self.filter_matrix)<7: return 0,current_joint self.filter_matrix = np.delete(self.filter_matrix,0,axis = 0) avg_joint = np.array([self.__circular_mean(self.filter_matrix[:, i]) for i in range(len(current_joint))]) return 0,avg_joint return -1,None def process_command(self, arm_position_command, arm_orientation_command): if (not self.is_exit) and (not self.is_standby): # 构造当前位姿(位置 + 四元数) current_pose = np.concatenate([ arm_position_command, # [x, y, z] arm_orientation_command # [qx, qy, qz, qw] ]) # 更新滑动窗口(FIFO) self.filter_matirx = np.append(self.filter_matirx, [current_pose], axis=0) # 如果窗口未填满,直接返回当前值(不滤波) if len(self.filter_matirx) < 7: rot_euler = R.from_quat(arm_orientation_command).as_euler('xyz', degrees=True) result = np.concatenate([arm_position_command, rot_euler]) return 0, result # 窗口已填满,删除最旧数据 self.filter_matirx = np.delete(self.filter_matirx, 0, axis=0) # --- 位置分量:滑动平均(仅对有效数据)--- avg_position = np.mean(self.filter_matirx[:, :3], axis=0) # --- 四元数分量:加权平均(处理符号歧义)--- quats = self.filter_matirx[:, 3:7] for i in range(1, len(quats)): if np.dot(quats[0], quats[i]) < 0: quats[i] = -quats[i] # 确保所有四元数在同一半球 avg_quat = np.mean(quats, axis=0) avg_quat = avg_quat / np.linalg.norm(avg_quat) # 归一化 # 转换回欧拉角 rot_euler = R.from_quat(avg_quat).as_euler('xyz', degrees=True) # 合并结果 [x, y, z, roll, pitch, yaw] result = np.concatenate([avg_position, rot_euler]) return 0, result return -1, None def chooseRightFrame(self): self.chooseBaseFrame(i=1) # self.chooseEndEffector(i=0) return if __name__ == "__main__": # sleep(5) dobot = dobot_nova5("192.168.5.1") dobot.start() posJ = [0,30,-120,0,90,0] dobot.RunPoint_P_inJoint(posJ) sleep(1) dobot.setEndEffector(i=9,tool_i="{0.0,0.0,154.071,0.0,0.0,0.0}") dobot.chooseEndEffector(i=9) print("Arm pose:",dobot.getPose()) # dobot.RunPoint_P_inPose([300, -135, 224.34, -180, 0, -90]) # dobot.start_drag() dobot.disableRobot()