from collections import deque
from scipy.interpolate import interp1d
import rospy
import rosgraph
import smach
import smach_ros
import sys
import psutil
from pathlib import Path
import websockets
import threading
import argparse
import asyncio
import numpy as np
import time
import json
import requests
import signal
import copy
import cv2
import os
import shutil
import random
import yaml
import requests
from tools.yaml_operator import read_yaml
from tools.log import CustomLogger
from typing import Literal
from MassageControl.MassageRobot_nova5 import MassageRobot
from aucpuncture2point import ToolCamera, AbdomenDetector,BackDetector,LegDetector, CoordinateTransformer
from scipy.spatial.transform import Rotation as R
from scipy.interpolate import make_interp_spline
import matplotlib.pyplot as plt
import datetime
import shutil
import importlib.util
from MassageControl.hardware.force_sensor import XjcSensor
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from VortXDB.client import VTXClient
from functools import wraps # 加个wraps,保持函数名不会丢
# 自定义输出类,将输出同时发送到终端和日志文件
class MultiWriter:
def __init__(self, *writers):
self.writers = writers
def write(self, message):
for writer in self.writers:
writer.write(message)
writer.flush() # 确保及时输出
def flush(self):
for writer in self.writers:
writer.flush()
# 重定向标准输出和标准错误到文件和终端
def redirect_output(log_file):
# 保存当前的标准输出和标准错误(终端输出)
original_stdout_fd = sys.stdout.fileno()
original_stderr_fd = sys.stderr.fileno()
# 打开日志文件用于写入
log_fd = os.open(log_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
# 保留原始的标准输出和错误输出,创建文件对象
original_stdout = os.fdopen(os.dup(original_stdout_fd), 'w')
original_stderr = os.fdopen(os.dup(original_stderr_fd), 'w')
# 将 stdout 和 stderr 分别指向日志文件
os.dup2(log_fd, original_stdout_fd)
os.dup2(log_fd, original_stderr_fd)
# 将输出同时发送到终端和日志文件
sys.stdout = MultiWriter(original_stdout, os.fdopen(log_fd, 'w'))
sys.stderr = MultiWriter(original_stderr, os.fdopen(log_fd, 'w'))
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")
# 目标目录和目标文件路径(将日志文件复制到新的位置)
target_dir = '/home/jsfb/jsfb_ws/MassageLog/'
os.makedirs(target_dir, exist_ok=True) # 确保目标目录存在
source_log_file = '../log/Massage.log'
# 如果源文件不存在,则创建一个空文件
if not os.path.exists(source_log_file):
with open(source_log_file, 'w'):
pass # 创建空文件
# 定义复制的目标文件路径(带时间戳)
target_file = os.path.join(target_dir, f'Massage_{timestamp}.log')
# 复制文件到新位置
shutil.copy('../log/Massage.log', target_file)
log_file = '../log/Massage.log'
redirect_output(log_file)
# 定义模块加载的函数
def load_class_from_pyc(pyc_path, class_name, module_name):
"""
动态加载 .pyc 文件并尝试访问其中的类。
:param pyc_path: .pyc 文件路径
:param class_name: 需要访问的类名
:param module_name: 模块的名称(用于调试和错误信息)
:return: 类对象(如果加载和访问成功)
:raises: 文件未找到或类未定义等异常
"""
# 检查文件是否存在
if not os.path.exists(pyc_path):
raise FileNotFoundError(f"Module file '{pyc_path}' not found.")
try:
# 动态加载模块
spec = importlib.util.spec_from_file_location(module_name, pyc_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
# 尝试获取指定的类
class_obj = getattr(module, class_name, None)
if class_obj is None:
raise AttributeError(f"Class '{class_name}' not found in module '{module_name}'.")
return class_obj # 返回类对象
except Exception as e:
raise RuntimeError(f"Failed to load class '{class_name}' from module '{module_name}': {str(e)}")
# 定义 .pyc 文件的路径
shockwave_pyc_path = '/home/jsfb/jsfb_ws/hardware_bin/shockwave.pyc'
thermotherapy_pyc_path = '/home/jsfb/jsfb_ws/hardware_bin/thermotherapy.pyc'
stone_pyc_path = '/home/jsfb/jsfb_ws/hardware_bin/stone.pyc'
ion_pyc_path = '/home/jsfb/jsfb_ws/hardware_bin/ion.pyc'
heat_pyc_path = '/home/jsfb/jsfb_ws/hardware_bin/heat.pyc'
# 使用封装的函数加载各个模块和类
try:
Shockwave = load_class_from_pyc(shockwave_pyc_path, "Shockwave", "shockwave")
print("Shockwave class loaded successfully.")
except Exception as e:
print(f"Error loading Shockwave: {str(e)}")
try:
Thermotherapy = load_class_from_pyc(thermotherapy_pyc_path, "Thermotherapy", "thermotherapy")
print("Thermotherapy class loaded successfully.")
except Exception as e:
print(f"Error loading Thermotherapy: {str(e)}")
try:
Stone = load_class_from_pyc(stone_pyc_path, "Stone", "stone")
print("Stone class loaded successfully.")
except Exception as e:
print(f"Error loading Stone: {str(e)}")
try:
Ion = load_class_from_pyc(ion_pyc_path, "Ion", "ion")
print("Ion class loaded successfully.")
except Exception as e:
print(f"Error loading Ion: {str(e)}")
try:
Heat = load_class_from_pyc(heat_pyc_path, "Heat", "heat")
print("Heat class loaded successfully.")
except Exception as e:
print(f"Error loading Heat: {str(e)}")
def monitor_cpu_usage(func):
@wraps(func)
def wrapper(*args, **kwargs):
process = psutil.Process(os.getpid())
# 获取执行前的 I/O 数据
io_start = process.io_counters()
# 记录执行前的内存使用情况
memory_start = process.memory_info().rss / 1024 / 1024 # 以 MB 为单位
# 记录执行前的 CPU 占用百分比
cpu_start_percent = process.cpu_percent(interval=None)
cpu_start = process.cpu_times()
time_start = time.time()
result = func(*args, **kwargs)
time_end = time.time()
cpu_end = process.cpu_times()
# 获取执行后的 I/O 数据
io_end = process.io_counters()
# 记录执行后的内存使用情况
memory_end = process.memory_info().rss / 1024 / 1024 # 以 MB 为单位
# 记录执行后的 CPU 占用百分比
cpu_end_percent = process.cpu_percent(interval=None)
# 计算 I/O 相关的差值
read_bytes = io_end.read_bytes - io_start.read_bytes
write_bytes = io_end.write_bytes - io_start.write_bytes
user_time = cpu_end.user - cpu_start.user
system_time = cpu_end.system - cpu_start.system
wall_time = time_end - time_start
# 打印 CPU 占用情况
print(f"\n【函数 {func.__name__} 执行完毕】")
print(f" CPU用户态时间: {user_time:.4f}秒")
print(f" CPU内核态时间: {system_time:.4f}秒")
print(f" 总墙钟时间(实际时间): {wall_time:.4f}秒")
print(f" 执行前 CPU 占用百分比: {cpu_start_percent:.2f}%")
print(f" 执行后 CPU 占用百分比: {cpu_end_percent:.2f}%")
print(f" 执行前内存使用: {memory_start:.2f}MB")
print(f" 执行后内存使用: {memory_end:.2f}MB")
print(f" 内存消耗: {memory_end - memory_start:.2f}MB")
print(f" 读取字节数: {read_bytes / 1024:.2f}KB")
print(f" 写入字节数: {write_bytes / 1024:.2f}KB")
return result
return wrapper
def parse_controller_args():
parser = argparse.ArgumentParser(description="Admittance control")
parser.add_argument(
"--massagecontrol_path",
type=str,
default="MassageControl/config/robot_config.yaml",
)
parser.add_argument("--force_limit", type=list, default=[-70, -5])
parser.add_argument("--unit_length", type=float, default=0.07)
parser.add_argument("--temperature_limit", type=list, default=[0, 5])
parser.add_argument("--speed_limit", type=list, default=[0, 5])
parser.add_argument("--gear_limit", type=list, default=[0, 5])
parser.add_argument("--shake_limit", type=list, default=[0, 5])
parser.add_argument("--press_limit", type=list, default=[1, 27])
parser.add_argument("--frequency_limit", type=list, default=[1, 16])
parser.add_argument("--high_limit", type=list, default=[0.04, 0.15])
args = parser.parse_args()
return args
class StopWorkflow(Exception):
pass
class PauseWorkflow(Exception):
pass
class EmergencyStopWorkflow(Exception):
pass
class SharedResources:
def __init__(self, args):
# 日志
self.logger = CustomLogger('Massage',True)
self.vtxdb = VTXClient(False)
# 机械臂对象
self.robot = MassageRobot(args.massagecontrol_path)
position,quat_rot = self.robot.arm.get_arm_position()
euler_rot = R.from_quat(quat_rot).as_euler('xyz')
self.ready_pose = [position[0], position[1], position[2], euler_rot[0], euler_rot[1], euler_rot[2]]
self.robot.init_hardwares(self.ready_pose)
time.sleep(1)
# 事件
self.begin_event = threading.Event()
self.stop_event = threading.Event()
self.pause_event = threading.Event()
self.skip_event = threading.Event()
self.adjust_event = threading.Event()
self.high_st_limit = 0.95
self.low_st_limit = 0.5
# self.cam_length = 1.70 # 1.80
# 变量
self.choose_task = None
self.position_increment = np.array([0, 0, 0])
self.now_state = "IDLE"
self.current_part = None # 正在运行的部位
self.task_time = 0
self.current_time = 0
self.loops = 0
self.body_part = None
self.progress = 0 # 按摩进度
self.robot.arm_state.massage_wrench = np.array([0, 0, -20, 0, 0, 0])
self.temperature = 3
self.gear = 0
self.shake = 0
self.press = 12
self.frequency = 6
self.speed = 2
self.direction = 1
# self.is_execute = False
self.temper_head = None
self.temperature_default = 3 #热疗头温度档位默认值
self.temperature_default_thermotherapy = 3 #热疗头温度档位默认值
self.temperature_default_stone = 1 #砭石头温度档位默认参数
self.temperature_default_ion = 1
self.temperature_default_heat = 1 #能量热疗温度档位默认参数
self.gear_default = self.gear
self.shake_default = self.shake
self.press_default = self.press
self.frequency_default = self.frequency
self.speed_default = self.speed
self.direction_default = self.direction
self.desired_high_default = 0.06
self.cloud_points = np.array([[1,1]])
self.cloud_points_shoulder = np.array([[1,1]])
self.abdomen_detector = AbdomenDetector()
self.back_detector = BackDetector()
self.leg_detector = LegDetector()
self.coordinate_tf = None
self.acupoint_key_data = None
self.y_median_depth_list =None
self.x_avg = None
self.intrinsics = None
# 常量
self.unit_length = args.unit_length
self.force_limit = args.force_limit
self.temperature_limit = args.temperature_limit
self.speed_limit = args.speed_limit
self.gear_limit = args.gear_limit
self.shake_limit = args.shake_limit
self.press_limit = args.press_limit
self.frequency_limit = args.frequency_limit
self.high_limit = args.high_limit
# 事件循环
self.loop = asyncio.new_event_loop()
# 相机
self.tool_cam = None
self.license_check = False
self.is_acupoint = False
self.mode_real = 1 #是否开启拍照,测试时设为0
self.is_switch_controller = True
self.use_algorithm = "hybrid"
self.broken_test = 0
self.show_image_num = 0
self.next_points = None
self.mode_commands_list = deque([])
self.mode_commands_list_default = deque([])
self.move_count = 0
self.use_mode = "smart"
self.is_pause = False
self.is_finish = False
self.is_insert = False
self.is_skip = False
self.path_length = 0
self.path_velocity = 30
self.is_first_move = True
self.is_jump = False
self.jump_length = 0
self.jump_velocity = 60
self.is_acupoint = False
self.plan_task = None
self.motion_task = None
self.generate_thread = None
self.insert_queue_thread = None
self.current_queue_num = -2
self.massage_task_num = 0
self.insert_len = 0
self.motion_count = 0
self.start_time = None
self.jump_mode = True
self.jump_mode_default = True
self.is_headOn = True
self.is_headOn_default = True
self.cal_acu = False
self.get_picture = True
self.manual_start = False
self.show_picture_type = 1
self.manual_stage = 0
self.acupuncture_dict = None
self.selected_plan = None
self.target_points = None
self.update_thread = None
self.update_massageHead_temper_thread = None
async def client(self, instruction, uri="ws://localhost:8766"):
async with websockets.connect(uri) as websocket:
self.logger.log_info("begin to send")
await websocket.send(instruction)
self.logger.log_info("Message " + instruction + " sent to the server")
# 接收来自服务器的响应
response = await websocket.recv()
print(f"Received response: {response}")
def send_instruction(self, instruction, target="language"):
if isinstance(instruction, dict) and "progress" in instruction:
if int(instruction["progress"]) > self.progress:
self.progress = int(instruction["progress"])
try:
if target == "language":
uri = "ws://localhost:8766"
asyncio.run_coroutine_threadsafe(
self.client(instruction, uri), self.loop
)
elif target == "ui_update_massage_status":
requests.post(
"http://127.0.0.1:5000/update_massage_status", data=instruction
)
self.logger.log_info(
"Instruction " + str(instruction) + " sent to ui server"
)
elif target == "ui_change_image":
requests.post(
"http://127.0.0.1:5000/change_image", data=instruction
)
self.logger.log_info(
"Instruction " + str(instruction) + " sent to ui server"
)
elif target == "ui_on_message":
requests.post(
"http://127.0.0.1:5000/on_message", data=instruction
)
self.logger.log_info(
"Instruction " + str(instruction) + " sent to ui server"
)
elif target == "record_usage":
requests.post(
"http://127.0.0.1:5288/api/license/use")
self.logger.log_info(
"Instruction " + str(instruction) + " sent to license server"
)
elif target == "check_license":
try:
response = requests.get("http://127.0.0.1:5288/api/license/check")
response.raise_for_status() # Check for HTTP request errors (e.g., 404, 500)
self.logger.log_info(
"Instruction " + str(instruction) + " sent to license server"
)
# Check if the response contains 'can_use'
if 'can_use' in response.json():
self.license_check = copy.deepcopy(response.json().get("can_use"))
else:
raise ValueError("Response does not contain 'can_use' field")
self.logger.log_info(
"license check status is : " + str(self.license_check)
)
except requests.exceptions.RequestException as e:
self.license_check = False
self.logger.log_error(f"Request failed: {e}")
except ValueError as e:
self.license_check = False
self.logger.log_error(f"Value error: {e}")
except Exception as e:
self.license_check = False
self.logger.log_error(f"An unexpected error occurred: {e}")
elif target == "iot_massage_head":
try:
instruction['device_name'] = os.uname()[1]
instruction['produce_name'] = "RS-LL-X1"
response = requests.post(
"http://app.robotstorm.tech:8080/iot/massageHead",
json=instruction,
timeout=5 # 设置5秒超时
)
response.raise_for_status() # 检查HTTP错误
self.logger.log_info(
f"send iot_massage_head: {instruction}, status code: {response.status_code}"
)
except Exception as e:
self.logger.log_error(f"Failed to send massage head data to IoT platform: {e}")
except Exception as e:
self.logger.log_error(f"请求失败: {e}")
def calculate_path_length(self,points: np.ndarray) -> float:
"""
计算路径上所有点之间的总长度
:param points: 轨迹点的坐标数组
:return: 轨迹路径的总长度
"""
length = 0.0
self.logger.log_blue(f"二维路径数组长度len(points):{len(points)}")
for i in range(1, len(points)):
# 计算两个连续点之间的欧几里得距离
length += np.abs(np.linalg.norm(np.array(points[i]) - np.array(points[i - 1])))
self.logger.log_blue(f"二维路径数组欧几里得距离length:{length}")
self.path_length = length
def generate_next_points(self,start_point,end_point,width,circles,direction:Literal["CW","CCW"] ="CW",path:Literal["line","in_spiral","out_spiral","ellipse","lemniscate","cycloid","point","point_rub"] ="line"):
self.show_image_num += 1
self.logger.log_blue(f"start and end position:{start_point},{end_point}")
if self.is_insert:
self.logger.log_info("检测到插入队列,执行退出!")
return
if path == "line":
# 循经直推法
temp_points = self.robot.generate_line_cloudPoint(start = start_point, end = end_point)
elif path == "in_spiral":
# 螺旋外散法
temp_points = self.robot.generate_in_spiral_cloudPoint(start = start_point, end = end_point, width = width, num_cycle_factor = circles, direction = direction)
elif path == "out_spiral":
# 螺旋内揉法
temp_points = self.robot.generate_out_spiral_cloudPoint(start = start_point, end = end_point, width = width, num_cycle_factor = circles, direction = direction)
elif path == "ellipse":
# 周天环摩法
temp_points = self.robot.generate_ellipse_cloudPoint(start = start_point, end = end_point, width = width, num_cycle_factor = circles, direction = direction)
elif path == "lemniscate":
# 双环疏经法
temp_points = self.robot.generate_Lemniscate_cloudPoint(start = start_point, end = end_point, width = width, num_cycle_factor = circles, direction = direction)
elif path == "cycloid":
# 摆浪通络法
temp_points = self.robot.generate_prolateCycloid_cloudPoint(start = start_point, end = end_point, width = width, num_cycle_factor = circles, direction = direction)
elif path == "point" or path == "point_rub":
# 定穴点按法
temp_points = []
temp_points.append(np.array(start_point))
temp_points = np.array(temp_points)
else:
self.logger.log_error("未找到該mode")
self.send_instruction(instruction={"message": "检测到输入手法异常
并尝试重新设置"}, target="ui_on_message")
raise StopWorkflow()
if self.is_insert:
self.logger.log_info("检测到插入队列,执行退出!")
return
if temp_points.size == 0:
self.send_instruction(instruction={"message": "请检查是否存在不合理的参数设置或过短的按摩长度
并尝试重新设置"}, target="ui_on_message")
self.logger.log_error("生成路径失败")
raise StopWorkflow()
self.next_points = self.coordinate_tf.pixel_to_robotarm(temp_points,self.show_image_num)
if self.is_insert:
self.logger.log_info("检测到插入队列,执行退出!")
return
if self.next_points is None:
if self.body_part == "belly":
self.send_instruction(instruction={"message": "您躺的位置有点不合适呢
请尝试往上躺一点呢"}, target="ui_on_message")
else:
self.send_instruction(instruction={"message": "您躺的位置有点不合适呢
请尝试往下躺一点呢"}, target="ui_on_message")
raise StopWorkflow()
else:
if path != "point" and path != "point_rub":
self.calculate_path_length(points = copy.deepcopy(temp_points))
else:
self.path_length = 0
if self.is_insert:
self.logger.log_info("检测到插入队列,执行退出!")
return
self.logger.log_info(f"当前转化路径模式{path}")
self.logger.log_blue(f"self.path_length:{self.path_length}")
self.logger.log_blue(f"生成下一个点位:{self.next_points[-1]}")
def parse_point(self, point_str):
"""解析点字符串,支持 xxx、xxx&xxx、xxx&xxx@1/4,返回 np.array 或 None"""
if point_str is None:
return np.array(None) # 如果本身是 None,直接返回 None,不处理也不报错
point_str = point_str.strip()
# 处理 xxx&xxx@1/4 形式
if '&' in point_str:
ratio_value = 0.5 # 默认中点
# 检查是否带比例
if '@' in point_str:
try:
points_part, ratio_part = point_str.split('@')
points = points_part.split('&')
numerator, denominator = map(float, ratio_part.strip().split('/'))
ratio_value = numerator / denominator
except Exception as e:
self.logger.log_error(f"Invalid ratio format in '{point_str}'")
return 0 # 比例错误,返回 0(根据你之前返回 0 的风格)
else:
points = point_str.split('&')
if len(points) != 2:
self.logger.log_error(f"Invalid point format, expected two points in '{point_str}'")
return None # 非法格式
p1_name, p2_name = points[0].strip(), points[1].strip()
point1 = self.acupuncture_dict.get(p1_name)
point2 = self.acupuncture_dict.get(p2_name)
# 只检查未匹配的情况(按你原来逻辑)
if p1_name and point1 is None:
self.logger.log_error(f"No acupuncture point found for '{p1_name}'")
return None
if p2_name and point2 is None:
self.logger.log_error(f"No acupuncture point found for '{p2_name}'")
return None
# 计算比例点
return np.array(point1) * (1 - ratio_value) + np.array(point2) * ratio_value
else:
# 单点情况
point = self.acupuncture_dict.get(point_str)
if point is None:
self.logger.log_error(f"No acupuncture point found for '{point_str}'")
return 0 # 未匹配才返回 0
return np.array(point)
# 定义一个处理点的函数
def handle_special_case(self, np_point, np_other_point, point_type):
# 计算斜率和截距
if np.abs(np_point[0] - np_other_point[0]) < 0.001: # 判断是否为垂直线
# 垂直线的x值是固定的
self.logger.log_blue(f"{point_type}点是垂直线,x值固定为 {np_point[0]}")
np_point_new = copy.deepcopy(np_point)
# 使用与其他点的x值相同
np_point_new[0] = np_point[0]
# 根据上下限调整y值
if np_point[1] <= self.coordinate_tf.camera_Upper_limit:
np_point_new[1] = self.coordinate_tf.camera_Upper_limit + 3
elif np_point[1] >= self.coordinate_tf.camera_Lower_limit:
np_point_new[1] = self.coordinate_tf.camera_Lower_limit - 3
else:
# 计算斜率
m = (np_other_point[1] - np_point[1]) / (np_other_point[0] - np_point[0])
# 计算截距
b = np_point[1] - m * np_point[0]
np_point_new = copy.deepcopy(np_point)
# 根据上下限调整y值
if np_point[1] <= self.coordinate_tf.camera_Upper_limit:
np_point_new[1] = self.coordinate_tf.camera_Upper_limit + 3
elif np_point[1] >= self.coordinate_tf.camera_Lower_limit:
np_point_new[1] = self.coordinate_tf.camera_Lower_limit - 3
# 根据新的y值计算对应的x
np_point_new[0] = (np_point_new[1] - b) / m
return np_point_new
def process_massage_plan(self, massage_type, body_part, plan_name):
massage_plan = self.vtxdb.get("massage_plan", plan_name)
if not massage_plan:
self.logger.log_error(f"No massage plan found for {massage_type} - {body_part}")
self.acupuncture_dict = None
return 0
if massage_plan.get('choose_task') != massage_type or massage_plan.get('body_part') != body_part:
self.logger.log_error(f"Wrong massage plan for {massage_type} - {body_part}")
self.acupuncture_dict = None
return 0
if self.body_part == "belly":
self.robot.width_default = np.abs(self.acupuncture_dict.get('水分')[1] - self.acupuncture_dict.get('关元')[1])
elif self.body_part == "back" or self.body_part == "shoulder" or self.body_part == "back_shoulder" or self.body_part == "waist":
self.robot.width_default = np.abs(self.acupuncture_dict.get('膈俞左')[0] - self.acupuncture_dict.get('膈关左')[0])
elif self.body_part == "leg":
self.robot.width_default = np.abs(self.acupuncture_dict.get('委中左')[1] - self.acupuncture_dict.get('合阳左')[1])
task_plan = massage_plan.get('task_plan')
last_end_point_raw = None
for task in task_plan:
start_point_raw = task.get('start_point')
end_point_raw = task.get('end_point')
if start_point_raw is None:
start_point_raw = copy.deepcopy(last_end_point_raw)
last_end_point_raw = copy.deepcopy(end_point_raw)
start_point = self.parse_point(start_point_raw)
if start_point is None: # 发现问题立即退出(按原逻辑)
return 0
end_point = self.parse_point(end_point_raw)
if end_point is None:
return 0
if start_point.tolist() is not None:
np_start_point = np.array(copy.deepcopy(start_point))
if end_point.tolist() is not None:
np_end_point = np.array(copy.deepcopy(end_point))
# 检查起点和终点的深度范围
if np.logical_or(np_start_point[1] <= (self.coordinate_tf.camera_Upper_limit+2), np_start_point[1] >= (self.coordinate_tf.camera_Lower_limit-2)) and np.logical_or(np_end_point[1] <= (self.coordinate_tf.camera_Upper_limit+2), np_end_point[1] >= (self.coordinate_tf.camera_Lower_limit-2)):
self.logger.log_error(f"传入起点:{start_point_raw}-{start_point}和终点:{end_point_raw}-{end_point}没有深度,消去该位置")
continue
elif np.logical_or(np_start_point[1] <= (self.coordinate_tf.camera_Upper_limit+2), np_start_point[1] >= (self.coordinate_tf.camera_Lower_limit-2)):# 如果起点没有深度,进行特殊处理
self.logger.log_blue(f"传入起点:{start_point_raw}-{start_point}没有深度,特殊处理该位置")
start_point = self.handle_special_case(np_start_point, np_end_point, 'start')
self.logger.log_blue(f"起点:{np_start_point}特殊处理成{start_point},特殊处理该位置")
elif np.logical_or(np_end_point[1] <= (self.coordinate_tf.camera_Upper_limit+2), np_end_point[1] >= (self.coordinate_tf.camera_Lower_limit-2)): # 如果终点没有深度,进行特殊处理
self.logger.log_blue(f"传入终点:{end_point_raw}-{end_point}没有深度,特殊处理该位置")
end_point = self.handle_special_case(np_end_point, np_start_point, 'end')
self.logger.log_blue(f"终点:{np_end_point}特殊处理成{end_point},特殊处理该位置")
path = task.get('path')
massage_time = task.get('time')
width = task.get('width')
cycles = task.get('cycles')
direction = task.get('direction')
jump_mode = task.get('jump_mode')
try:
is_headOn = task.get('headOn')
except:
is_headOn = True
if body_part == "shoulder":
pathBodypart = body_part
else:
pathBodypart = None
self.mode_commands_list.append([
copy.deepcopy(start_point),
copy.deepcopy(end_point),
width,
cycles,
direction,
path,
massage_time,
None,
pathBodypart,
start_point_raw,
end_point_raw,
jump_mode,
is_headOn
])
if self.mode_commands_list is None:
self.send_instruction(instruction={"message": "没有检测到穴位点
请重新拍照"}, target="ui_on_message")
return 0
return 1
def queue_final_deal(self, return_state):
self.is_first_move = True ##第一次标志位给True
try:
jump_mode_dict = self.vtxdb.get("system_config", "jump_mode")
self.jump_mode_default = jump_mode_dict.get('enabled', False)
self.logger.log_blue(f"当前系统默认跳跃模式:{self.jump_mode_default}")
except:
self.jump_mode_default = True
if self.choose_task == "finger" or self.choose_task == "shockwave":
if self.body_part != "back_shoulder":
self.mode_commands_list = self.mode_commands_list * 2
self.mode_commands_list_default = copy.deepcopy(self.mode_commands_list)
self.mode_commands_list = self.mode_commands_list * max(1, int(self.loops)) # 执行多遍按摩时 copy n次任务队列
self.massage_task_num = len(self.mode_commands_list) + 1 # 记录任务长度
self.logger.log_blue(f"当前记录的任务队列为第: {self.current_queue_num}个")
self.logger.log_blue(f"当前记录的move_count为第: {self.move_count}个")
self.logger.log_blue(f"当前记录的motion_count为第: {self.motion_count}个")
self.logger.log_blue(f"当前记录的图片序号为第: {self.show_image_num}个")
self.logger.log_blue(f"当前记录的任务队列为: {self.massage_task_num}")
self.logger.log_blue(f"下发运动计划总时间: {self.task_time}")
self.logger.log_blue(f"当前运动时间: {self.current_time}")
self.logger.log_blue(f"目标循环次数: {self.loops}")
if self.is_pause:
if self.current_queue_num > 0:
self.logger.log_info(f"消去已执行完的任务:{self.current_queue_num}")
startPos = self.mode_commands_list[self.current_queue_num][0]
if startPos.tolist() is None:
self.mode_commands_list[self.current_queue_num][0] = copy.deepcopy(self.mode_commands_list[self.current_queue_num-1][1])
for i in range(self.current_queue_num):
self.mode_commands_list.popleft()
self.move_count = copy.deepcopy(self.current_queue_num)
self.motion_count = copy.deepcopy(self.current_queue_num)
self.show_image_num = copy.deepcopy(self.current_queue_num)
else:
self.current_queue_num = -2
self.move_count = self.current_queue_num+2
self.motion_count = self.current_queue_num+2
self.show_image_num = self.current_queue_num+2
else:
self.progress = 0
self.logger.log_blue(f"当前执行的任务队列为第: {self.current_queue_num}个")
self.logger.log_blue(f"当前规划的任务队列为: {self.mode_commands_list}")
self.acupuncture_dict = None
return return_state
def get_massage_plan_config(self):
"""获取按摩类型和身体部位的配置映射"""
return {
"ball": {
"supported_parts": ["belly", "back", "waist"],
"plan_prefix": "全能滚珠"
},
"thermotherapy": {
"supported_parts": ["belly", "back", "waist", "leg"],
"plan_prefix": "深部热疗"
},
"stone": {
"supported_parts": ["belly", "back", "waist", "leg"],
"plan_prefix": "温砭舒揉"
},
"roller": {
"supported_parts": ["back", "waist", "leg"],
"plan_prefix": "滚滚刺疗"
},
"spheres": {
"supported_parts": ["back", "waist", "leg"],
"plan_prefix": "天球滚捏"
},
"heat": {
"supported_parts": ["belly", "back", "waist", "leg"],
"plan_prefix": "能量热疗"
},
"shockwave": {
"supported_parts": ["back", "waist", "leg", "shoulder", "back_shoulder"],
"plan_prefix": "点阵按摩"
},
"ion": {
"supported_parts": ["back","belly"],
"plan_prefix": "离子光灸"
},
"finger": {
"supported_parts": ["back", "waist", "leg", "shoulder", "back_shoulder"],
"plan_prefix": "指疗通络"
}
}
def get_plan_name(self, task_prefix, part_name, selected_plan=None):
"""
生成或验证按摩方案名称
Args:
task_prefix: 按摩类型前缀(如"深部热疗")
part_name: 身体部位名称(如"背部")
selected_plan: 用户选择的方案名称
Returns:
str: 最终使用的方案名称
"""
default_plan = f"{task_prefix}-{part_name}-默认"
if not selected_plan:
return default_plan
# 解析选择的方案
parts = selected_plan.split("-")
if len(parts) != 3:
self.logger.log_error(f"选择的方案格式不正确: {selected_plan}")
return default_plan
# 验证前缀和部位是否匹配
if parts[0] != task_prefix or parts[1] != part_name:
self.logger.log_error(f"选择的方案与当前按摩类型或部位不匹配: {selected_plan}")
return default_plan
return selected_plan
def process_massage_task(self):
"""处理按摩任务"""
massage_config = self.get_massage_plan_config()
# 检查按摩类型是否支持
if self.choose_task not in massage_config:
self.manual_start = False
self.logger.log_error(f"未知的按摩类型 {self.choose_task}")
return "stop"
task_config = massage_config[self.choose_task]
body_part = self.body_part
# 检查身体部位是否支持
if body_part not in task_config["supported_parts"]:
self.manual_start = False
self.logger.log_error(f"{self.choose_task} 不支持按摩部位 -{body_part}-")
return None
# 特殊处理背部和肩部组合按摩
if body_part == "back_shoulder":
result1 = self.process_massage_plan(
self.choose_task,
"shoulder",
self.get_plan_name(task_config["plan_prefix"], "肩颈", self.selected_plan)
)
result2 = self.process_massage_plan(
self.choose_task,
"back",
self.get_plan_name(task_config["plan_prefix"], "背部", self.selected_plan)
)
if result1 and result2:
return self.queue_final_deal(f"to_{self.choose_task}")
else:
# 普通部位按摩处理
# part_name = "腹部" if body_part == "belly" else "背部" if body_part == "back" else "肩颈"
part_name = (
"腹部" if body_part == "belly"
else "背部" if body_part == "back"
else "肩颈" if body_part == "shoulder"
else "腰部" if body_part == "waist"
else "腿部"
)
plan_name = self.get_plan_name(
task_config["plan_prefix"],
part_name,
self.selected_plan
)
if self.process_massage_plan(self.choose_task, body_part, plan_name):
# 获取当前时间并格式化为指定格式
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构建按摩头数据
instruction = {
"start_time": current_time,
"task_time": self.task_time,
"head_type": self.choose_task,
"body_part": body_part
}
self.send_instruction(instruction, target="iot_massage_head")
return self.queue_final_deal(f"to_{self.choose_task}")
self.manual_start = False
return None
def update_massageHead_temper(self):
task_map = {
"stone": self.robot.stone
# "stone": self.robot.stone,
# "heat": self.robot.heat
}
while self.choose_task is not None:
try:
device = task_map.get(self.choose_task)
if device and device.isOn:
response = device.get_temp()
self.logger.log_info(f"读取到的按摩头温度:{response}")
self.temper_head = response
else:
self.temper_head = None
except Exception:
self.temper_head = None
if self.temper_head is not None:
instruction = {
"temperature": self.temperature,
"temper_head": self.temper_head
}
self.logger.log_yellow(instruction)
self.send_instruction(instruction, "ui_update_massage_status")
time.sleep(5)
def init_massage_parameters(self):
if self.choose_task == "thermotherapy":
config = self.vtxdb.get("robot_config","massage_head.thermotherapy_head")
temperature_limit = config.get('temperature_limit', None)
gear_limit = config.get('gear_limit', None)
shake_limit = config.get('shake_limit', None)
if temperature_limit is None or gear_limit is None or shake_limit is None:
self.logger.log_info("limit is none,please check the input")
return 'pause' if self.is_pause else 'stop'
self.temperature_limit = temperature_limit
self.gear_limit = gear_limit
self.shake_limit = shake_limit
self.robot.thermotherapy = Thermotherapy()
if self.is_pause == False:
self.temperature = self.temperature_default_thermotherapy
self.gear = self.gear_default
self.shake = self.shake_default
if self.body_part == "belly":
self.robot.arm_state.massage_wrench = np.array([0, 0, -10, 0, 0, 0])
else:
self.robot.arm_state.massage_wrench = np.array([0, 0, -20, 0, 0, 0])
self.logger.log_info('启动热疗按摩')
for _ in range(3): self.robot.thermotherapy.start_process(); time.sleep(0.5)
self.robot.thermotherapy.set_working_status(heat_level=self.temperature)
elif self.choose_task == "stone":
config = self.vtxdb.get("robot_config","massage_head.stone_head")
temperature_limit = config.get('temperature_limit', None)
speed_limit = config.get('speed_limit', None)
if temperature_limit is None or speed_limit is None:
self.logger.log_info("limit is none,please check the input")
return "stop"
self.temperature_limit = temperature_limit
self.speed_limit = speed_limit
self.robot.stone = Stone()
if self.is_pause == False:
self.temperature = self.temperature_default_stone
self.speed = self.speed_default
self.direction = self.direction_default
if self.body_part == "belly":
self.robot.arm_state.massage_wrench = np.array([0, 0, -10, 0, 0, 0])
else:
self.robot.arm_state.massage_wrench = np.array([0, 0, -20, 0, 0, 0])
self.logger.log_info('启动砭石按摩')
for _ in range(3): self.robot.stone.power_on(); time.sleep(0.5)
self.robot.stone.set_working_status(heat_level=self.temperature)
if self.update_massageHead_temper_thread is None:
self.update_massageHead_temper_thread = threading.Thread(target=self.update_massageHead_temper)
self.update_massageHead_temper_thread.start()
elif self.choose_task == "heat":
config = self.vtxdb.get("robot_config","massage_head.heat_head")
temperature_limit = config.get('temperature_limit', None)
if temperature_limit is None:
self.logger.log_info("limit is none,please check the input")
return "stop"
self.temperature_limit = temperature_limit
self.robot.heat = Heat()
if self.is_pause == False:
self.temperature = self.temperature_default_heat
if self.body_part == "belly":
self.massage_wrench = np.array([0, 0, -10, 0, 0, 0])
else:
self.massage_wrench = np.array([0, 0, -20, 0, 0, 0])
self.logger.log_info('启动能量按摩')
for _ in range(3): self.robot.heat.power_on(); time.sleep(0.5)
self.robot.heat.set_working_status(heat_level=self.temperature)
if self.update_massageHead_temper_thread is None:
self.update_massageHead_temper_thread = threading.Thread(target=self.update_massageHead_temper)
self.update_massageHead_temper_thread.start()
elif self.choose_task == "shockwave":
config = self.vtxdb.get("robot_config","massage_head.shockwave_head")
press_limit = config.get('press_limit', None)
frequency_limit = config.get('frequency_limit', None)
if press_limit is None or frequency_limit is None:
self.logger.log_info("limit is none,please check the input")
return "stop"
self.press_limit = press_limit
self.frequency_limit = frequency_limit
self.logger.log_info('启动点阵按摩')
self.robot.shockwave = Shockwave()
if self.is_pause == False:
self.press = self.press_default
self.frequency = self.frequency_default
self.robot.arm_state.massage_wrench = np.array([0, 0, -25, 0, 0, 0])
elif self.choose_task == "ball":
if self.is_pause == False:
self.robot.arm_state.massage_wrench = np.array([0, 0, -15, 0, 0, 0])
elif self.choose_task == "finger":
if self.is_pause == False:
self.robot.arm_state.massage_wrench = np.array([0, 0, -25, 0, 0, 0])
elif self.choose_task == "roller":
if self.is_pause == False:
self.robot.arm_state.massage_wrench = np.array([0, 0, -25, 0, 0, 0])
elif self.choose_task == "spheres":
if self.is_pause == False:
self.robot.arm_state.massage_wrench = np.array([0, 0, -25, 0, 0, 0])
elif self.choose_task == "ion":
config = self.vtxdb.get("robot_config","massage_head.ion_head")
temperature_limit = config.get('temperature_limit', None)
high_limit = config.get('high_limit', None)
if temperature_limit is None or high_limit is None:
self.logger.log_info("limit is none,please check the input")
return "stop"
self.robot.ion = Ion()
self.temperature_limit = temperature_limit
self.high_limit = high_limit
self.logger.log_info('启动离子按摩')
if self.is_pause == False:
self.temperature = self.temperature_default_ion
self.robot.arm_state.desired_high = self.desired_high_default
self.robot.arm_state.massage_wrench = np.array([0, 0, -5, 0, 0, 0])
return None
def init_queue_para(self):
self.move_count = 0
self.motion_count = 0
self.show_image_num = 0
self.plan_task = None
self.motion_task = None
self.current_queue_num = -2
self.insert_len = 0
self.mode_commands_list = deque([])
self.mode_commands_list_default = deque([])
self.target_points = None
self.is_skip = False
def move_camera_position(self): #运动到拍照位置
## 测试时使用的注释程序
self.tool_cam = ToolCamera(host="127.0.0.1")
self.tool_cam.start()
time.sleep(1)
self.logger.log_yellow("运动到拍照位置")
self.robot.arm.disable_servo()
time.sleep(0.5)
# code = self.robot.arm.move_joint(self.robot.arm.cam_pose, max_retry_count = 4,wait=True)
code = self.robot.set_joint(joint = self.robot.arm.cam_pose)
if code == -1:
self.logger.log_error("运动到拍照位置失败")
self.stop_event.set()
return "emergency"
time.sleep(1)
self.cam_position,self.cam_quaternion = self.robot.arm.get_end_position()
self.logger.log_info(f"获取cam的位置:{self.cam_position}和姿态:{self.cam_quaternion}")
if self.stop_event.is_set():
self.logger.log_info(f"检测到停止事件")
return "stop"
if self.pause_event.is_set():
self.pause_event.clear()
self.send_instruction(instruction="no_pause", target="language")
self.send_instruction(instruction={"message": "我现在在拍照呢
暂时不可以暂停哦"}, target="ui_on_message")
return None
def process_take_photo(self): #拍照
self.send_instruction(instruction="take_a_photo", target="language")
# 最大重试次数
MAX_RETRIES = 5
# 初始化重试计数器
retry_count = 0
while retry_count < MAX_RETRIES:
# 尝试获取数据
color_image, depth_image, intrinsics = self.tool_cam.get_latest_frame()
# 检查获取到的内容是否为 None
if color_image is not None and depth_image is not None and intrinsics is not None:
# 如果所有数据都成功获取,则退出循环
break
# 如果获取失败,增加重试计数器
retry_count += 1
self.logger.log_warning(f"获取数据失败,正在重试 {retry_count}/{MAX_RETRIES}...")
self.tool_cam.start()
time.sleep(1)
# 检查是否超出重试次数
if retry_count == MAX_RETRIES:
self.logger.log_warning("获取数据失败,已达到最大重试次数。")
else:
self.logger.log_info("拍照成功。")
cali_intrinsics = self.vtxdb.get("robot_config","camera.intrinsics")
if cali_intrinsics is not None:
self.intrinsics = cali_intrinsics
else:
self.intrinsics = copy.deepcopy(intrinsics)
self.logger.log_info("self.intrinsics:" + str(self.intrinsics))
# 获取当前时间并格式化为文件夹名 (格式: 年-月-日_时-分-秒)
current_time = time.strftime("%Y-%m-%d_%H-%M-%S")
current_time = f"{current_time}_{self.body_part}"
# 判断 mode_real 并保存图像
if self.mode_real == 1 or self.mode_real == 3:
# 根据 mode_real 来设置图片保存的基础路径
if self.mode_real == 1:
base_dir = "/home/jsfb/jsfb_ws/collected_data/images"
else:
base_dir = "/home/jsfb/jsfb_ws/test_data/test_images"
# 创建一个根据时间命名的新文件夹
folder_path = os.path.join(base_dir, current_time)
os.makedirs(folder_path, exist_ok=True) # 如果文件夹不存在则创建
try:
# 保存 color 和 depth 图像到文件
cv2.imwrite(os.path.join(folder_path, "color.png"), color_image)
cv2.imwrite(os.path.join(folder_path, "depth.png"), depth_image)
# 另外保存到固定位置
cv2.imwrite("aucpuncture2point/configs/using_img/color.png", color_image)
cv2.imwrite("aucpuncture2point/configs/using_img/depth.png", depth_image)
# 保存相机内参到文件
with open(os.path.join(folder_path, "intrinsics.json"), "w") as f:
json.dump(self.intrinsics, f)
if self.mode_real == 3:
# 定义基础路径
if self.body_part == "belly":
base_path = "/home/jsfb/jsfb_ws/test_data/belly_images/"
elif self.body_part == "leg":
base_path = '/home/jsfb/jsfb_ws/test_data/leg_images/'
else:
base_path = '/home/jsfb/jsfb_ws/test_data/images/'
# 获取所有子文件夹的列表,并按修改时间排序
subfolders = sorted(
[f.path for f in os.scandir(base_path) if f.is_dir()],
key=lambda x: os.path.getmtime(x) # 根据修改时间排序
)
# 打印出所有子文件夹的名称(按时间顺序)
print("Subfolders sorted by modification time:")
for folder in subfolders:
print(folder)
# 按照时间顺序选择文件夹
# 检查是否达到最后一个文件夹,如果是则从头开始
selected_subfolder = subfolders[self.broken_test]
if self.broken_test == len(subfolders) - 1:
self.broken_test = 0 # 重置为第一个文件夹
else:
self.broken_test += 1 # 否则,选择下一个文件夹
# 打印出选择的文件夹
print(f"\nSelected subfolder (earliest modified): {selected_subfolder}")
# 拼接color.png和depth.png的路径
img_source = os.path.join(selected_subfolder, 'color.png')
depth_source = os.path.join(selected_subfolder, 'depth.png')
# 目标路径
img_destination = 'aucpuncture2point/configs/using_img/color.png'
depth_destination = 'aucpuncture2point/configs/using_img/depth.png'
try:
# 使用 shutil 复制文件
shutil.copy(img_source, img_destination)
shutil.copy(depth_source, depth_destination)
except Exception as e:
print(f"Error occurred while copying test images: {e}")
return 'stop'
except Exception as e:
# 打印异常信息并返回 'stop'
print(f"Error occurred while saving images: {e}")
return 'pause' if self.is_pause else 'stop'
else:
base_dir = "/home/jsfb/jsfb_ws/collected_data/images"
# 创建一个根据时间命名的新文件夹
folder_path = os.path.join(base_dir, current_time)
os.makedirs(folder_path, exist_ok=True) # 如果文件夹不存在则创建
# 根据 body_part 来选择图像源
if self.body_part == "belly":
img_source = '/home/jsfb/jsfb_ws/test_data/test_front/color.png'
depth_source = '/home/jsfb/jsfb_ws/test_data/test_front/depth.png'
elif self.body_part == "leg":
img_source = "/home/jsfb/jsfb_ws/test_data/test_leg/color.png"
depth_source = "/home/jsfb/jsfb_ws/test_data/test_leg/depth.png"
else:
img_source = '/home/jsfb/jsfb_ws/test_data/test_back/color.png'
depth_source = '/home/jsfb/jsfb_ws/test_data/test_back/depth.png'
# 目标路径
img_destination = 'aucpuncture2point/configs/using_img/color.png'
depth_destination = 'aucpuncture2point/configs/using_img/depth.png'
try:
# 使用 shutil 复制文件
shutil.copy(img_source, img_destination)
shutil.copy(depth_source, depth_destination)
except Exception as e:
print(f"Error occurred while copying test images: {e}")
return 'stop'
if self.use_mode == "manual":
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/color.png","type":self.show_picture_type}
self.send_instruction(instruction=instruction, target="ui_change_image")
color_image = cv2.imread('aucpuncture2point/configs/using_img/color.png')
depth_image = cv2.imread('aucpuncture2point/configs/using_img/depth.png', cv2.IMREAD_UNCHANGED)
self.coordinate_tf = CoordinateTransformer(self.intrinsics,color_image,depth_image,self.cam_position,self.cam_quaternion,self.vtxdb)
if self.use_mode == "manual":
self.manual_stage = 1
self.send_instruction({"manual_stage": self.manual_stage}, "ui_update_massage_status")
self.get_picture = False
return None
def process_acupoint_recognize(self): # 处理按摩点识别
self.send_instruction(instruction="acupoint_recognize", target="language")
if self.acupoint_key_data is not None:
self.logger.log_info("手动计算穴位")
if self.body_part == "belly":
self.logger.log_info("计算腹部穴位")
self.acupuncture_dict = self.abdomen_detector.detect_by_manual(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/abdomen_img/abdomen_acupoints.png',json_data=self.acupoint_key_data)
elif self.body_part == "back" or self.body_part == "shoulder" or self.body_part == "back_shoulder" or self.body_part == "waist":
self.logger.log_info("计算背部穴位")
self.acupuncture_dict = self.back_detector.detect_by_manual(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/back_img/back_acupoints.png',json_data=self.acupoint_key_data)
elif self.body_part == "leg":
self.logger.log_info("计算腿部穴位")
self.acupuncture_dict = self.leg_detector.detect_by_manual(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/leg_img/leg_acupoints.png',json_data=self.acupoint_key_data)
else:
self.logger.log_error("未知的身体部位")
self.send_instruction(instruction={"message": "未知的身体部位
请重新选择"}, target="ui_on_message")
return 'stop'
else:
self.logger.log_info("自动计算穴位")
if self.body_part == "belly":
self.logger.log_info("计算腹部穴位")
self.acupuncture_dict = self.abdomen_detector.detect_by_yolo(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/abdomen_img/abdomen_acupoints.png')
elif self.body_part == "back" or self.body_part == "shoulder" or self.body_part == "back_shoulder" or self.body_part == "waist":
self.logger.log_info("计算背部穴位")
self.acupuncture_dict = self.back_detector.detect_by_yolo(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/back_img/back_acupoints.png')
elif self.body_part == "leg":
self.logger.log_info("计算腿部穴位")
self.acupuncture_dict = self.leg_detector.detect_by_yolo(image_path='aucpuncture2point/configs/using_img/color.png',output_path='aucpuncture2point/configs/using_img/leg_img/leg_acupoints.png')
else:
self.logger.log_error("未知的身体部位")
self.send_instruction(instruction={"message": "未知的身体部位
请重新选择"}, target="ui_on_message")
return 'stop'
if self.acupuncture_dict is not None:
# 将 acupoints 中的所有 tuple 值转换为 list
for key, value in self.acupuncture_dict.items():
if isinstance(value, tuple): # 检查值是否为 tuple
self.acupuncture_dict[key] = list(value) # 转换为 list
self.logger.log_info(f"self.acupuncture_dict{self.acupuncture_dict}")
self.cal_acu = False
self.acupoint_key_data = None
if self.body_part == "belly":
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/abdomen_img/abdomen_acupoints.png","type":3}
elif self.body_part == "back" or self.body_part == "shoulder" or self.body_part == "back_shoulder" or self.body_part == "waist":
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/back_img/back_acupoints.png","type":3}
elif self.body_part == "leg":
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/leg_img/leg_acupoints.png","type":3}
self.send_instruction(instruction=instruction, target="ui_change_image")
if self.coordinate_tf:
if self.body_part == "belly":
self.coordinate_tf.color_visualize = cv2.imread(os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/abdomen_img/abdomen_acupoints.png")
elif self.body_part == "back" or self.body_part == "shoulder" or self.body_part == "back_shoulder" or self.body_part == "waist":
self.coordinate_tf.color_visualize = cv2.imread(os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/back_img/back_acupoints.png")
elif self.body_part == "leg":
self.coordinate_tf.color_visualize = cv2.imread(os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/leg_img/leg_acupoints.png")
else:
self.logger.log_error("坐标转换器未初始化")
instruction = {}
self.is_acupoint = True
instruction["is_acupoint"] = self.is_acupoint
self.logger.log_yellow(instruction)
self.send_instruction(instruction, "ui_update_massage_status")
else:
if self.use_mode == "smart":
self.logger.log_error("沒有檢測到穴位")
self.send_instruction(instruction={"message": "没有检测到穴位点
请重新拍照"}, target="ui_on_message")
return 'pause' if self.is_pause else 'stop'
elif self.use_mode == "manual":
self.logger.log_error("没有检测到关键点")
self.send_instruction(instruction={"message": "没有检测到穴位点
请重新拍照"}, target="ui_on_message")
self.cal_acu = False
return None
def process_ready_massage(self): # 处理准备按摩
if self.use_mode == "manual":
self.manual_stage = 2
self.send_instruction({"manual_stage": self.manual_stage}, "ui_update_massage_status")
if self.tool_cam is not None:
self.tool_cam.stop()
pose = copy.deepcopy(self.robot.arm.standby_pos)
if self.stop_event.is_set():
return "stop"
if self.pause_event.is_set():
self.pause_event.clear()
self.send_instruction(instruction="no_pause", target="language")
self.send_instruction(instruction={"message": "即将开始按摩
暂时不可以暂停哦"}, target="ui_on_message")
self.robot.is_waitting = True
self.robot.arm.disable_servo()
time.sleep(1)
# code = self.robot.arm.move_joint(pose, max_retry_count = 4)
code = self.robot.set_joint(joint = pose)
if code == -1:
self.logger.log_error("运动到standby位置失败")
self.stop_event.set()
return "emergency"
time.sleep(1)
if self.stop_event.is_set():
return "stop"
if self.pause_event.is_set():
self.pause_event.clear()
self.send_instruction(instruction="no_pause", target="language")
self.send_instruction(instruction={"message": "即将开始按摩
暂时不可以暂停哦"}, target="ui_on_message")
self.robot.is_waitting = False
self.logger.log_info("执行预处理")
self.robot.arm_state.desired_wrench = np.zeros(6,dtype=np.float64) # 重置期望力矩
self.is_finish = False #重置任务完成标志
#是否切换控制方式
self.is_switch_controller = self.mode_real != 0
if self.choose_task == "shockwave" or self.choose_task == "finger":
self.use_algorithm = "position" if self.mode_real == 3 else ("hybridAdmit" if self.is_switch_controller else "admittance")
elif self.choose_task == "ion":
self.use_algorithm = "positionerSensor"
else:
self.use_algorithm = "position" if self.mode_real == 3 else ("hybrid" if self.is_switch_controller else "admittance")
return None
def tf2base(self, offset, rpy):
# 创建从 rpy (roll, pitch, yaw) 生成的旋转对象
rotation = R.from_euler("xyz", rpy)
# 将偏移应用到旋转矩阵,转换到基坐标系
base_offset = rotation.apply(offset)
return base_offset
def process_insert_queue(self):
if self.generate_thread is not None:
self.generate_thread.join()
self.generate_thread = None
self.coordinate_tf.insert_queue = False #重置视觉插入队列标志位
self.show_image_num = self.motion_count #重置图片编号
self.current_queue_num = self.motion_count-2
if self.plan_task is not None:
self.mode_commands_list.appendleft(self.plan_task)
turn_motion_task = copy.deepcopy(self.motion_task)
turn_motion_task[0] = copy.deepcopy(self.motion_task[1])
turn_motion_task[1] = copy.deepcopy(self.motion_task[0])
if self.motion_task[5] == "in_spiral": #如果当前运动任务为螺旋线,则特殊处理当前螺旋线方式
turn_motion_task[5] = "out_spiral"
elif self.motion_task[5] == "out_spiral":
turn_motion_task[5] = "in_spiral"
self.mode_commands_list.appendleft(self.motion_task)
self.mode_commands_list.appendleft(turn_motion_task)
self.insert_len = 2
self.massage_task_num += 2
# 获取任务信息
self.plan_task = self.mode_commands_list.popleft()
self.insert_len -= 1
self.logger.log_info(f"plan_task:{self.plan_task}")
if self.plan_task[0].tolist() is None:
if self.plan_task[5] == "point" or self.plan_task[5] == "point_rub":
self.plan_task[0] = copy.deepcopy(self.plan_task[1]) # 更新任务队列
else:
self.plan_task[0] = copy.deepcopy(self.motion_task[1]) # 更新任务队列
self.logger.log_blue(f"修正 startPos:{self.plan_task[0]}")
# 启动新线程
self.logger.log_info(f"开始规划 {self.plan_task} 路线")
self.is_insert = False #重置插入标志位
self.generate_thread = threading.Thread(target=self.generate_next_points, args=(self.plan_task[0],self.plan_task[1],self.plan_task[2],self.plan_task[3]),kwargs={'direction':self.plan_task[4],'path':self.plan_task[5]})
self.generate_thread.start()
#准备处理之前插入
if self.plan_task[0] is not None and not np.allclose(self.plan_task[0], self.motion_task[1], atol=0.1):
# 跳跃准备
self.is_jump = True
self.jump_length = np.linalg.norm(self.plan_task[0] - self.motion_task[1])
def update_time(self):
self.start_time = time.time() - self.current_time
while self.is_finish == False:
self.current_time = time.time() - self.start_time #更新时间
if self.loops != 0:
self.send_instruction({"progress": str(round((self.move_count/self.massage_task_num)*100))}, "ui_update_massage_status")
else:
if self.task_time > 0:
self.send_instruction({"progress": str(round((self.current_time/self.task_time)*100))}, "ui_update_massage_status")
else:
self.send_instruction({"progress": str(round(0))}, "ui_update_massage_status")
if self.current_time > self.task_time:
self.is_finish = True
time.sleep(5)
if self.loops == 0:
self.logger.log_info(f"按摩时间到{self.current_time},{self.task_time}")
self.is_finish = True
else:
self.logger.log_info(f"当前控制为循环几次:{self.loops}")
class SMART_DEAL(smach.State):
def __init__(self, resources: SharedResources):
smach.State.__init__(
self,
outcomes=[
"to_thermotherapy",
"to_shockwave",
"to_ball",
"to_finger",
"to_roller",
"to_spheres",
"to_stone",
"to_heat",
"to_ion",
"pause",
"stop",
"emergency",
],
)
self.resources = resources
def execute(self, userdata):
self.resources.robot.is_waitting = True
try: #初始化参数
if (code := self.resources.init_massage_parameters()):
return code
except Exception as e:
self.resources.send_instruction(instruction={"message": "按摩头开启失败
请检查按摩头及连接状态"}, target="ui_on_message")
self.resources.logger.log_error(f'启动按摩头错误: {e}')
return "stop"
instruction = {}
instruction["massage_service_started"] = True
instruction["is_massaging"] = (True)
instruction["massage_head"] = (self.resources.robot.current_head)
instruction["progress"] = str(self.resources.progress)
instruction["force"] = str(-self.resources.robot.arm_state.massage_wrench[2])
instruction["current_head"] = self.resources.robot.current_head
instruction["temperature"] = self.resources.temperature
instruction["gear"] = self.resources.gear
instruction["shake"] = self.resources.shake
instruction["press"] = self.resources.press
instruction["high"] = int(self.resources.robot.arm_state.desired_high * 100)
instruction["frequency"] = self.resources.frequency
instruction["task_time"] = str(self.resources.task_time)
instruction["current_time"] = str(self.resources.current_time)
instruction["loops"] = str(self.resources.loops)
instruction["body_part"] = self.resources.body_part
instruction["mode_real"] = self.resources.mode_real
instruction["massage_state"] = self.resources.now_state
instruction["manual_stage"] = self.resources.manual_stage
instruction["temper_head"] = None
self.resources.logger.log_yellow(instruction)
self.resources.send_instruction(instruction, "ui_update_massage_status")
if (code := self.resources.move_camera_position()): #运动到拍照位置
return code
if (code := self.resources.process_take_photo()): #运动到拍照位置
return code
if (code := self.resources.process_acupoint_recognize()): #穴位识别
return code
if (code := self.resources.process_ready_massage()): #准备按摩
return code
# 在原代码中替换整个 if-elif 链
result = self.resources.process_massage_task()
if result:
return result
return 'pause' if self.resources.is_pause else 'stop'
class MANUAL_DEAL(smach.State):
def __init__(self, resources: SharedResources):
smach.State.__init__(
self,
outcomes=[
"to_thermotherapy",
"to_shockwave",
"to_ball",
"to_finger",
"to_roller",
"to_spheres",
"to_stone",
"to_heat",
"pause",
"stop",
"to_ion",
"emergency",
],
)
self.resources = resources
def execute(self, userdata):
self.resources.robot.is_waitting = True
try:
code = self.resources.init_massage_parameters()
if code is not None:
return code
except Exception as e:
self.resources.send_instruction(instruction={"message": "按摩头开启失败
请检查按摩头及连接状态"}, target="ui_on_message")
self.resources.logger.log_error(f'启动按摩头错误: {e}')
return "stop"
instruction = {}
instruction["massage_service_started"] = True
instruction["is_massaging"] = (True)
instruction["massage_head"] = (self.resources.robot.current_head)
instruction["progress"] = str(self.resources.progress)
instruction["force"] = str(-self.resources.robot.arm_state.massage_wrench[2])
instruction["current_head"] = self.resources.robot.current_head
instruction["temperature"] = self.resources.temperature
instruction["gear"] = self.resources.gear
instruction["shake"] = self.resources.shake
instruction["press"] = self.resources.press
instruction["high"] = int(self.resources.robot.arm_state.desired_high * 100)
instruction["frequency"] = self.resources.frequency
instruction["task_time"] = str(self.resources.task_time)
instruction["current_time"] = str(self.resources.current_time)
instruction["loops"] = str(self.resources.loops)
instruction["body_part"] = self.resources.body_part
instruction["mode_real"] = self.resources.mode_real
instruction["massage_state"] = self.resources.now_state
instruction["manual_stage"] = self.resources.manual_stage
self.resources.logger.log_yellow(instruction)
self.resources.send_instruction(instruction, "ui_update_massage_status")
if (code := self.resources.move_camera_position()): #运动到拍照位置
return code
while not self.resources.stop_event.is_set():
if self.resources.get_picture == True:
if (code := self.resources.process_take_photo()): #执行拍照
return code
elif self.resources.cal_acu == True:
if (code := self.resources.process_acupoint_recognize()): #穴位识别
return code
elif self.resources.manual_start == True and self.resources.acupuncture_dict is not None:
if (code := self.resources.process_ready_massage()): #准备按摩
return code
# 在原代码中替换整个 if-elif 链
result = self.resources.process_massage_task()
if result:
return result
return 'pause' if self.resources.is_pause else 'stop'
if self.resources.stop_event.is_set():
if self.resources.tool_cam is not None:
self.resources.tool_cam.stop()
return "stop"
if self.resources.tool_cam is not None:
self.resources.tool_cam.stop()
return 'stop'
class TaskBase(smach.State):
def __init__(self, resources: SharedResources):
smach.State.__init__(self, outcomes=["pause","stop", "emergency"])
self.resources = resources
self.admit_begin_pos = None
self.start_time = 10
self.turn = [0, 0, 45*np.pi/180]
self.turn_2D = [0, 0, 30*np.pi/180]
self.turn_ion = [0, 0, -45*np.pi/180]
self.back_angle = R.from_matrix(R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix() @ R.from_euler("xyz",self.turn,degrees=False).as_matrix()).as_euler("xyz",degrees=False)
self.back_angle_ion = R.from_matrix(R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix() @ R.from_euler("xyz",self.turn_ion,degrees=False).as_matrix()).as_euler("xyz",degrees=False)
def organize_dict(self, dictionary, substrings):
raise NotImplementedError("You should implement this method in derived classes")
def process_except(self, code):
"""处理异常,返回值为2或4时结束并返回原位,返回值为3时紧急停止,检测到调整事件时触发调整异常,return0表示正常运行,return1表示异常运行"""
if code == 3:
raise EmergencyStopWorkflow()
if self.resources.stop_event.is_set() or code == 4 or code == 2:
if code == 2:
self.resources.send_instruction(instruction="record_usage", target="record_usage")
raise StopWorkflow()
if self.resources.is_finish == False:
if self.resources.pause_event.is_set() or code == 5:
raise PauseWorkflow()
if self.resources.is_finish == False:
if self.resources.skip_event.is_set() or code == 6:
time.sleep(0.2)
position,quat_rot = self.resources.robot.arm.get_arm_position()
time.sleep(0.2)
target_point = np.zeros(6)
target_point[:3] = copy.deepcopy(position)
target_point[3:] = copy.deepcopy(R.from_quat(quat_rot).as_euler('xyz',degrees=False))
target_point[3:] = self.resources.robot.arm.level_base
target_point[:3] = target_point[:3] + self.resources.tf2base([0, 0, -0.10], self.resources.robot.arm.level_base)
self.resources.robot.skip_pos = copy.deepcopy(target_point)
self.resources.logger.log_blue(f"检测到跳过,正在处理跳过起始点: {self.resources.robot.skip_pos}")
return 0
if self.resources.adjust_event.is_set():
self.adjust_value()
return 1
return 0
def adjust_value(self):
raise NotImplementedError("You should implement this method in derived classes")
def execute(self, userdata):
raise NotImplementedError("You should implement this method in derived classes")
def tf2base(self, offset, rpy):
# 创建从 rpy (roll, pitch, yaw) 生成的旋转对象
rotation = R.from_euler("xyz", rpy)
# 将偏移应用到旋转矩阵,转换到基坐标系
base_offset = rotation.apply(offset)
return base_offset
def adjust_value(self):
pass
def write_to_json(self, filename):
# Helper function to convert numpy arrays to lists
def convert_numpy(obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
elif isinstance(obj, (list, tuple)):
return [convert_numpy(x) for x in obj]
elif isinstance(obj, dict):
return {k: convert_numpy(v) for k, v in obj.items()}
return obj
# Get motion data (ensure NumPy arrays are converted)
motion_data = {
"startPos": convert_numpy(self.resources.motion_task[9]),
"endPos": convert_numpy(self.resources.motion_task[10]),
"massagePath": convert_numpy(self.resources.motion_task[5]),
"massage_wrench": convert_numpy(self.resources.robot.arm_state.massage_wrench),
"massage_head": self.resources.choose_task,
"massage_plan": self.resources.selected_plan,
"task_time": self.resources.task_time,
"current_time": datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")
}
# Define the directory path
directory = "/home/jsfb/jsfb_ws/collected_data/message"
# Ensure the directory exists
if not os.path.exists(directory):
os.makedirs(directory)
# Construct full file path
filepath = os.path.join(directory, filename)
# Load existing data (if file exists) or initialize a new list
if os.path.exists(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
existing_data = json.load(f)
else:
existing_data = []
# Append new data
existing_data.append(motion_data)
# Write back to file
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(existing_data, f, ensure_ascii=False, indent=4)
def process_jump_deal(self, jump_point):
self.resources.logger.log_yellow(f"jump_point: {jump_point}{self.resources.jump_length}")
# 跳跃处理
self.resources.logger.log_blue("开始处理跳跃")
jump_time = max(1, min(60, self.resources.jump_length/(self.resources.jump_velocity)))
if self.resources.choose_task == "shockwave":
self.resources.robot.shockwave.off()
# self.resources.send_instruction(instruction="change_position", target="language")
if self.resources.mode_real != 0 and self.resources.mode_real != 3:
massage_wrench = np.array([0, 0, 0, 0, 0, 0])
code = self.resources.robot.apply_force(wrench=massage_wrench, t=1, algorithm=self.resources.use_algorithm)
self.process_except(code)
if self.resources.jump_mode:
if self.resources.choose_task == "ion":
up_target_point = copy.deepcopy(jump_point)
up_target_point[3:] = copy.deepcopy(self.back_angle_ion)
up_target_point[:3] = up_target_point[:3] + self.tf2base([0, 0, -self.resources.robot.arm_state.desired_high], self.back_angle_ion)
else:
up_target_point = copy.deepcopy(jump_point)
up_target_point[3:] = self.resources.robot.arm.level_base
up_target_point[:3] = up_target_point[:3] + self.tf2base([0, 0, -0.10], self.resources.robot.arm.level_base)
print("up_target_point:",up_target_point)
if self.resources.choose_task == "shockwave" or self.resources.choose_task == "ion":
code = self.resources.robot.move_to_point(pose=up_target_point, t=jump_time, timeout=0.5, algorithm="admithybrid", is_interrupt=False,is_switch_controller=True)
else:
code = self.resources.robot.move_to_point(pose=up_target_point, t=jump_time, timeout=0.5, algorithm="admittance", is_interrupt=False,is_switch_controller=True)
self.process_except(code)
jump_point[:3] = jump_point[:3] + self.tf2base([0, 0, 0.015], self.resources.robot.arm.level_base)
if self.resources.choose_task == "shockwave" or self.resources.choose_task == "ion":
code = self.resources.robot.move_to_point(pose=jump_point, t=2.5, timeout=0.5, algorithm='admithybrid',is_interrupt=False)
else:
code = self.resources.robot.move_to_point(pose=jump_point, t=2.5, timeout=0.5, algorithm='admittance',is_interrupt=False)
self.process_except(code)
else:
if self.resources.mode_real != 0 and self.resources.mode_real != 3:
massage_wrench = np.array([0.5, 0.5, 0.5, 0.1, 0.1, 0.1])
code = self.resources.robot.apply_force(wrench=massage_wrench, t=1, algorithm=self.resources.use_algorithm)
self.process_except(code)
up_target_point = copy.deepcopy(jump_point)
if self.resources.choose_task == "ion":
up_target_point[3:] = copy.deepcopy(self.back_angle_ion)
up_target_point[:3] = jump_point[:3] + self.tf2base([0, 0, -self.resources.robot.arm_state.desired_high], self.back_angle_ion)
# else:
# up_target_point = copy.deepcopy(jump_point)
# up_target_point[3:] = self.resources.robot.arm.level_base
# up_target_point[:3] = up_target_point[:3] + self.tf2base([0, 0, -0.10], self.resources.robot.arm.level_base)
if self.resources.choose_task == "ion":
code = self.resources.robot.move_to_point(pose=up_target_point, t=jump_time*1.5, timeout=0.5, algorithm="positionerSensor", is_interrupt=False,is_switch_controller=True)
else:
code = self.resources.robot.move_to_point(pose=up_target_point, t=jump_time*1.5, timeout=0.5, algorithm="admittanceZ", is_interrupt=False,is_switch_controller=True)
self.process_except(code)
code = self.resources.robot.move_to_point(pose=jump_point, t=2, timeout=0.5, algorithm=self.resources.use_algorithm,is_interrupt=False,is_switch_controller=True)
self.process_except(code)
if self.resources.mode_real != 0 and self.resources.mode_real != 3:
code = self.resources.robot.apply_force(wrench=self.resources.robot.arm_state.massage_wrench, t=3, algorithm=self.resources.use_algorithm)
self.process_except(code)
self.resources.is_jump = False
def process_high_safety(self,target_point):
# 获取拍照位置
admit_begin_pos = copy.deepcopy(self.resources.cam_position)
ground_pos = admit_begin_pos + self.tf2base([0, 0, self.resources.robot.arm.cam_length], self.resources.robot.arm.level_base)
self.resources.logger.log_blue(f"ground_pos:{ground_pos}")
self.admit_begin_pos = ground_pos + self.tf2base([0, 0, -self.resources.high_st_limit], self.resources.robot.arm.level_base)
low_st_limit_pos = ground_pos + self.tf2base([0, 0, -self.resources.low_st_limit], self.resources.robot.arm.level_base)
self.resources.logger.log_blue(f"self.admit_begin_pos:{self.admit_begin_pos},{admit_begin_pos + self.tf2base([0, 0, self.resources.robot.arm.cam_length],self.resources.robot.arm.level_base)}")
target_point[3:] = self.resources.robot.arm.level_base
target_point[:3] = target_point[:3] + self.tf2base([0, 0, -0.15], self.resources.robot.arm.level_base)
admit_high = R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix() @ self.admit_begin_pos
target_high = R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix() @ target_point[:3]
target_low = R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix() @ low_st_limit_pos
self.resources.logger.log_blue(f"admit_high:,{admit_high},{target_high},{target_low}")
if target_high[2] >= target_low[2]:
self.resources.logger.log_error("目标点深度异常")
self.resources.send_instruction(instruction={"message": "目标位置深度信息异常
请检测设备情况"}, target="ui_on_message")
return False, target_point
if target_high[2] >= admit_high[2]:
self.resources.logger.log_blue(f"第一个目标点高度{target_high[2]}低于{admit_high[2]},将导纳开始位置置于{self.resources.high_st_limit}")
target_high[2] = copy.deepcopy(admit_high[2])
target_point[:3] = R.from_euler("xyz",self.resources.robot.arm.level_base,degrees=False).as_matrix().T @ target_high
self.start_time = 10 + np.abs((target_high[2] - admit_high[2])/0.015)
else:
self.start_time = 10
self.resources.logger.log_blue(f"导纳运动时间为{self.start_time}")
return True, target_point
def move_to_startpos(self):
# 任务执行步骤
# 到达第一个点位上方
# 获取第一组数据
first_command = self.resources.mode_commands_list[0]
# 获取第一组数据中的第一个元素(即 tempMassagePoints[0])
first_pos = first_command[0]
self.first_bodypart = first_command[8]
if first_pos is None:
self.resources.logger.log_error("the first position is None!")
raise StopWorkflow
if self.first_bodypart is None: # 若没有指定bodypart,则默认为back
self.first_bodypart = "back"
self.resources.logger.log_blue(f"读取第一个点位:{first_pos}")
self.target_point_list = []
self.target_point_list.append(np.array(first_pos))
# 给视觉发送xy坐标读取返回的位置姿态
self.resources.logger.log_blue(f"开始发送第一个点位:{self.target_point_list}")
self.target_point_list = self.resources.coordinate_tf.pixel_to_robotarm(self.target_point_list,number=self.resources.show_image_num)
if self.target_point_list is None:
if self.resources.body_part == "belly":
self.resources.send_instruction(instruction={"message": "您躺的位置有点不合适呢
请尝试往上躺一点呢"}, target="ui_on_message")
else:
self.resources.send_instruction(instruction={"message": "您躺的位置有点不合适呢
请尝试往下躺一点呢"}, target="ui_on_message")
if self.resources.is_pause:
raise PauseWorkflow
else:
raise StopWorkflow
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/Pixel_Visualization_{self.resources.show_image_num}.jpg"}
self.resources.logger.log_info(f'展示图片编号:{self.resources.show_image_num}')
self.resources.send_instruction(instruction=instruction, target="ui_change_image")
task = self.resources.choose_task
instruction = f"{task}_introduction" if task in ["shockwave", "finger", "roller", "spheres", "thermotherapy", "ball", "stone","ion", "heat"] else None
if instruction:
self.resources.send_instruction(instruction=instruction, target="language")
else:
self.resources.logger.log_error("未知的任务类型")
raise StopWorkflow
if self.resources.robot.force_sensor:
self.resources.robot.force_sensor.stop_background_reading()
self.resources.robot.force_sensor = XjcSensor() # 初始化实例化力传感器
time.sleep(0.2)
code = self.resources.robot.sensor_set_zero()
if code != 0:
self.resources.logger.log_error("传感器初始化失败")
self.resources.send_instruction(instruction={"message": "传感器初始化失败"}, target="ui_on_message")
if self.resources.is_pause:
raise PauseWorkflow
else:
raise StopWorkflow
code = self.resources.robot.sensor_enable()
if code != 0:
self.resources.logger.log_error("传感器初始化失败")
self.resources.send_instruction(instruction={"message": "传感器初始化失败"}, target="ui_on_message")
if self.resources.is_pause:
raise PauseWorkflow
else:
raise StopWorkflow
#启动机械臂,切换按摩头palyload
task = self.resources.choose_task
current_head = f"{task}_head" if task in ["shockwave", "finger", "roller", "spheres","thermotherapy", "ball", "stone","ion", "heat"] else None
if self.resources.choose_task == "thermotherapy":
self.resources.logger.log_info('启动热疗按摩')
self.resources.robot.thermotherapy.set_working_status(cur_level=self.resources.gear, heat_level=self.resources.temperature, vib_level=self.resources.shake)
elif self.resources.choose_task == "stone":
self.resources.logger.log_info('启动砭石按摩')
self.resources.robot.stone.set_working_status(heat_level=self.resources.temperature, speed_level=self.resources.speed, rotate_direction=self.resources.direction)
elif self.resources.choose_task == "heat":
self.resources.logger.log_info('启动能量按摩')
self.resources.robot.heat.set_working_status(heat_level=self.resources.temperature)
if current_head:
self.resources.robot.switch_payload(current_head)
else:
self.resources.logger.log_error("未知的按摩头类型")
raise StopWorkflow
#TODO 根据不同的任务选择不同的pose
time.sleep(0.1)
self.resources.logger.log_yellow("运动到伺服启动位置")
self.resources.robot.is_waitting = True
self.resources.robot.arm.disable_servo()
time.sleep(0.5)
self.resources.logger.log_blue(f"接受的第一个点位:{self.target_point_list}")
target_point = copy.deepcopy(self.target_point_list[0])
code,target_point = self.process_high_safety(target_point)
if not code:
raise StopWorkflow
self.resources.logger.log_blue(f"move_to_points: {target_point}")
code = self.resources.robot.set_position(pose=target_point,is_wait=True)
#先到达第一个点位上方
if code == -1:
self.resources.logger.log_error("运动伺服启动位置失败")
self.resources.stop_event.set()
raise EmergencyStopWorkflow
time.sleep(3.5)
self.resources.robot.is_waitting = False
self.resources.robot.start()
time.sleep(0.5)
def wait_queue(self): #等待规划生成线程完成并预处理,将plantask赋值给motiontask,并且置为None,发送信息到前端
if self.resources.insert_queue_thread is not None:
self.resources.insert_queue_thread.join()
self.resources.insert_queue_thread = None
self.resources.is_skip = False #重置跳过标志位
self.resources.generate_thread.join()
self.resources.generate_thread = None
instruction = {"path": os.path.dirname(os.path.abspath(__file__)) + f"/aucpuncture2point/configs/using_img/Pixel_Visualization_{self.resources.show_image_num}.jpg"}
self.resources.logger.log_blue(f"展示图片:{self.resources.show_image_num}")
self.resources.send_instruction(instruction=instruction, target="ui_change_image")
self.resources.motion_task = copy.deepcopy(self.resources.plan_task) #将规划任务赋给运动任务执行,更新
self.resources.motion_count += 1
self.resources.move_count += 1
self.resources.logger.log_blue(f"move_count:{self.resources.move_count},{self.resources.current_queue_num},{self.resources.show_image_num},{self.resources.insert_len}")
instruction = {}
instruction["start_pos"] = self.resources.motion_task[9]
instruction["end_pos"] = self.resources.motion_task[10]
instruction["massage_path"] = self.resources.motion_task[5]
self.resources.send_instruction(instruction, target="ui_update_massage_status")
self.resources.plan_task = None
self.resources.target_points = copy.deepcopy(self.resources.next_points)
#锁定姿态
num_points = len(self.resources.target_points)
task = self.resources.choose_task
motion = self.resources.motion_task[8]
if task == "ion":
angle = self.back_angle_ion
else:
angle = self.back_angle
if motion == "leg" or (task == "shockwave" and motion != "shoulder") or task == "ion":
for i in range(num_points):
self.resources.target_points[i][3:] = angle
if (self.resources.motion_task[6] is None or self.resources.motion_task[6] == 0):
if self.resources.motion_task[5] != "point" and self.resources.motion_task[5] != "point_rub":
self.resources.motion_task[6] = self.resources.path_length/self.resources.path_velocity
else:
self.resources.logger.log_info("未设置点摁的按摩时间")
self.resources.motion_task[6] = 20 # 当未设置点摁的按摩时间时,默认点摁按摩时间为20s
self.resources.motion_task[6] = max(0.5, min(450, self.resources.motion_task[6]))
def execute_movement(self): #执行motiontask任务
self.resources.logger.log_info(f"{self.resources.motion_task} 按摩中...")
if self.resources.is_jump:
self.process_jump_deal(jump_point=copy.deepcopy(self.resources.target_points[0]))
self.resources.robot.is_execute = True
if self.resources.motion_task[7] is not None:
self.resources.robot.arm_state.massage_wrench[2] = self.resources.motion_task[7]
code = self.resources.robot.apply_force(wrench=self.resources.robot.arm_state.massage_wrench, t=1, algorithm=self.resources.use_algorithm)
self.process_except(code)
if self.resources.skip_event.is_set() and self.resources.robot.skip_envent.is_set():
self.resources.logger.log_blue("检测到跳过,准备进入跳过流程")
self.resources.robot.is_execute = False
return True
if self.resources.choose_task == "shockwave":
self.resources.robot.shockwave.on() # 开启冲击波
self.resources.robot.shockwave.p_set(level = self.resources.press)
self.resources.robot.shockwave.f_set(level = self.resources.frequency)
if self.resources.motion_task[5] == "point" or self.resources.motion_task[5] == "point_rub":
use_algorithm = self.resources.use_algorithm
if self.resources.choose_task != "ion" and self.resources.motion_task[5] == "point":
self.resources.use_algorithm = "hybridPid"
print("self.resources.use_algorithm:",self.resources.use_algorithm)
code = self.resources.robot.move_to_point(pose=self.resources.target_points, t=self.resources.motion_task[6], timeout=0.5, interpolation="circle", algorithm=self.resources.use_algorithm,is_interrupt=False,is_switch_controller = self.resources.is_switch_controller)
self.resources.use_algorithm = use_algorithm
else:
code = self.resources.robot.move_to_point(pose=self.resources.target_points, t=self.resources.motion_task[6], timeout=0.5, interpolation="cloud_point", algorithm=self.resources.use_algorithm,is_interrupt=False,is_switch_controller = self.resources.is_switch_controller) #
self.process_except(code)
if self.resources.skip_event.is_set() and self.resources.robot.skip_envent.is_set():
self.resources.logger.log_blue("检测到跳过,准备进入跳过流程")
self.resources.robot.is_execute = False
return True
#准备处理之前插入
if self.resources.plan_task is not None:
if self.resources.plan_task[0] is not None and not np.allclose(self.resources.plan_task[0], self.resources.motion_task[1], atol=0.1):
# 跳跃准备
self.resources.is_jump = True
if self.resources.motion_task[11] is not None:
self.resources.jump_mode = copy.deepcopy(self.resources.motion_task[11])
else:
self.resources.jump_mode = copy.deepcopy(self.resources.jump_mode_default)
self.resources.jump_length = np.linalg.norm(self.resources.plan_task[0] - self.resources.motion_task[1])
self.resources.robot.is_execute = False
return False
def process_queue(self):# 从队列中获取任务赋值给planTASK用于生成线程规划
# 获取任务信息
self.resources.plan_task = self.resources.mode_commands_list.popleft()
if self.resources.insert_len > 0:
self.resources.insert_len -= 1
else:
self.resources.current_queue_num += 1 # 队列取出来第几个任务
self.resources.logger.log_info(f"plan_task:{self.resources.plan_task}") #startPos,endPos,pathWidth,pathCircles,pathDir,pathMode
if self.resources.plan_task[0].tolist() is None:
if self.resources.plan_task[5] == "point" or self.resources.plan_task[5] == "point_rub":
self.resources.plan_task[0] = copy.deepcopy(self.resources.plan_task[1]) # 赋值为上一步的结束位置,更新任务队列
else:
self.resources.plan_task[0] = copy.deepcopy(self.resources.motion_task[1]) # 更新任务队列
self.resources.logger.log_blue(f"修正 startPos:{self.resources.plan_task[0]}")
# 启动新线程
self.resources.logger.log_info(f"开始规划 {self.resources.plan_task} 路线")
self.resources.generate_thread = threading.Thread(target=self.resources.generate_next_points, args=(self.resources.plan_task[0],self.resources.plan_task[1],self.resources.plan_task[2],self.resources.plan_task[3]),kwargs={'direction':self.resources.plan_task[4],'path':self.resources.plan_task[5]})
self.resources.generate_thread.start()
def first_touch(self):
self.resources.logger.log_info("预处理-完成")
self.resources.send_instruction(instruction="skin_touch",target="language")
if self.resources.choose_task == "shockwave" and self.first_bodypart != "shoulder": # 修正冲击波姿态
self.target_point_list[0][3:] = copy.deepcopy(self.back_angle)
if self.resources.choose_task == "ion":
self.target_point_list[0][3:] = copy.deepcopy(self.back_angle_ion)
touch_pos = copy.deepcopy(self.target_point_list[0])
touch_pos[:3] = touch_pos[:3] + self.tf2base([0, 0, 0.03], self.resources.robot.arm.level_base)
self.resources.logger.log_blue(f"move_to_points: {touch_pos}")
if self.resources.choose_task == "shockwave" or self.resources.choose_task == "ion":
code = self.resources.robot.move_to_point(pose=touch_pos, t=self.start_time, timeout=0.5, algorithm="admithybrid",is_interrupt=False,is_switch_controller = True)
else:
code = self.resources.robot.move_to_point(pose=touch_pos, t=self.start_time, timeout=0.5, algorithm="admittance",is_interrupt=False,is_switch_controller = True)
self.process_except(code)
if self.resources.mode_real != 0 and self.resources.mode_real != 3:# 开启力位混合
code = self.resources.robot.apply_force(wrench=self.resources.robot.arm_state.massage_wrench, t=5, algorithm=self.resources.use_algorithm)
self.process_except(code)
self.resources.is_first_move = False
def skip_process(self):
self.resources.logger.log_info(f"进入跳过流程,skip_pos:{self.resources.robot.skip_pos}")
self.resources.skip_event.clear()
self.resources.robot.skip_envent.clear()
if self.resources.mode_real != 0 and self.resources.mode_real != 3:
massage_wrench = np.array([0, 0, 0, 0, 0, 0])
code = self.resources.robot.apply_force(wrench=massage_wrench, t=1, algorithm=self.resources.use_algorithm)
self.process_except(code)
if self.resources.choose_task == "shockwave":
self.resources.robot.shockwave.off()
if self.resources.choose_task == "shockwave":
code = self.resources.robot.move_to_point(pose=self.resources.robot.skip_pos, t=5, timeout=0.5, algorithm="admithybrid",is_interrupt=False,is_switch_controller = True)
else:
code = self.resources.robot.move_to_point(pose=self.resources.robot.skip_pos, t=5, timeout=0.5, algorithm="admittance",is_interrupt=False,is_switch_controller = True)
self.process_except(code)
#准备处理之前插入
if self.resources.plan_task is not None: # 准备将六维坐标转换为二维坐标方便在像素坐标系中处理
# rotation_matrix = R.from_euler(self.turn_2D, "xyz", degrees=False).as_matrix()
rotation_matrix = R.from_euler("xyz", self.turn_2D, degrees=False).as_matrix()
# 构建线性方程组:A @ P + b = Q
# 所以 A = (Q2 - Q1) @ inv(P2 - P1), b = Q1 - A @ P1
# 原空间
startPos = np.array((rotation_matrix @ self.resources.target_points[0][:3])[:2])
endPos = np.array((rotation_matrix @ self.resources.target_points[-1][:3])[:2])
# 目标空间
targetStart = np.array(copy.deepcopy(self.resources.motion_task[0]))
targetEnd = np.array(copy.deepcopy(self.resources.motion_task[1]))
# 计算向量差
dP = startPos - endPos # 差向量 (原空间)
dQ = targetStart - targetEnd # 差向量 (目标空间)
if (np.abs(dP) < 0.001).all() or (np.abs(dQ) < 0.001).all():
skipPos2D = copy.deepcopy(self.resources.motion_task[0])
else:
# 转换成列向量来构建仿射矩阵 A
A = np.outer(dQ, dP) / np.dot(dP, dP)
# 求 b
b = targetStart - A @ startPos
# 要转换的第三个点
skipPos = np.array((rotation_matrix @ self.resources.robot.skip_pos[:3])[:2])
# 计算在目标空间中的对应点
skipPos2D = A @ skipPos + b
self.resources.logger.log_info(f"skipPos在二维像素坐标系中的对应点: {skipPos2D}")
self.resources.is_jump = True
self.resources.jump_length = np.linalg.norm(self.resources.plan_task[0] - skipPos2D)
def tasks_queue_execution(self): # 开始按摩
self.resources.logger.log_info(f"切换控制方式: {self.resources.is_switch_controller}")
self.resources.logger.log_info(f"use_algorithm: {self.resources.use_algorithm}")
self.resources.logger.log_info(f"任务长度: {self.resources.massage_task_num}")
# 在单独的线程中启动 update_time 方法
self.resources.update_thread = threading.Thread(target=self.resources.update_time)
self.resources.update_thread.start()
self.resources.is_pause = False
instruction = {
"is_pause": False,
}
self.resources.send_instruction(instruction, "ui_update_massage_status")
# 获取当前时间,并格式化为字符串(例如:2025-04-01_10-00-00.json)
current_time = datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")
filename = f"{current_time}_{self.resources.choose_task}_{self.resources.body_part}_{self.resources.selected_plan}.json"
while True:
while self.resources.mode_commands_list:
# 等待生成轨迹点的线程完成,获取上一步的target_points
if self.resources.is_first_move == False:
self.wait_queue() #等待生成线程完成并预处理
self.process_queue()
if self.resources.is_first_move == False:# 执行上一步
is_skip = self.execute_movement() #按顺序执行已生成的command
self.write_to_json(filename=filename)
if self.resources.is_finish == True and self.resources.loops == 0:
self.resources.logger.log_blue(f"时间已到,完成按摩")
break
if is_skip:
self.skip_process()
else:
self.write_to_json(filename=filename)
else:
self.first_touch() #进行第一次接触
if self.resources.is_finish == True and self.resources.loops == 0:
self.resources.logger.log_blue(f"时间已到,完成按摩")
break
self.wait_queue()
# 执行最后一步
is_skip = self.execute_movement()
self.write_to_json(filename=filename)
if self.resources.is_finish == True and self.resources.loops == 0:
self.resources.logger.log_blue(f"时间已到,完成按摩")
break
if is_skip:
self.skip_process()
else:
self.write_to_json(filename=filename)
if self.resources.mode_commands_list:
self.resources.logger.log_blue(f"仍存在未执行完毕的队列:{self.resources.mode_commands_list}")
else:
if self.resources.is_finish == False and self.resources.loops == 0:
self.resources.logger.log_blue(f"队列执行完毕但时间未到,复制default给队列执行:{self.resources.mode_commands_list_default}")
self.resources.mode_commands_list = copy.deepcopy(self.resources.mode_commands_list_default)
self.resources.current_queue_num = -2
else:
self.resources.logger.log_blue(f"时间已到,完成按摩")
self.resources.is_finish = True #按摩完成标志
break
if self.resources.generate_thread is None and self.resources.insert_queue_thread is None:
self.process_queue()
if self.resources.plan_task is not None:
if self.resources.plan_task[0] is not None and not np.allclose(self.resources.plan_task[0], self.resources.motion_task[1], atol=0.1):
# 跳跃准备
self.resources.is_jump = True
self.resources.jump_length = np.linalg.norm(self.resources.plan_task[0] - self.resources.motion_task[1])
self.resources.logger.log_yellow("重新进入循环执行队列")
if self.resources.is_skip == False:#完成按摩
massage_wrench = np.array([0, 0, 0, 0, 0, 0])
code = self.resources.robot.apply_force(wrench=massage_wrench, t=1, algorithm= "hybrid")
self.process_except(code)
if self.resources.choose_task == "shockwave":
self.resources.robot.shockwave.off() # 关闭冲击波
up_target_point = copy.deepcopy(self.resources.target_points[-1])
up_target_point[:3] = up_target_point[:3] + self.tf2base([0, 0, -0.10], up_target_point[3:])
self.resources.logger.log_blue(f"move_to_points: {up_target_point}")
if self.resources.choose_task == "shockwave" or self.resources.choose_task == "ion":
code = self.resources.robot.move_to_point(pose=up_target_point, t=1.5, timeout=0.5, algorithm="admithybrid", is_interrupt=False,is_switch_controller = True)
else:
code = self.resources.robot.move_to_point(pose=up_target_point, t=1.5, timeout=0.5, algorithm="admittance", is_interrupt=False,is_switch_controller = True)
self.process_except(code)
self.resources.is_skip = False
instruction = {"progress": "100"}
self.resources.send_instruction(instruction, "ui_update_massage_status")
self.resources.init_queue_para() #初始化队列参数
class Thermotherapy_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Thermotherapy_Task, self).__init__(resources)
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Thermotherapy_Task")
self.move_to_startpos()
self.resources.logger.log_info('启动热疗按摩')
# self.resources.robot.thermotherapy.set_working_status(cur_level=self.resources.gear, heat_level=self.resources.temperature, vib_level=self.resources.shake)
self.tasks_queue_execution() # 按照任务队列执行任务
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Shockwave_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Shockwave_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.max_radius_st = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Shockwave_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务
return "stop"
except PauseWorkflow:
if self.resources.robot.shockwave is not None:
self.resources.robot.shockwave.off() # 关闭冲击波
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
if self.resources.robot.shockwave is not None:
self.resources.robot.shockwave.off() # 关闭冲击波
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
if self.resources.robot.shockwave is not None:
self.resources.robot.shockwave.off() # 关闭冲击波
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
return "emergency"
class Ball_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Ball_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.max_radius_st = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Ball_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Stone_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Stone_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.max_radius_st = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Stone_Task")
self.move_to_startpos()
self.resources.logger.log_info('启动砭石按摩')
self.resources.robot.stone.set_working_status(heat_level=self.resources.temperature, speed_level=self.resources.speed , rotate_direction=self.resources.direction)
self.tasks_queue_execution() # 按照任务队列执行任务
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Finger_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Finger_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.max_radius_st = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Finger_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务s
return "stop"
except PauseWorkflow:
time.sleep(0.5)
self.resources.motion_task = None
self.resources.plan_task = None
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
return "emergency"
class Heat_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Heat_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.max_radius_st = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Heat_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务s
if self.resources.robot.heat is not None:
self.resources.robot.heat.power_off()
return "stop"
except PauseWorkflow:
if self.resources.robot.heat is not None:
self.resources.robot.heat.power_off()
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
if self.resources.robot.heat is not None:
self.resources.robot.heat.power_off()
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
if self.resources.robot.heat is not None:
self.resources.robot.heat.power_off()
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Roller_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Roller_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Roller_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务s
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Spheres_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Spheres_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Spheres_Task")
self.move_to_startpos()
self.tasks_queue_execution() # 按照任务队列执行任务s
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.init_queue_para()
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
time.sleep(0.5)
return "emergency"
class Ion_Task(TaskBase):
def __init__(self, resources: SharedResources):
super(Ion_Task, self).__init__(resources)
# 存储测试使用时间
self.time = 0
self.last_point = np.array([]) # 储存上个路径最后一个点
self.stop_ion = threading.Event()
self.stop_ion.clear()
self.ion_thread = None
def ion_update_dis(self):
try:
countDanger = 0
while not self.stop_ion.is_set():
# 获取数据并进行处理
# 处理烫伤风险
if self.resources.robot.ion.is_reconnect == False:
self.resources.robot.arm_state.positionerSensorData = self.resources.robot.ion.ver_dis_kalman
if self.resources.robot.arm_state.positionerSensorData < 0.015 and self.resources.robot.arm_state.positionerSensorData > 0:
countDanger += 1
else:
countDanger = 0
else:
self.resources.robot.arm_state.positionerSensorData = 0
countDanger = 0
if countDanger > 10:
# 当风险超过阈值时发送警告
self.resources.send_instruction(instruction={"message": "检测到烫伤的风险
机器人停止按摩"}, target="ui_on_message")
self.stop_ion.set()
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off() # 确保异常发生时关闭设备
self.resources.robot.ion.stop_monitoring()
self.resources.stop_event.set()
self.resources.robot.interrupt_event.set()
break
time.sleep(0.05) # 延时防止CPU占用过高
except Exception as e: # 捕获所有其他异常
print(f"捕获到异常: {e}")
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off() # 确保异常发生时关闭设备
self.resources.robot.ion.stop_monitoring()
self.stop_ion.set()
self.resources.stop_event.set()
self.resources.robot.interrupt_event.set()
finally:
# 无论是否发生异常,都会执行的清理操作
print("退出 ion_update_dis 线程")
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off() # 确保设备关闭
self.resources.robot.ion.stop_monitoring()
def execute(self, userdata):
try:
self.resources.logger.log_info("Executing Stone_Task")
self.move_to_startpos()
# self.resources.robot.ion.power_on()
self.resources.robot.ion.start_monitoring(0.05)
self.resources.robot.ion.set_working_status(goal_state=self.resources.temperature)
self.ion_thread = threading.Thread(target=self.ion_update_dis)
self.ion_thread.start()
self.tasks_queue_execution() # 按照任务队列执行任务s
self.stop_ion.set()
self.ion_thread.join()
self.ion_thread = None
self.stop_ion.clear()
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
return "stop"
except PauseWorkflow:
self.resources.motion_task = None
self.resources.plan_task = None
self.stop_ion.set() # 设置事件,通知线程停止
# 等待线程结束
if self.ion_thread is not None:
self.ion_thread.join()
self.ion_thread = None
self.stop_ion.clear()
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
time.sleep(0.5)
time.sleep(0.5)
return "pause"
except StopWorkflow:
self.stop_ion.set() # 设置事件,通知线程停止
# 等待线程结束
if self.ion_thread is not None:
self.ion_thread.join()
self.ion_thread = None
self.stop_ion.clear()
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
self.resources.init_queue_para()
time.sleep(0.5)
return "stop"
except EmergencyStopWorkflow:
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.send_instruction({"progress": "0"}, "ui_update_massage_status")
self.stop_ion.set() # 设置事件,通知线程停止
# 等待线程结束
if self.ion_thread is not None:
self.ion_thread.join()
self.ion_thread = None
self.stop_ion.clear()
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
self.resources.init_queue_para()
time.sleep(0.5)
return "emergency"
class Idle(smach.State):
def __init__(self, resources: SharedResources):
smach.State.__init__(self, outcomes=["to_manual","to_smart"])
"""空闲状态下不进行移动,可以停在任意位置,等待开始信号"""
self.resources = resources
def execute(self, userdata):
self.resources.logger.log_info("Executing state idle")
self.resources.logger.log_info("等待开始")
self.resources.stop_event.clear()
self.resources.begin_event.wait()
self.resources.begin_event.clear()
time.sleep(0.5)
self.resources.logger.log_info(
"now_process_task:" + str(self.resources.choose_task)
)
self.resources.send_instruction({"is_massaging": True}, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
if self.resources.use_mode == "manual":
return "to_manual"
else:
return "to_smart"
class Stop(smach.State):
def __init__(self, resources: SharedResources):
"""停止状态下自动返回初始位置,然后进入空闲状态"""
smach.State.__init__(self, outcomes=["to_idle", "emergency"])
self.resources = resources
def execute(self, userdata):
self.resources.logger.log_info("Executing state stop")
# 获取当前时间并格式化为指定格式
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构建按摩头数据
instruction = {
"stop_time": current_time,
"head_type": self.resources.choose_task,
"body_part": self.resources.body_part
}
self.resources.send_instruction(instruction, target="iot_massage_head")
self.resources.is_finish = True
self.resources.send_instruction("run_stop", "language")
if self.resources.tool_cam:
self.resources.tool_cam.stop()
self.resources.get_picture = True
self.resources.cal_acu = False
self.resources.manual_start = False
self.resources.selected_plan = None
if self.resources.update_thread is not None:
self.resources.update_thread.join()
self.resources.update_thread = None
self.resources.is_acupoint = False #重置穴位展示标示位
self.resources.progress = 0
self.resources.current_part = None
self.resources.task_time = 0
self.resources.current_time = 0
self.resources.loops = 0
self.resources.start_time = 0
self.resources.body_part = None
self.resources.mode_real = 1
self.resources.use_mode = "smart"
self.resources.show_image_num = 0
self.resources.massage_task_num = 0
self.resources.move_count = 0
self.resources.motion_count = 0
self.resources.insert_len = 0
self.resources.motion_task = None
self.resources.plan_task = None
self.resources.mode_commands_list = deque([])
self.resources.mode_commands_list_default = deque([])
self.resources.is_first_move = True
self.resources.is_jump = False
self.resources.is_skip = False
self.resources.is_insert = False
self.resources.current_queue_num = -2
self.resources.target_points = None
self.resources.robot.arm_state.massage_wrench = np.zeros(6)
self.resources.temperature = self.resources.temperature_default
self.resources.gear = self.resources.gear_default
self.resources.shake = self.resources.shake_default
self.resources.press = self.resources.press_default
self.resources.frequency = self.resources.frequency_default
self.resources.speed = self.resources.speed_default
self.resources.direction = self.resources.direction_default
if self.resources.robot.controller_manager.current_controller.name == "hybrid":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=1, algorithm="hybrid"
)
if code == 3:
return "emergency"
elif self.resources.robot.controller_manager.current_controller.name == "hybridPid":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=1, algorithm="hybridPid"
)
if code == 3:
return "emergency"
elif self.resources.robot.controller_manager.current_controller.name == "hybridAdmit":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=1, algorithm="hybridAdmit"
)
if code == 3:
return "emergency"
self.resources.robot.is_waitting = True
time.sleep(1)
self.resources.robot.arm.disable_servo()
self.resources.robot.arm.disable_servo()
self.resources.robot.stop_thread()
if self.resources.next_points is not None:
time.sleep(1)
position,quat_rot = self.resources.robot.arm.get_arm_position()
time.sleep(0.2)
target_point = np.zeros(6)
target_point[:3] = copy.deepcopy(position)
target_point[3:] = copy.deepcopy(R.from_quat(quat_rot).as_euler('xyz',degrees=False))
# target_point[:3] = copy.deepcopy(self.resources.robot.arm_state.arm_position_command)
# target_point[3:] = copy.deepcopy(R.from_quat(self.resources.robot.arm_state.arm_orientation_command).as_euler('xyz',degrees=False))
# target_point[3:] = [150 * (np.pi / 180), 0, 180 * (np.pi / 180)]
target_point[:3] = target_point[:3] + self.resources.tf2base([0, 0, -0.10], self.resources.robot.arm.level_base)
self.resources.logger.log_blue(f"set_position: {target_point},{position}")
code = self.resources.robot.set_position(pose=target_point,is_wait=True)
self.resources.next_points = None
time.sleep(1)
pose = copy.deepcopy(self.resources.robot.arm.standby_pos)
self.resources.logger.log_yellow("self.resources.robot.arm.disable_servo()")
time.sleep(1)
if not self.resources.is_pause:
# self.resources.robot.arm.move_joint(pose)
code = self.resources.robot.set_joint(pose)
if code == -1:
self.resources.logger.log_error(f"运动到pose:{pose}位置失败")
return "emergency"
pose = copy.deepcopy(self.resources.robot.arm.init_pos)
time.sleep(0.1)
if not self.resources.is_pause:
# self.resources.robot.arm.move_joint(pose)
code = self.resources.robot.set_joint(pose)
if code == -1:
self.resources.logger.log_error(f"运动到pose:{pose}位置失败")
return "emergency"
time.sleep(1)
if self.resources.choose_task == "thermotherapy":
self.resources.logger.log_info('关闭热疗按摩')
if self.resources.robot.thermotherapy is not None:
for _ in range(3): self.resources.robot.thermotherapy.stop_process(); time.sleep(0.5)
elif self.resources.choose_task == "shockwave":
self.resources.logger.log_info('关闭点阵按摩')
if self.resources.robot.shockwave is not None:
for _ in range(3): self.resources.robot.shockwave.off(); time.sleep(0.5)
elif self.resources.choose_task == "stone":
self.resources.logger.log_info('关闭砭石按摩')
if self.resources.robot.stone is not None:
for _ in range(3): self.resources.robot.stone.power_off(); time.sleep(0.5)
elif self.resources.choose_task == "heat":
self.resources.logger.log_info('关闭能量按摩')
if self.resources.robot.heat is not None:
for _ in range(3): self.resources.robot.heat.power_off(); time.sleep(0.5)
elif self.resources.choose_task == "ion":
self.resources.logger.log_info('关闭负离子按摩')
if self.resources.robot.ion is not None:
for _ in range(3):
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
time.sleep(0.5)
self.resources.choose_task = None
self.resources.robot.thermotherapy = None
self.resources.robot.shockwave = None
self.resources.robot.stone = None
self.resources.robot.heat = None
del self.resources.robot.ion
self.resources.robot.ion = None
if self.resources.update_massageHead_temper_thread is not None:
self.resources.update_massageHead_temper_thread.join()
self.resources.update_massageHead_temper_thread = None
self.resources.temper_head = None
self.resources.robot.arm_state.desired_high = self.resources.desired_high_default
self.resources.robot.is_waitting = False
self.resources.send_instruction("finish", "language")
self.resources.robot.stop()
self.resources.stop_event.clear()
self.resources.pause_event.clear()
self.resources.skip_event.clear()
self.resources.is_pause = False
self.resources.is_acupoint = False
instruction = {}
instruction["is_massaging"] = (False)
instruction["is_acupoint"] = (False)
if self.resources.manual_stage != 0:
self.resources.manual_stage = 0
instruction["manual_stage"] = self.resources.manual_stage
self.resources.send_instruction(instruction, "ui_update_massage_status")
self.resources.send_instruction({"is_pause": False}, "ui_update_massage_status")
self.resources.logger.log_info("Massage stopped!")
return "to_idle"
class Pause(smach.State):
def __init__(self, resources: SharedResources):
"""停止状态下自动返回初始位置,然后进入空闲状态"""
smach.State.__init__(self, outcomes=["to_smart", "to_manual", "stop", "emergency"])
self.resources = resources
def execute(self, userdata):
self.resources.logger.log_info("Executing state pause")
self.resources.is_finish = True
self.resources.send_instruction(instruction="pause_massage", target="language")
print("zhixing dao duilie de di jige renwu:",self.resources.current_queue_num)
print("renwuduilie:",self.resources.mode_commands_list)
self.resources.is_acupoint = False
if self.resources.update_thread is not None:
self.resources.update_thread.join()
self.resources.update_thread = None
self.resources.send_instruction("run_pause", "language")
if self.resources.tool_cam:
self.resources.tool_cam.stop()
self.resources.get_picture = True
self.resources.cal_acu = False
self.resources.manual_start = False
self.resources.is_first_move = True
self.resources.is_jump = False
self.resources.is_skip = False
self.resources.is_insert = False
self.resources.manual_start = False
self.resources.acupuncture_dict = None
self.resources.manual_stage = 1
self.resources.insert_len = 0
self.resources.motion_task = None
self.resources.plan_task = None
self.resources.mode_commands_list = deque([])
self.resources.target_points = None
if self.resources.robot.controller_manager.current_controller.name == "hybrid":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=0, algorithm="hybrid"
)
if code == 3:
return "emergency"
elif self.resources.robot.controller_manager.current_controller.name == "hybridPid":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=0, algorithm="hybridPid"
)
if code == 3:
return "emergency"
elif self.resources.robot.controller_manager.current_controller.name == "hybridAdmit":
code = self.resources.robot.apply_force(
wrench=np.zeros(6), t=1, algorithm="hybridAdmit"
)
if code == 3:
return "emergency"
self.resources.robot.is_waitting = True
time.sleep(1)
self.resources.robot.arm.disable_servo()
self.resources.robot.stop_thread()
if self.resources.next_points is not None:
time.sleep(1)
position,quat_rot = self.resources.robot.arm.get_arm_position()
time.sleep(0.2)
target_point = np.zeros(6)
target_point[:3] = copy.deepcopy(position)
target_point[3:] = copy.deepcopy(R.from_quat(quat_rot).as_euler('xyz',degrees=False))
# target_point[:3] = copy.deepcopy(self.resources.robot.arm_state.arm_position_command)
# target_point[3:] = copy.deepcopy(R.from_quat(self.resources.robot.arm_state.arm_orientation_command).as_euler('xyz',degrees=False))
# target_point[3:] = [150 * (np.pi / 180), 0, 180 * (np.pi / 180)]
target_point[:3] = target_point[:3] + self.resources.tf2base([0, 0, -0.15], self.resources.robot.arm.level_base)
self.resources.logger.log_blue(f"set_position: {target_point}")
code = self.resources.robot.set_position(pose=target_point,is_wait=True)
self.resources.next_points = None
time.sleep(1)
pose = copy.deepcopy(self.resources.robot.arm.standby_pos)
time.sleep(0.1)
# self.resources.robot.arm.move_joint(pose)
code = self.resources.robot.set_joint(pose)
if code == -1:
self.resources.logger.log_error(f"运动到pose:{pose}位置失败")
return "emergency"
pose = copy.deepcopy(self.resources.robot.arm.init_pos)
time.sleep(0.1)
# self.resources.robot.arm.`move_joint`(pose)
code = self.resources.robot.set_joint(pose)
if code == -1:
self.resources.logger.log_error(f"运动到pose:{pose}位置失败")
return "emergency"
time.sleep(1)
if self.resources.choose_task == "thermotherapy":
self.resources.logger.log_info('关闭热疗按摩')
if self.resources.robot.thermotherapy is not None:
for _ in range(3): self.resources.robot.thermotherapy.stop_process(); time.sleep(0.5)
elif self.resources.choose_task == "shockwave":
self.resources.logger.log_info('关闭点阵按摩')
if self.resources.robot.shockwave is not None:
for _ in range(3): self.resources.robot.shockwave.off(); time.sleep(0.5)
elif self.resources.choose_task == "stone":
self.resources.logger.log_info('关闭砭石按摩')
if self.resources.robot.stone is not None:
for _ in range(3): self.resources.robot.stone.power_off(); time.sleep(0.5)
elif self.resources.choose_task == "ion":
self.resources.logger.log_info('关闭负离子按摩')
if self.resources.robot.ion is not None:
for _ in range(3):
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
time.sleep(0.5)
self.resources.robot.thermotherapy = None
self.resources.robot.shockwave = None
self.resources.robot.stone = None
self.resources.robot.heat = None
del self.resources.robot.ion
self.resources.robot.ion = None
self.resources.robot.arm_state.desired_high = self.resources.desired_high_default
self.resources.robot.is_waitting = False
self.resources.send_instruction("pause", "language")
self.resources.robot.stop()
self.resources.is_pause = True
self.resources.send_instruction({"is_pause": True}, "ui_update_massage_status")
self.resources.send_instruction({"is_massaging": False}, "ui_update_massage_status")
self.resources.logger.log_info("Massage pause!")
self.resources.pause_event.clear()
self.resources.skip_event.clear()
while True:
if self.resources.stop_event.is_set():
return "stop"
print("now_begin_envent:",self.resources.begin_event.is_set())
if self.resources.begin_event.is_set():
self.resources.logger.log_info("From state pause to restart manual or cv")
self.resources.begin_event.clear()
time.sleep(0.5)
# self.resources.robot.start()
self.resources.logger.log_info(
"now_process_task:" + str(self.resources.choose_task)
)
self.resources.send_instruction({"is_massaging": True}, "ui_update_massage_status")
self.resources.send_instruction(instruction="pause_resume", target="language")
if self.resources.use_mode == "manual":
return "to_manual"
else:
self.resources.manual_stage = 1
self.resources.send_instruction({"manual_stage": self.resources.manual_stage}, "ui_update_massage_status")
return "to_smart"
time.sleep(0.5)
class EmergencyExitState(smach.State):
def __init__(self, resources: SharedResources):
smach.State.__init__(self, outcomes=["emergency_exit"])
self.resources = resources
def execute(self, userdata):
instruction = {
"message": "紧急停止",
}
self.resources.send_instruction(instruction, "ui_on_message")
instruction = {
"is_massaging": False,
"is_pause": False,
"massage_service_started": False,
"progress": "0",
"is_acupoint": False,
}
if self.resources.manual_stage != 0:
self.resources.manual_stage = 0
self.resources.is_finish = True
instruction["manual_stage"] = self.resources.manual_stage
if self.resources.choose_task == "thermotherapy":
self.resources.logger.log_info('关闭热疗按摩')
if self.resources.robot.thermotherapy is not None:
for _ in range(3): self.resources.robot.thermotherapy.stop_process(); time.sleep(0.5)
elif self.resources.choose_task == "shockwave":
self.resources.logger.log_info('关闭点阵按摩')
if self.resources.robot.shockwave is not None:
for _ in range(3): self.resources.robot.shockwave.off(); time.sleep(0.5)
elif self.resources.choose_task == "stone":
self.resources.logger.log_info('关闭砭石按摩')
if self.resources.robot.stone is not None:
for _ in range(3): self.resources.robot.stone.power_off(); time.sleep(0.5)
elif self.resources.choose_task == "heat":
self.resources.logger.log_info('关闭能量按摩')
if self.resources.robot.heat is not None:
for _ in range(3): self.resources.robot.heat.power_off(); time.sleep(0.5)
elif self.resources.choose_task == "ion":
self.resources.logger.log_info('关闭负离子按摩')
if self.resources.robot.ion is not None:
for _ in range(3):
self.resources.robot.arm_state.positionerSensorData = 0
self.resources.robot.ion.power_off()
self.resources.robot.ion.stop_monitoring()
time.sleep(0.5)
if self.resources.update_thread is not None:
self.resources.update_thread.join()
self.resources.update_thread = None
if self.resources.tool_cam:
self.resources.tool_cam.stop()
self.resources.task_time = 0
self.resources.current_time = 0
self.resources.loops = 0
self.resources.get_picture = True
self.resources.cal_acu = False
self.resources.manual_start = False
self.resources.selected_plan = None
self.resources.show_image_num = 0
self.resources.massage_task_num = 0
self.resources.move_count = 0
self.resources.motion_count = 0
self.resources.insert_len = 0
self.resources.next_points = None
self.resources.motion_task = None
self.resources.plan_task = None
self.resources.mode_commands_list = deque([])
self.resources.target_points = None
self.resources.is_first_move = True
self.resources.is_jump = False
self.resources.is_skip = False
self.resources.is_insert = False
self.resources.current_queue_num = -2
self.resources.is_acupoint = False #重置穴位展示标志
self.resources.robot.arm_state.massage_wrench = np.zeros(6)
self.resources.temperature = self.resources.temperature_default
self.resources.gear = self.resources.gear_default
self.resources.shake = self.resources.shake_default
self.resources.press = self.resources.press_default
self.resources.frequency = self.resources.frequency_default
self.resources.speed = self.resources.speed_default
self.resources.direction = self.resources.direction_default
self.resources.robot.arm_state.desired_high = self.resources.desired_high_default
self.resources.robot.thermotherapy = None
self.resources.robot.shockwave = None
self.resources.robot.stone = None
self.resources.robot.heat = None
del self.resources.robot.ion
self.resources.robot.ion = None
self.resources.choose_task = None
if self.resources.update_massageHead_temper_thread is not None:
self.resources.update_massageHead_temper_thread.join()
self.resources.update_massageHead_temper_thread = None
self.resources.temper_head = None
self.resources.is_pause = False
self.resources.send_instruction(instruction, "ui_update_massage_status")
self.resources.send_instruction(instruction="emergency_stop", target="language")
# 这里可以处理一些清理工作
self.resources.logger.log_error("Emergency exit triggered, shutting down...")
self.resources.robot.stop_thread()
self.resources.robot.stop()
# self.resources.robot.arm.power_off()
self.resources.stop_event.clear()
# 或者使用 sys.exit() 直接退出程序
# sys.exit(1)
return "emergency_exit"
async def handler(websocket, path, resources: SharedResources):
async for message in websocket:
resources.logger.log_info("received: " + message)
resources.logger.log_info("now_state: " + resources.now_state)
if (
message == "stop"
and resources.now_state != "STOP"
and resources.now_state != "IDLE"
):
resources.stop_event.set()
resources.robot.user_interrupt()
if (
message == "pause"
and resources.now_state != "STOP"
and resources.now_state != "PAUSE"
and resources.now_state != "IDLE"
):
if resources.now_state == "SMART_DEAL" or resources.now_state == "MANUAL_DEAL":
resources.send_instruction(instruction="no_pause", target="language")
resources.send_instruction(instruction={"message": "我现在在拍照呢
暂时不可以暂停哦"}, target="ui_on_message")
else:
if resources.is_finish == False:
resources.pause_event.set()
resources.robot.user_pause()
elif resources.is_finish == True:
resources.send_instruction(instruction={"message": "按摩已经结束啦
马上就要停止了哦"}, target="ui_on_message")
elif (
message == "skip_queue"
and resources.now_state != "STOP"
and resources.now_state != "PAUSE"
and resources.now_state != "IDLE"
and resources.now_state != "SMART_DEAL"
and resources.now_state != "MANUAL_DEAL"
and not resources.skip_event.set()
and resources.robot.is_execute == True
):
if resources.is_finish == False and resources.motion_task is not None:
resources.send_instruction(instruction="skip_queue", target="language")
print("skip_queue,待测试")
resources.is_skip = True #跳过标志位置为True
resources.skip_event.set()
resources.robot.user_skip()
elif "begin" in message and (resources.now_state == "IDLE" or resources.now_state == "PAUSE"):
resources.send_instruction("check_license",target="check_license")
if resources.license_check == True:
resources.stop_event.clear()
resources.robot.interrupt_event.clear()
resources.begin_event.set()
parts = message.split(":")
print(f"begin command: {parts}")
resources.robot.current_head = parts[1] + "_head"
resources.choose_task = parts[1]
resources.body_part = parts[3]
resources.mode_real = int(parts[4])
if resources.is_pause == False:
resources.task_time = int(parts[2])
resources.show_picture_type = 1
# 检查是否提供 use_mode,若没有则默认 "smart"
resources.use_mode = parts[5] if len(parts) > 5 else "smart"
print("change_resources.begin_event.:",resources.begin_event.is_set())
resources.logger.log_blue((
f"message: {message} , choose_task: {resources.choose_task} , "
f"task_time: {resources.task_time}, body_part: {resources.body_part}, "
f"mode_real: {resources.mode_real}, use_mode: {resources.use_mode}"
f"loops: {resources.loops}"
))
elif "begin" in message and resources.now_state == "MANUAL_DEAL" and resources.manual_start == False:
resources.send_instruction("check_license",target="check_license")
if resources.license_check == True:
resources.stop_event.clear()
resources.robot.interrupt_event.clear()
parts = message.split(":")
print(f"begin command: {parts}")
resources.robot.current_head = parts[1] + "_head"
resources.choose_task = parts[1]
if resources.is_pause == False:
resources.task_time = int(parts[2])
resources.body_part = parts[3]
resources.mode_real = int(parts[4])
resources.use_mode = parts[5]
resources.get_picture = True
resources.selected_plan = None
resources.show_picture_type = 1
resources.logger.log_blue((
f"message: {message} , choose_task: {resources.choose_task} , "
f"task_time: {resources.task_time}, body_part: {resources.body_part}, "
f"mode_real: {resources.mode_real}, use_mode: {resources.use_mode}"
))
elif (
"adjust" in message
# and resources.now_state != "STOP"
# and resources.now_state != "IDLE"
):
resources.adjust_event.set()
adjust_type = message.split(":")[1]
adjust_command = message.split(":")[2]
adjust_degree = message.split(":")[3]
resources.logger.log_info("adjust_type: " + adjust_type)
if adjust_type == "pose":
position,quat_rot = resources.robot.arm.get_arm_position()
resources.position_increment = position_adjust(
adjust_command, adjust_degree, quat_rot
)
resources.logger.log_info(
"processing adjust with position_increment: "
+ str(resources.position_increment)
)
resources.robot.user_adjust(
pose_increment=resources.position_increment
) # 修改当前点的位置,并且在任务重处理后续点的位置
elif adjust_type == "force":
# resources.logger.log_info(
# f"if adjust to force: {resources.robot.is_execute}"
# )
# if resources.is_execute:
resources.robot.arm_state.massage_wrench = force_adjust(
adjust_command,
adjust_degree,
resources.robot.arm_state.massage_wrench,
resources.force_limit,
)
resources.logger.log_info(
f"processing adjust to force: {resources.robot.arm_state.massage_wrench}"
)
resources.send_instruction(
{"force": str(-resources.robot.arm_state.massage_wrench[2])}, "ui_update_massage_status"
)
resources.robot.user_adjust(
force=resources.robot.arm_state.massage_wrench
) # 直接修改力度大小,不在任务中处理
elif adjust_type == "temperature":
resources.temperature = temperature_adjust(adjust_command,adjust_degree,resources.temperature,resources.temperature_limit)
resources.logger.log_info(
f"processing adjust to force: {resources.temperature}"
)
resources.send_instruction(
{"temperature": str(resources.temperature)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
temperature=resources.temperature
)
if adjust_type == "speed":
resources.speed = stone_speed_adjust(adjust_command,adjust_degree,resources.speed,resources.speed_limit)
resources.logger.log_info(
f"processing adjust to stone speed: {resources.speed}"
)
resources.send_instruction(
{"speed": str(resources.speed)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
speed=resources.speed
)
if adjust_type == "direction":
if resources.direction == 1:
resources.direction = 0
else:
resources.direction = 1
resources.logger.log_info(
f"processing adjust to stone dircetion: {resources.direction}"
)
resources.send_instruction(
{"direction": str(resources.direction)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
direction=resources.direction
)
elif adjust_type == "gear":
resources.gear = gear_adjust(adjust_command,adjust_degree,resources.gear,resources.gear_limit)
resources.logger.log_info(
f"processing adjust to gear: {resources.gear}"
)
resources.send_instruction(
{"gear": str(resources.gear)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
gear=resources.gear
)
elif adjust_type == "shake":
resources.shake = shake_adjust(adjust_command,adjust_degree,resources.shake,resources.shake_limit)
resources.logger.log_info(
f"processing adjust to shake: {resources.shake}"
)
resources.send_instruction(
{"shake": str(resources.shake)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
shake=resources.shake
)
elif adjust_type == "press":
resources.press = press_adjust(adjust_command,adjust_degree,resources.press,resources.press_limit)
resources.logger.log_info(
f"processing adjust to press: {resources.press}"
)
resources.send_instruction(
{"press": str(resources.press)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
press=resources.press
)
elif adjust_type == "frequency":
resources.frequency = frequency_adjust(adjust_command,adjust_degree,resources.frequency,resources.frequency_limit)
resources.logger.log_info(
f"processing adjust to frequency: {resources.frequency}"
)
resources.send_instruction(
{"frequency": str(resources.frequency)}, "ui_update_massage_status"
)
resources.robot.user_adjust(
frequency=resources.frequency
)
elif adjust_type == "high":
resources.robot.arm_state.desired_high = high_adjust(adjust_command,resources.robot.arm_state.desired_high,resources.high_limit)
resources.logger.log_info(
f"processing adjust to high: {resources.robot.arm_state.desired_high}"
)
resources.send_instruction(
{"high": str(round(resources.robot.arm_state.desired_high * 100.0))}, "ui_update_massage_status"
)
elif (
message == "insert_queue"
and resources.now_state != "STOP"
and resources.now_state != "PAUSE"
and resources.now_state != "IDLE"
and resources.now_state != "SMART_DEAL"
and resources.now_state != "MANUAL_DEAL"
and resources.is_insert != True
and resources.robot.is_execute == True
):
if resources.is_finish == False and resources.motion_task is not None:
resources.send_instruction(instruction="massage_time_longer", target="language")
resources.is_insert = True #插入队列标志位置为True
resources.insert_queue_thread = threading.Thread(target=resources.process_insert_queue)
resources.insert_queue_thread.start()
if "get_status" in message or "adjust" in message:
instruction = {}
instruction["massage_service_started"] = True
instruction["is_massaging"] = (
True if (resources.now_state != "IDLE" and resources.now_state != "PAUSE") else False
)
instruction["manual_stage"] = resources.manual_stage
instruction["massage_head"] = (
resources.robot.current_head if resources.now_state != "IDLE" else False
)
if resources.now_state != "IDLE":
instruction["progress"] = str(resources.progress)
resources.logger.log_error(
"processing get_status with progress: " + str(resources.progress)
)
instruction["force"] = str(-resources.robot.arm_state.massage_wrench[2])
instruction["current_head"] = resources.robot.current_head
instruction["temperature"] = resources.temperature
instruction["temper_head"] = None
instruction["speed"] = resources.speed
instruction["direction"] = resources.direction
instruction["gear"] = resources.gear
instruction["shake"] = resources.shake
instruction["press"] = resources.press
instruction["high"] = round(resources.robot.arm_state.desired_high * 100)
instruction["frequency"] = resources.frequency
instruction["task_time"] = str(resources.task_time)
instruction["current_time"] = str(resources.current_time)
instruction["loops"] = str(resources.loops)
instruction["body_part"] = resources.body_part
instruction["mode_real"] = resources.mode_real
instruction["massage_state"] = resources.now_state
instruction["is_acupoint"] = resources.is_acupoint
instruction["use_mode"] = resources.use_mode
instruction["is_pause"] = resources.is_pause
instruction["start_pos"] = resources.motion_task[9] if resources.motion_task is not None else None
instruction["end_pos"] = resources.motion_task[10] if resources.motion_task is not None else None
instruction["massage_path"] = resources.motion_task[5] if resources.motion_task is not None else None
else:
instruction["progress"] = "0"
instruction["force"] = "0"
instruction["temperature"] = resources.temperature
instruction["temper_head"] = None
instruction["speed"] = resources.speed
instruction["direction"] = resources.direction
instruction["gear"] = resources.gear
instruction["shake"] = resources.shake
instruction["press"] = resources.press
instruction["high"] = ""
instruction["frequency"] = resources.frequency
instruction["current_head"] = ""
instruction["task_time"] = 0
instruction["current_time"] = 0
instruction["loops"] = 0
instruction["body_part"] = ""
instruction["mode_real"] = 1
instruction["massage_state"] = resources.now_state
instruction["is_acupoint"] = False
instruction["use_mode"] = "smart"
instruction["is_pause"] = resources.is_pause
instruction["start_pos"] = None
instruction["end_pos"] = None
instruction["massage_path"] = ""
resources.logger.log_yellow(instruction)
resources.send_instruction(f"massage_status:{str(instruction)}", "language")
resources.send_instruction(instruction, "ui_update_massage_status")
elif "get_picture" in message and resources.now_state == "MANUAL_DEAL":
resources.get_picture = True
resources.show_picture_type = 2
resources.logger.log_info("processing get_picture")
elif "cal_acu" in message and resources.now_state == "MANUAL_DEAL":
try:
# 检查 cal_acu 是否为 None
if message.split(":")[1] == "None":
resources.logger.log_info("Entering automatic recognition mode.")
# 在此处添加进入自动识别模式的逻辑
resources.cal_acu = True
resources.show_picture_type = 3
else:
# 解析点的坐标
points_data = message.split(":")[1:]
# 检查是否为新格式数据(包含额外参数)
if len(points_data) < 10 and len(points_data) != 16: # 检查是否包含 10 个数值(5 个点,每点 x 和 y)
resources.logger.log_error(f"Invalid points data format: {points_data}")
else:
# 将点解析为 [[x1, y1], [x2, y2], ...] 的格式
if resources.body_part == "back" or resources.body_part == "shoulder" or resources.body_part == "back_shoulder" or resources.body_part == "waist":
points = [[int(points_data[i]), int(points_data[i + 1])] for i in range(0, len(points_data), 2)]
resources.acupoint_key_data = {"shapes":[{"label": "back","points": [points[1],points[2],points[3],points[4]],"shape_type": "polygon"},
{"label": "back_center","points": [points[0]],"shape_type": "point"}]}
elif resources.body_part == "belly":
points = [[int(points_data[i]), int(points_data[i + 1])] for i in range(0, len(points_data), 2)]
resources.acupoint_key_data = {"shapes":[{"label": "fubu","points": [points[1],points[2],points[3],points[4]],"shape_type": "polygon"},
{"label": "duqiyan","points": [points[0]],"shape_type": "point"}]}
elif resources.body_part == "leg":
# 提取前12个值作为点坐标(6个点)
points = [[int(points_data[i]), int(points_data[i + 1])] for i in range(0, min(12, len(points_data)), 2)]
# 提取可能的额外参数(如曲率等)
extra_params = [round(float(val)*0.5) for val in points_data[12:]] if len(points_data) > 12 else []
# 为leg身体部位创建新的JSON结构
resources.acupoint_key_data = {
"shapes": [
{
"label": "C1",
"points": [
[points[0][0], points[0][1]],
[points[1][0], points[1][1]],
[points[2][0], points[2][1]]
],
"curvatures": {
"thigh": extra_params[0],
"calf": -extra_params[1]
},
"shape_type": "linestrip"
},
{
"label": "C2",
"points": [
[points[3][0], points[3][1]],
[points[4][0], points[4][1]],
[points[5][0], points[5][1]]
],
"curvatures": {
"thigh": extra_params[2],
"calf": -extra_params[3]
},
"shape_type": "linestrip"
}
]
}
resources.logger.log_info(f"processing cal_acu with data: {resources.acupoint_key_data}")
# 设置标志
resources.cal_acu = True
resources.show_picture_type = 3
except Exception as e:
resources.logger.log_error(f"processing cal_acu with error: {e}")
elif "manual_start" in message and resources.now_state == "MANUAL_DEAL":
resources.get_picture = False
resources.cal_acu = False
resources.manual_start = True
resources.show_picture_type = 4
parts = message.split(":")
if resources.is_pause == False:
resources.task_time = int(parts[1])
resources.loops = int(parts[2])
resources.logger.log_blue("taske_time:",parts,resources.task_time)
resources.logger.log_blue("loops:",parts,resources.loops)
resources.logger.log_info("manual_start")
elif "select_plan" in message and resources.now_state == "MANUAL_DEAL" and resources.manual_stage == 1:
parts = message.split(":")
if len(parts) == 2:
resources.selected_plan = parts[1]
resources.logger.log_info(f"processing select_plan with plan: {resources.selected_plan}")
else:
resources.logger.log_info("invalid command")
await websocket.send(f"success received")
def position_adjust(command, degree, quat_rot):
"""位置调整解算,返回位置增量"""
if "left" in command:
if degree == "high":
position_increment = [0.02, 0, 0]
elif degree == "low":
position_increment = [0.01, 0, 0]
else:
position_increment = [0.015, 0, 0]
elif "right" in command:
if degree == "high":
position_increment = [-0.02, 0, 0]
elif degree == "low":
position_increment = [-0.01, 0, 0]
else:
position_increment = [-0.015, 0, 0]
elif "up" in command:
if degree == "high":
position_increment = [0, 0.05, 0]
elif degree == "low":
position_increment = [0, 0.025, 0]
else:
position_increment = [0, 0.04, 0]
elif "down" in command:
if degree == "high":
position_increment = [0, -0.05, 0]
elif degree == "low":
position_increment = [0, -0.025, 0]
else:
position_increment = [0, -0.04, 0]
rotation = R.from_quat(quat_rot)
position_increment = rotation.apply(position_increment)
return np.array(position_increment)
def force_adjust(command, degree, massage_wrench, limit):
"""力度调整解算,返回力度"""
print("force_limit:",limit)
force_increment = 0
if "decrease" in command:
if degree == "high":
force_increment += 8
elif degree == "low":
force_increment += 2
else:
force_increment += 5
elif "increase" in command:
if degree == "high":
force_increment -= 8
elif degree == "low":
force_increment -= 2
else:
force_increment -= 5
massage_wrench[2] += force_increment
massage_wrench[2] = np.clip(massage_wrench[2], limit[0], limit[1])
return np.array(massage_wrench)
def temperature_adjust(command, degree, temperature, limit):
temperature_increment = 0
if "decrease" in command:
if degree == "high":
temperature_increment = -2
elif degree == "low":
temperature_increment = -1
else:
temperature_increment = -1
elif "increase" in command:
if degree == "high":
temperature_increment = +2
elif degree == "low":
temperature_increment = +1
else:
temperature_increment = +1
new_temperature = temperature + temperature_increment
return max(limit[0], min(new_temperature, limit[1]))
def stone_speed_adjust(command, degree, speed, limit):
speed_increment = 0
if "decrease" in command:
if degree == "high":
speed_increment = -2
elif degree == "low":
speed_increment = -1
else:
speed_increment = -1
elif "increase" in command:
if degree == "high":
speed_increment = +2
elif degree == "low":
speed_increment = +1
else:
speed_increment = +1
new_speed = speed + speed_increment
return max(limit[0], min(new_speed, limit[1]))
def gear_adjust(command, degree, gear, limit):
gear_increment = 0
if "decrease" in command:
if degree == "high":
gear_increment = -2
elif degree == "low":
gear_increment = -1
else:
gear_increment = -1
elif "increase" in command:
if degree == "high":
gear_increment = +2
elif degree == "low":
gear_increment = +1
else:
gear_increment = +1
new_gear = gear + gear_increment
return max(limit[0], min(new_gear, limit[1]))
def shake_adjust(command, degree, shake, limit):
shake_increment = 0
if "decrease" in command:
if degree == "high":
shake_increment = -2
elif degree == "low":
shake_increment = -1
else:
shake_increment = -1
elif "increase" in command:
if degree == "high":
shake_increment = +2
elif degree == "low":
shake_increment = +1
else:
shake_increment = +1
new_shake = shake + shake_increment
return max(limit[0], min(new_shake, limit[1]))
def press_adjust(command, degree, press, limit):
press_increment = 0
if "decrease" in command:
if degree == "high":
press_increment = -3
elif degree == "low":
press_increment = -1
else:
press_increment = -2
elif "increase" in command:
if degree == "high":
press_increment = +3
elif degree == "low":
press_increment = +1
else:
press_increment = +2
new_press = press + press_increment
return max(limit[0], min(new_press, limit[1]))
def frequency_adjust(command, degree, frequency, limit):
frequency_increment = 0
if "decrease" in command:
if degree == "high":
frequency_increment = -3
elif degree == "low":
frequency_increment = -1
else:
frequency_increment = -2
elif "increase" in command:
if degree == "high":
frequency_increment = +3
elif degree == "low":
frequency_increment = +1
else:
frequency_increment = +2
new_frequency = frequency + frequency_increment
return max(limit[0], min(new_frequency, limit[1]))
def high_adjust(command, high, limit):
high_increment = 0
if "decrease" in command:
high_increment = -0.01
elif "increase" in command:
high_increment = +0.01
print("high command:",high_increment)
print("limit",limit)
new_high = high + high_increment
print("new_high",new_high)
return max(limit[0], min(new_high, limit[1]))
def start_server(resources: SharedResources):
async def handler_with_kwargs(websocket, path):
await handler(websocket, path, resources)
async def server():
async with websockets.serve(handler_with_kwargs, "localhost", 8765):
resources.logger.log_info("WebSocket server started on ws://localhost:8765")
await asyncio.Future()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(server())
def start_client(resources):
asyncio.set_event_loop(resources.loop)
resources.loop.run_forever()
def create_state_change_callback(resources: SharedResources):
def state_change_callback(userdata, active_states):
resources.logger.log_info("state changed to: " + active_states[0])
resources.now_state = active_states[0]
return state_change_callback
def start_sm(resources):
sm = smach.StateMachine(outcomes=["emergency_exit"])
with sm:
smach.StateMachine.add(
"IDLE",
Idle(resources),
transitions={
"to_smart": "SMART_DEAL",
"to_manual": "MANUAL_DEAL",
},
)
smach.StateMachine.add(
"SMART_DEAL",
SMART_DEAL(resources),
transitions={
"to_thermotherapy": "THERMOTHERAPY_TASK",
"to_shockwave": "SHOCKWAVE_TASK",
"to_ball": "BALL_TASK",
"to_finger": "FINGER_TASK",
"to_roller": "ROLLER_TASK",
"to_spheres": "SPHERES_TASK",
"to_stone": "STONE_TASK",
"to_heat": "HEAT_TASK",
"to_ion": "ION_TASK",
"pause": "PAUSE",
"stop": "STOP",
"emergency": "error_exit",
},
)
smach.StateMachine.add(
"MANUAL_DEAL",
MANUAL_DEAL(resources),
transitions={
"to_thermotherapy": "THERMOTHERAPY_TASK",
"to_shockwave": "SHOCKWAVE_TASK",
"to_ball": "BALL_TASK",
"to_finger": "FINGER_TASK",
"to_roller": "ROLLER_TASK",
"to_spheres": "SPHERES_TASK",
"to_stone": "STONE_TASK",
"to_heat": "HEAT_TASK",
"to_ion": "ION_TASK",
"pause": "PAUSE",
"stop": "STOP",
"emergency": "error_exit",
},
)
smach.StateMachine.add(
"THERMOTHERAPY_TASK",
Thermotherapy_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"SHOCKWAVE_TASK",
Shockwave_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"BALL_TASK",
Ball_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"STONE_TASK",
Stone_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"HEAT_TASK",
Heat_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"FINGER_TASK",
Finger_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"ROLLER_TASK",
Roller_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"SPHERES_TASK",
Spheres_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"ION_TASK",
Ion_Task(resources),
transitions={"pause": "PAUSE","stop": "STOP", "emergency": "error_exit"},
)
smach.StateMachine.add(
"PAUSE",
Pause(resources),
transitions={
"to_smart": "SMART_DEAL",
"to_manual": "MANUAL_DEAL",
"stop": "STOP",
"emergency": "error_exit",
},
)
smach.StateMachine.add(
"STOP",
Stop(resources),
transitions={"to_idle": "IDLE", "emergency": "error_exit"},
)
smach.StateMachine.add(
"error_exit",
EmergencyExitState(resources), # 定义一个退出的状态
transitions={"emergency_exit": "emergency_exit"} # 定义为终止状态
)
sis = smach_ros.IntrospectionServer("server_name", sm, "/SM_ROOT")
sis.start()
sm.register_transition_cb(create_state_change_callback(resources))
outcome = sm.execute()
# 根据状态机的执行结果处理
if outcome == "emergency_exit":
print("State machine terminated with emergency exit.")
sis.stop()
def main():
exit_event = threading.Event()
exit_event.clear()
def signal_handler(signum, frame):
resources.robot.stop()
instruction = {
"is_massaging": False,
"is_pause": False,
"massage_service_started": False,
"massage_head": None,
"progress": "0",
"force": "0",
"temperature": "",
"temper_head": "",
"speed": "",
"direction": "",
"gear": "",
"shake": "",
"press": "",
"frequency": "",
"current_head": "",
"task_time": 0,
"loops": 0,
"current_time": 0,
"body_part": "",
"massage_state": "",
"mode_real": 1,
"use_mode": "smart",
"manual_stage": 0
}
resources.send_instruction(f"massage_status:{str(instruction)}", "language")
exit_event.set()
args = parse_controller_args()
resources = SharedResources(args)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler) # 捕获 systemctl stop
rospy.init_node("state_machine")
print("inited ros node")
resources.logger.log_blue('start_sm')
sm_thread = threading.Thread(target=start_sm, args=(resources,), daemon=True)
sm_thread.start()
resources.logger.log_blue('start_client')
threading.Thread(target=start_client, args=(resources,), daemon=True).start()
resources.logger.log_blue('start_server')
threading.Thread(target=start_server, args=(resources,), daemon=True).start()
instruction = {
"is_massaging": False,
"is_pause": False,
"massage_service_started": True,
"massage_head":resources.robot.current_head,
"progress":str(resources.progress),
"force": str(-resources.robot.arm_state.massage_wrench[2]),
"temperature": resources.temperature,
"temper_head": None,
"speed": resources.speed,
"direction": resources.direction,
"gear": resources.gear,
"shake": resources.shake,
"press": resources.press,
"frequency": resources.frequency,
"task_time": str(resources.task_time),
"current_time": str(resources.current_time),
"loops": str(resources.loops),
"body_part": resources.body_part,
"massage_state": resources.now_state,
"mode_real": resources.mode_real,
"use_mode": resources.use_mode
}
resources.send_instruction(instruction, "ui_update_massage_status")
resources.send_instruction(f"massage_status:{str(instruction)}", "language")
resources.send_instruction("connect_successfully", "language")
while sm_thread.is_alive() and not exit_event.is_set():
time.sleep(0.1)
sys.exit(0)
if __name__ == "__main__":
main()