From 2990e70c25a174bb7f6e736d96bc600b5b381c1c Mon Sep 17 00:00:00 2001 From: zhangshixiang <@zhangshixiang> Date: Fri, 25 Apr 2025 10:47:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A8=A1=E5=9E=8B=E6=96=B9?= =?UTF-8?q?=E5=90=91=EF=BC=8C=E5=9C=A8yaml=E4=B8=AD=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8F=98=E6=8D=A2=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/experiments/test.json | 10 +- unilabos/app/main.py | 7 +- .../macro_device.xacro | 4 +- unilabos/device_mesh/resource_visalization.py | 40 +- unilabos/device_mesh/view_robot.rviz | 923 ++++++++++++++++++ .../devices/ros_dev/resource_mesh_manager.py | 252 ++++- .../registry/resources/opentrons/plates.yaml | 4 +- 7 files changed, 1173 insertions(+), 67 deletions(-) create mode 100644 unilabos/device_mesh/view_robot.rviz diff --git a/test/experiments/test.json b/test/experiments/test.json index a07592c2..97ad5ec7 100644 --- a/test/experiments/test.json +++ b/test/experiments/test.json @@ -9,8 +9,8 @@ "type": "device", "class": "gripper.mock", "position": { - "x": 620.6111111111111, - "y": 171, + "x": 1000, + "y": 1000, "z": 0 }, "config": { @@ -27,9 +27,9 @@ "type": "plate", "class": "nest_96_wellplate_100ul_pcr_full_skirt", "position": { - "x": 620.6111111111111, - "y": 171, - "z": 0 + "x": 0, + "y": 0, + "z": 69 }, "config": { }, diff --git a/unilabos/app/main.py b/unilabos/app/main.py index a31df68a..caa029c1 100644 --- a/unilabos/app/main.py +++ b/unilabos/app/main.py @@ -166,10 +166,9 @@ def main(): resource_visualization = ResourceVisualization(args_dict["devices_config"], args_dict["resources_config"],registry_dict) start_backend(**args_dict) - print('-'*100) - print(resource_visualization.resource_model) - print(json.dumps(args_dict["resources_config"], indent=4, ensure_ascii=False)) - print('-'*100) + # print('-'*100) + # print(resource_visualization.resource_model) + # print('-'*100) server_thread = threading.Thread(target=start_server) server_thread.start() diff --git a/unilabos/device_mesh/devices/opentrons_liquid_handler/macro_device.xacro b/unilabos/device_mesh/devices/opentrons_liquid_handler/macro_device.xacro index 5dc945ba..4e660557 100644 --- a/unilabos/device_mesh/devices/opentrons_liquid_handler/macro_device.xacro +++ b/unilabos/device_mesh/devices/opentrons_liquid_handler/macro_device.xacro @@ -8,14 +8,14 @@ - + - + diff --git a/unilabos/device_mesh/resource_visalization.py b/unilabos/device_mesh/resource_visalization.py index e73a6807..3a616dab 100644 --- a/unilabos/device_mesh/resource_visalization.py +++ b/unilabos/device_mesh/resource_visalization.py @@ -28,7 +28,12 @@ class ResourceVisualization: self.mesh_path = Path(__file__).parent.absolute() self.enable_rviz = enable_rviz + self.srdf_str = ''' + + + + ''' self.robot_state_str= ''' @@ -56,6 +61,13 @@ class ResourceVisualization: new_dev = etree.SubElement(self.root, f"{{{xacro_uri}}}{model_config['mesh']}") new_dev.set("parent_link", "world") new_dev.set("mesh_path", str(self.mesh_path)) + new_dev.set("device_name", node["id"]+"_") + new_dev.set("x",str(float(node["position"]["x"])/1000)) + new_dev.set("y",str(float(node["position"]["y"])/1000)) + new_dev.set("z",str(float(node["position"]["z"])/1000)) + if "rotation" in node["config"]: + new_dev.set("r",str(float(node["config"]["rotation"]["z"])/1000)) + elif node['type'] in self.resource_type: # print(registry.resource_type_registry) @@ -65,9 +77,14 @@ class ResourceVisualization: elif "model" in registry.resource_type_registry[resource_class].keys(): model_config = registry.resource_type_registry[resource_class]['model'] if model_config['type'] == 'resource': - self.resource_model[node['id']] = f"{str(self.mesh_path)}/resources/{model_config['mesh']}" + self.resource_model[node['id']] = { + 'mesh': f"{str(self.mesh_path)}/resources/{model_config['mesh']}", + 'mesh_tf': model_config['mesh_tf']} if model_config['children_mesh'] is not None: - self.resource_model[f"{node['id']}_"] = f"{str(self.mesh_path)}/resources/{model_config['children_mesh']}" + self.resource_model[f"{node['id']}_"] = { + 'mesh': f"{str(self.mesh_path)}/resources/{model_config['children_mesh']}", + 'mesh_tf': model_config['children_mesh_tf'] + } re = etree.tostring(self.root, encoding="unicode") doc = xacro.parse(re) @@ -102,23 +119,35 @@ class ResourceVisualization: }] ) + joint_state_publisher_node = nd( + package='joint_state_publisher_gui', # 或 joint_state_publisher + executable='joint_state_publisher_gui', + name='joint_state_publisher', + output='screen' + ) # 创建move_group节点 move_group = nd( package='moveit_ros_move_group', executable='move_group', output='screen', parameters=[{ - 'robot_description': robot_description, - 'allow_trajectory_execution': True, + 'robot_description': self.robot_state_str, + 'robot_description_semantic': self.srdf_str, 'capabilities': '', 'disable_capabilities': '', 'monitor_dynamics': False, - 'publish_monitored_planning_scene': True + 'publish_monitored_planning_scene': True, + 'publish_robot_description_semantic': True, + 'publish_planning_scene': True, + 'publish_geometry_updates': True, + 'publish_state_updates': True, + 'publish_transforms_updates': True, }] ) # 将节点添加到launch描述中 self.launch_description.add_action(robot_state_publisher) + self.launch_description.add_action(joint_state_publisher_node) self.launch_description.add_action(move_group) # 如果启用RViz,添加RViz节点 @@ -127,6 +156,7 @@ class ResourceVisualization: package='rviz2', executable='rviz2', name='rviz2', + arguments=['-d', f"{str(self.mesh_path)}/view_robot.rviz"], output='screen' ) self.launch_description.add_action(rviz_node) diff --git a/unilabos/device_mesh/view_robot.rviz b/unilabos/device_mesh/view_robot.rviz new file mode 100644 index 00000000..ef202c9d --- /dev/null +++ b/unilabos/device_mesh/view_robot.rviz @@ -0,0 +1,923 @@ +Panels: + - Class: rviz_common/Displays + Help Height: 138 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /TF1 + - /TF1/Tree1 + - /RobotModel1 + - /PlanningScene1 + - /PlanningScene1/Scene Geometry1 + - /RobotState1 + - /RobotState1/Links1 + Splitter Ratio: 0.5 + Tree Height: 275 + - Class: rviz_common/Selection + Name: Selection + - Class: rviz_common/Tool Properties + Expanded: + - /2D Goal Pose1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz_common/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz_default_plugins/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: + Value: true + - Class: rviz_default_plugins/TF + Enabled: true + Frame Timeout: 15 + Frames: + All Enabled: false + Gripper1_device_link: + Value: false + Gripper1_first_link: + Value: true + Gripper1_fourth_link: + Value: false + Gripper1_main_link: + Value: false + Gripper1_second_link: + Value: false + Gripper1_socketTypeGenericSbsFootprint: + Value: false + Gripper1_socketTypeHEPAModule: + Value: false + Gripper1_third_link: + Value: false + Plate1: + Value: false + Plate1_A1: + Value: false + Plate1_A10: + Value: false + Plate1_A11: + Value: false + Plate1_A12: + Value: false + Plate1_A2: + Value: false + Plate1_A3: + Value: false + Plate1_A4: + Value: false + Plate1_A5: + Value: false + Plate1_A6: + Value: false + Plate1_A7: + Value: false + Plate1_A8: + Value: false + Plate1_A9: + Value: false + Plate1_B1: + Value: false + Plate1_B10: + Value: false + Plate1_B11: + Value: false + Plate1_B12: + Value: false + Plate1_B2: + Value: false + Plate1_B3: + Value: false + Plate1_B4: + Value: false + Plate1_B5: + Value: false + Plate1_B6: + Value: false + Plate1_B7: + Value: false + Plate1_B8: + Value: false + Plate1_B9: + Value: false + Plate1_C1: + Value: false + Plate1_C10: + Value: false + Plate1_C11: + Value: false + Plate1_C12: + Value: false + Plate1_C2: + Value: false + Plate1_C3: + Value: false + Plate1_C4: + Value: false + Plate1_C5: + Value: false + Plate1_C6: + Value: false + Plate1_C7: + Value: false + Plate1_C8: + Value: false + Plate1_C9: + Value: false + Plate1_D1: + Value: false + Plate1_D10: + Value: false + Plate1_D11: + Value: false + Plate1_D12: + Value: false + Plate1_D2: + Value: false + Plate1_D3: + Value: false + Plate1_D4: + Value: false + Plate1_D5: + Value: false + Plate1_D6: + Value: false + Plate1_D7: + Value: false + Plate1_D8: + Value: false + Plate1_D9: + Value: false + Plate1_E1: + Value: false + Plate1_E10: + Value: false + Plate1_E11: + Value: false + Plate1_E12: + Value: false + Plate1_E2: + Value: false + Plate1_E3: + Value: false + Plate1_E4: + Value: false + Plate1_E5: + Value: false + Plate1_E6: + Value: false + Plate1_E7: + Value: false + Plate1_E8: + Value: false + Plate1_E9: + Value: false + Plate1_F1: + Value: false + Plate1_F10: + Value: false + Plate1_F11: + Value: false + Plate1_F12: + Value: false + Plate1_F2: + Value: false + Plate1_F3: + Value: false + Plate1_F4: + Value: false + Plate1_F5: + Value: false + Plate1_F6: + Value: false + Plate1_F7: + Value: false + Plate1_F8: + Value: false + Plate1_F9: + Value: false + Plate1_G1: + Value: false + Plate1_G10: + Value: false + Plate1_G11: + Value: false + Plate1_G12: + Value: false + Plate1_G2: + Value: false + Plate1_G3: + Value: false + Plate1_G4: + Value: false + Plate1_G5: + Value: false + Plate1_G6: + Value: false + Plate1_G7: + Value: false + Plate1_G8: + Value: false + Plate1_G9: + Value: false + Plate1_H1: + Value: false + Plate1_H10: + Value: false + Plate1_H11: + Value: false + Plate1_H12: + Value: false + Plate1_H2: + Value: false + Plate1_H3: + Value: false + Plate1_H4: + Value: false + Plate1_H5: + Value: false + Plate1_H6: + Value: false + Plate1_H7: + Value: false + Plate1_H8: + Value: false + Plate1_H9: + Value: false + world: + Value: false + Marker Scale: 1 + Name: TF + Show Arrows: true + Show Axes: true + Show Names: true + Tree: + world: + Gripper1_device_link: + Gripper1_main_link: + Gripper1_first_link: + Gripper1_second_link: + Gripper1_fourth_link: + {} + Gripper1_third_link: + {} + Plate1_C6: + {} + Gripper1_socketTypeGenericSbsFootprint: + {} + Gripper1_socketTypeHEPAModule: + {} + Plate1: + Plate1_A1: + {} + Plate1_A10: + {} + Plate1_A11: + {} + Plate1_A12: + {} + Plate1_A2: + {} + Plate1_A3: + {} + Plate1_A4: + {} + Plate1_A5: + {} + Plate1_A6: + {} + Plate1_A7: + {} + Plate1_A8: + {} + Plate1_A9: + {} + Plate1_B1: + {} + Plate1_B10: + {} + Plate1_B11: + {} + Plate1_B12: + {} + Plate1_B2: + {} + Plate1_B3: + {} + Plate1_B4: + {} + Plate1_B5: + {} + Plate1_B6: + {} + Plate1_B7: + {} + Plate1_B8: + {} + Plate1_B9: + {} + Plate1_C1: + {} + Plate1_C10: + {} + Plate1_C11: + {} + Plate1_C12: + {} + Plate1_C2: + {} + Plate1_C3: + {} + Plate1_C4: + {} + Plate1_C7: + {} + Plate1_C8: + {} + Plate1_C9: + {} + Plate1_D1: + {} + Plate1_D10: + {} + Plate1_D11: + {} + Plate1_D12: + {} + Plate1_D2: + {} + Plate1_D3: + {} + Plate1_D4: + {} + Plate1_D5: + {} + Plate1_D6: + {} + Plate1_D7: + {} + Plate1_D8: + {} + Plate1_D9: + {} + Plate1_E1: + {} + Plate1_E10: + {} + Plate1_E11: + {} + Plate1_E12: + {} + Plate1_E2: + {} + Plate1_E3: + {} + Plate1_E4: + {} + Plate1_E5: + {} + Plate1_E6: + {} + Plate1_E7: + {} + Plate1_E8: + {} + Plate1_E9: + {} + Plate1_F1: + {} + Plate1_F10: + {} + Plate1_F11: + {} + Plate1_F12: + {} + Plate1_F2: + {} + Plate1_F3: + {} + Plate1_F4: + {} + Plate1_F5: + {} + Plate1_F6: + {} + Plate1_F7: + {} + Plate1_F8: + {} + Plate1_F9: + {} + Plate1_G1: + {} + Plate1_G10: + {} + Plate1_G11: + {} + Plate1_G12: + {} + Plate1_G2: + {} + Plate1_G3: + {} + Plate1_G4: + {} + Plate1_G5: + {} + Plate1_G6: + {} + Plate1_G7: + {} + Plate1_G8: + {} + Plate1_G9: + {} + Plate1_H1: + {} + Plate1_H10: + {} + Plate1_H11: + {} + Plate1_H12: + {} + Plate1_H2: + {} + Plate1_H3: + {} + Plate1_H4: + {} + Plate1_H5: + {} + Plate1_H6: + {} + Plate1_H7: + {} + Plate1_H8: + {} + Plate1_H9: + {} + Plate1_C5: + {} + Update Interval: 0 + Value: true + - Alpha: 1 + Class: rviz_default_plugins/RobotModel + Collision Enabled: false + Description File: "" + Description Source: Topic + Description Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /robot_description + Enabled: true + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Gripper1_device_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_first_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_fourth_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_main_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_second_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_socketTypeGenericSbsFootprint: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_socketTypeHEPAModule: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_third_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Link Tree Style: Links in Alphabetic Order + world: + Alpha: 1 + Show Axes: false + Show Trail: false + Mass Properties: + Inertia: false + Mass: false + Name: RobotModel + TF Prefix: "" + Update Interval: 0 + Value: true + Visual Enabled: true + - Class: moveit_rviz_plugin/PlanningScene + Enabled: false + Move Group Namespace: "" + Name: PlanningScene + Planning Scene Topic: /monitored_planning_scene + Robot Description: robot_description + Scene Geometry: + Scene Alpha: 0.8999999761581421 + Scene Color: 50; 230; 50 + Scene Display Time: 0.009999999776482582 + Show Scene Geometry: true + Voxel Coloring: Z-Axis + Voxel Rendering: Occupied Voxels + Scene Robot: + Attached Body Color: 150; 50; 150 + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Gripper1_device_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_first_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_fourth_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_main_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_second_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_socketTypeGenericSbsFootprint: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_socketTypeHEPAModule: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_third_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Link Tree Style: Links in Alphabetic Order + world: + Alpha: 1 + Show Axes: false + Show Trail: false + Robot Alpha: 1 + Show Robot Collision: false + Show Robot Visual: false + Value: false + - Attached Body Color: 150; 50; 150 + Class: moveit_rviz_plugin/RobotState + Collision Enabled: false + Enabled: false + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Gripper1_device_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_first_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_fourth_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_main_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_second_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_socketTypeGenericSbsFootprint: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_socketTypeHEPAModule: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_third_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Link Tree Style: Links in Alphabetic Order + world: + Alpha: 1 + Show Axes: false + Show Trail: false + Name: RobotState + Robot Alpha: 1 + Robot Description: robot_description + Robot State Topic: display_robot_state + Show All Links: true + Show Highlights: true + Value: false + Visual Enabled: true + - Acceleration_Scaling_Factor: 0.1 + Class: moveit_rviz_plugin/MotionPlanning + Enabled: true + Move Group Namespace: "" + MoveIt_Allow_Approximate_IK: false + MoveIt_Allow_External_Program: false + MoveIt_Allow_Replanning: false + MoveIt_Allow_Sensor_Positioning: false + MoveIt_Planning_Attempts: 10 + MoveIt_Planning_Time: 5 + MoveIt_Use_Cartesian_Path: false + MoveIt_Use_Constraint_Aware_IK: false + MoveIt_Workspace: + Center: + X: 0 + Y: 0 + Z: 0 + Size: + X: 2 + Y: 2 + Z: 2 + Name: MotionPlanning + Planned Path: + Color Enabled: false + Interrupt Display: false + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Gripper1_device_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_first_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_fourth_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_main_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_second_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_socketTypeGenericSbsFootprint: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_socketTypeHEPAModule: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_third_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Link Tree Style: Links in Alphabetic Order + world: + Alpha: 1 + Show Axes: false + Show Trail: false + Loop Animation: false + Robot Alpha: 0.5 + Robot Color: 150; 50; 150 + Show Robot Collision: false + Show Robot Visual: true + Show Trail: false + State Display Time: 3x + Trail Step Size: 1 + Trajectory Topic: /display_planned_path + Use Sim Time: false + Planning Metrics: + Payload: 1 + Show Joint Torques: false + Show Manipulability: false + Show Manipulability Index: false + Show Weight Limit: false + TextHeight: 0.07999999821186066 + Planning Request: + Colliding Link Color: 255; 0; 0 + Goal State Alpha: 1 + Goal State Color: 250; 128; 0 + Interactive Marker Size: 0 + Joint Violation Color: 255; 0; 255 + Planning Group: "" + Query Goal State: true + Query Start State: false + Show Workspace: false + Start State Alpha: 1 + Start State Color: 0; 255; 0 + Planning Scene Topic: /monitored_planning_scene + Robot Description: robot_description + Scene Geometry: + Scene Alpha: 0.8999999761581421 + Scene Color: 50; 230; 50 + Scene Display Time: 0.009999999776482582 + Show Scene Geometry: true + Voxel Coloring: Z-Axis + Voxel Rendering: Occupied Voxels + Scene Robot: + Attached Body Color: 150; 50; 150 + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Gripper1_device_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_first_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_fourth_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_main_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_second_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Gripper1_socketTypeGenericSbsFootprint: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_socketTypeHEPAModule: + Alpha: 1 + Show Axes: false + Show Trail: false + Gripper1_third_link: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Link Tree Style: Links in Alphabetic Order + world: + Alpha: 1 + Show Axes: false + Show Trail: false + Robot Alpha: 1 + Show Robot Collision: false + Show Robot Visual: true + Value: true + Velocity_Scaling_Factor: 0.1 + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Fixed Frame: world + Frame Rate: 30 + Name: root + Tools: + - Class: rviz_default_plugins/Interact + Hide Inactive Objects: true + - Class: rviz_default_plugins/MoveCamera + - Class: rviz_default_plugins/Select + - Class: rviz_default_plugins/FocusCamera + - Class: rviz_default_plugins/Measure + Line color: 128; 128; 0 + - Class: rviz_default_plugins/SetInitialPose + Covariance x: 0.25 + Covariance y: 0.25 + Covariance yaw: 0.06853891909122467 + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /initialpose + - Class: rviz_default_plugins/SetGoal + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /goal_pose + - Class: rviz_default_plugins/PublishPoint + Single click: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /clicked_point + Transformation: + Current: + Class: rviz_default_plugins/TF + Value: true + Views: + Current: + Class: rviz_default_plugins/Orbit + Distance: 1.49553644657135 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Focal Point: + X: 1.2573390007019043 + Y: 1.1951926946640015 + Z: 0.23975235223770142 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.5797955989837646 + Target Frame: + Value: Orbit (rviz) + Yaw: 6.250748634338379 + Saved: ~ +Window Geometry: + "": + collapsed: false + " - Trajectory Slider": + collapsed: false + Displays: + collapsed: false + Height: 1656 + Hide Left Dock: false + Hide Right Dock: true + QMainWindow State: 000000ff00000000fd0000000400000000000003a3000005dcfc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b000000b000fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000006e0000020b0000018200fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb000000280020002d0020005400720061006a006500630074006f0072007900200053006c00690064006500720000000000ffffffff0000007a00fffffffbffffffff0100000285000003c5000002b700ffffff000000010000010f00000387fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003b000003870000013200fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d0065010000000000000450000000000000000000000627000005dc00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: true + Width: 2518 + X: 125 + Y: 145 diff --git a/unilabos/devices/ros_dev/resource_mesh_manager.py b/unilabos/devices/ros_dev/resource_mesh_manager.py index 7943189e..7b1b3610 100644 --- a/unilabos/devices/ros_dev/resource_mesh_manager.py +++ b/unilabos/devices/ros_dev/resource_mesh_manager.py @@ -15,8 +15,10 @@ from rclpy.task import Future import copy from typing import Tuple, Optional, Union, Any, List from tf_transformations import quaternion_from_euler -from tf2_ros import StaticTransformBroadcaster - +from tf2_ros import TransformBroadcaster, Buffer, TransformListener +from rclpy.action import ActionServer +from unilabos_msgs.action import SendCmd +from rclpy.action.server import ServerGoalHandle class ResourceMeshManager(Node): def __init__(self, resource_model: dict, resource_config: list, node_name: str): @@ -30,13 +32,13 @@ class ResourceMeshManager(Node): super().__init__(node_name) self.resource_model = resource_model - self.resource_config = resource_config - self.resource_config_dict = {item['id']: item for item in self.resource_config} + self.resource_config_dict = {item['id']: item for item in resource_config} self.move_group_ready = False self.resource_tf_dict = {} - self.tf_broadcaster = StaticTransformBroadcaster(self) - - self.create_timer(1, self.publish_resource_tf) + self.tf_broadcaster = TransformBroadcaster(self) + self.tf_buffer = Buffer() + self.tf_listener = TransformListener(self.tf_buffer, self) + self.create_timer(0.02, self.publish_resource_tf) callback_group = ReentrantCallbackGroup() self._get_planning_scene_service = self.create_client( @@ -74,9 +76,65 @@ class ResourceMeshManager(Node): AttachedCollisionObject, "/attached_collision_object", 10 ) + # 创建一个Action Server用于修改resource_tf_dict + self._action_server = ActionServer( + self, + SendCmd, + f'{node_name}/tf_update', + self.tf_update, + callback_group=callback_group + ) + + def tf_update(self, goal_handle : ServerGoalHandle): + tf_update_msg = goal_handle.request + + try: + cmd_dict = json.loads(tf_update_msg.command.replace("'",'"')) + for resource_id, target_parent in cmd_dict.items(): + + # 获取从resource_id到target_parent的转换 + transform = self.tf_buffer.lookup_transform( + target_parent, + resource_id, + rclpy.time.Time() + ) + + # 提取转换中的位置和旋转信息 + position = { + "x": transform.transform.translation.x, + "y": transform.transform.translation.y, + "z": transform.transform.translation.z + } + + rotation = { + "x": transform.transform.rotation.x, + "y": transform.transform.rotation.y, + "z": transform.transform.rotation.z, + "w": transform.transform.rotation.w + } + + self.resource_tf_dict[resource_id] = { + "parent": target_parent, + "position": position, + "rotation": rotation + } + + print(self.resource_tf_dict) + self.attach_collision_object(id=resource_id,link_name=target_parent) + + self.publish_resource_tf() + + + + except Exception as e: + self.get_logger().error(f"更新资源TF字典失败: {e}") + goal_handle.abort() + return SendCmd.Result(success=False) + goal_handle.succeed() + return SendCmd.Result(success=True) - """检查move_group节点是否已初始化完成""" def check_move_group_ready(self): + """检查move_group节点是否已初始化完成""" while not self.move_group_ready: # 获取当前可用的节点列表 if self._get_planning_scene_service.service_is_ready() and self._apply_planning_scene_service.service_is_ready(): @@ -91,20 +149,54 @@ class ResourceMeshManager(Node): #遍历resource_config中的资源配置,判断panent是否在resource_model中, - for resource_config in self.resource_config: - if resource_config['parent'] in self.resource_model: - # self.resource_tf_dict[resource_config['id']] = resource_config['parent'] - self.resource_tf_dict.update({resource_config['id']:{"parent":resource_config['parent'], - "position":resource_config['position'], - "rotation":resource_config['config']['rotation']}}) - elif resource_config['parent'] is None and resource_config['id'] in self.resource_model: - self.resource_tf_dict.update({resource_config['id']:{'parent':'world', - "position":resource_config['position'], - "rotation":resource_config['config']['rotation']}}) - elif resource_config['parent'] not in self.resource_model and resource_config['parent'] is not None: - self.resource_tf_dict.update({resource_config['id']:{'parent':f"{self.resource_config_dict[resource_config['parent']]['parent']}{resource_config['parent']}_device_link".replace("None",""), - "position":resource_config['position'], - "rotation":resource_config['config']['rotation']}}) + for resource_id, resource_config in self.resource_config_dict.items(): + + parent = resource_config['parent'] + parent_link = 'world' + if parent in self.resource_model: + parent_link = parent + elif parent is None and resource_id in self.resource_model: + pass + elif parent not in self.resource_model and parent is not None: + parent_link = f"{self.resource_config_dict[parent]['parent']}{parent}_device_link".replace("None","") + else: + continue + # 提取位置信息并转换单位 + position = { + "x": float(resource_config['position']['x'])/1000, + "y": float(resource_config['position']['y'])/1000, + "z": float(resource_config['position']['z'])/1000 + } + + rotation_dict = { + "x": 0, + "y": 0, + "z": 0 + } + + if 'rotation' in resource_config['config']: + rotation_dict = resource_config['config']['rotation'] + + # 从欧拉角转换为四元数 + q = quaternion_from_euler( + float(rotation_dict['x']), + float(rotation_dict['y']), + float(rotation_dict['z']) + ) + + rotation = { + "x": q[0], + "y": q[1], + "z": q[2], + "w": q[3] + } + + # 更新资源TF字典 + self.resource_tf_dict[resource_id] = { + "parent": parent_link, + "position": position, + "rotation": rotation + } def publish_resource_tf(self): """ @@ -113,12 +205,7 @@ class ResourceMeshManager(Node): 遍历self.resource_tf_dict中的每个元素,根据key,parent,以及position和rotation, 发布key和parent之间的tf关系 """ - self.get_logger().info('开始发布资源TF关系') - - # 创建静态TF广播器 - - - # 存储所有需要发布的静态变换 + transforms = [] # 遍历资源TF字典 @@ -135,31 +222,92 @@ class ResourceMeshManager(Node): transform.child_frame_id = resource_id # 设置位置 - transform.transform.translation.x = float(position['x'])/1000 - transform.transform.translation.y = float(position['y'])/1000 - transform.transform.translation.z = float(position['z'])/1000 - - # 从欧拉角转换为四元数 - q = quaternion_from_euler( - float(rotation['x']), - float(rotation['y']), - float(rotation['z']) - ) + transform.transform.translation.x = float(position['x']) + transform.transform.translation.y = float(position['y']) + transform.transform.translation.z = float(position['z']) # 设置旋转 - transform.transform.rotation.x = q[0] - transform.transform.rotation.y = q[1] - transform.transform.rotation.z = q[2] - transform.transform.rotation.w = q[3] + transform.transform.rotation.x = rotation['x'] + transform.transform.rotation.y = rotation['y'] + transform.transform.rotation.z = rotation['z'] + transform.transform.rotation.w = rotation['w'] transforms.append(transform) # 一次性发布所有静态变换 if transforms: self.tf_broadcaster.sendTransform(transforms) - self.get_logger().info(f'已发布 {len(transforms)} 个资源TF关系') + # self.get_logger().info(f'已发布 {len(transforms)} 个资源TF关系') + def add_resource_collision_meshes(self): + """ + 遍历资源配置字典,为每个在resource_model中有对应模型的资源添加碰撞网格 + 该方法检查每个资源ID是否在self.resource_model中有对应的3D模型文件路径, + 如果有,则调用add_collision_mesh方法将其添加到碰撞环境中。 + """ + self.get_logger().info('开始添加资源碰撞网格') + + for resource_id, tf_info in self.resource_tf_dict.items(): + + if resource_id in self.resource_model: + # 获取位置信息 + + position = [ + float(self.resource_model[resource_id]['mesh_tf'][0]), + float(self.resource_model[resource_id]['mesh_tf'][1]), + float(self.resource_model[resource_id]['mesh_tf'][2]) + ] + + # 获取旋转信息并转换为四元数 + + q = quaternion_from_euler( + float(self.resource_model[resource_id]['mesh_tf'][3]), + float(self.resource_model[resource_id]['mesh_tf'][4]), + float(self.resource_model[resource_id]['mesh_tf'][5]) + ) + + # 添加碰撞网格 + self.add_collision_mesh( + filepath=self.resource_model[resource_id]['mesh'], + id=resource_id, + position=position, + quat_xyzw=q, + frame_id=resource_id + ) + + elif f"{tf_info['parent']}_" in self.resource_model: + # 获取资源的父级框架ID + id_ = f"{tf_info['parent']}_" + + # 获取位置信息 + position = [ + float(self.resource_model[id_]['mesh_tf'][0]), + float(self.resource_model[id_]['mesh_tf'][1]), + float(self.resource_model[id_]['mesh_tf'][2]) + ] + + # 获取旋转信息并转换为四元数 + + q = quaternion_from_euler( + float(self.resource_model[id_]['mesh_tf'][3]), + float(self.resource_model[id_]['mesh_tf'][4]), + float(self.resource_model[id_]['mesh_tf'][5]) + ) + + # 添加碰撞网格 + self.add_collision_mesh( + filepath=self.resource_model[id_]['mesh'], + id=resource_id, + position=position, + quat_xyzw=q, + frame_id=resource_id + ) + time.sleep(0.01) + + self.get_logger().info('资源碰撞网格添加完成') + + def add_collision_primitive( self, id: str, @@ -672,7 +820,8 @@ class ResourceMeshManager(Node): if __name__ == '__main__': model_s = ''' -{'Plate1': '/home/z43/git_pj/Uni-Lab-OS/unilabos/device_mesh/resources/tecan_nested_tip_rack/meshes/plate.stl', 'Plate1_': '/home/z43/git_pj/Uni-Lab-OS/unilabos/device_mesh/resources/generic_labware_tube_10_75/meshes/0_base.stl'} +{'Plate1': {'mesh': '/home/z43/git_pj/Uni-Lab-OS/unilabos/device_mesh/resources/tecan_nested_tip_rack/meshes/plate.stl', 'mesh_tf': [0.064, 0.043, 0, -1.5708, 0, 1.5708]}, +'Plate1_': {'mesh': '/home/z43/git_pj/Uni-Lab-OS/unilabos/device_mesh/resources/generic_labware_tube_10_75/meshes/0_base.stl', 'mesh_tf': [0.0018, 0.0018, 0, -1.5708,0, 0]}} ''' resource_model = json.loads(model_s.replace("'",'"')) @@ -686,8 +835,8 @@ if __name__ == '__main__': "type": "device", "class": "gripper.mock", "position": { - "x": 620.6111111111111, - "y": 171, + "x": 0, + "y": 0, "z": 0 }, "config": {}, @@ -799,9 +948,9 @@ if __name__ == '__main__': "type": "device", "class": "", "position": { - "x": 620.6111111111111, - "y": 171, - "z": 0 + "x": 0, + "y": 0, + "z": 69 }, "config": { "type": "Plate", @@ -4668,5 +4817,8 @@ if __name__ == '__main__': rclpy.init() resource_mesh_manager = ResourceMeshManager(resource_model, resource_config, 'resource_mesh_manager') resource_mesh_manager.resource_mesh_setup() - print(json.dumps(resource_mesh_manager.resource_tf_dict, indent=4, ensure_ascii=False)) + resource_mesh_manager.publish_resource_tf() + # resource_mesh_manager.clear_all_collision_objects() + # print(json.dumps(resource_mesh_manager.resource_tf_dict, indent=4, ensure_ascii=False)) + resource_mesh_manager.add_resource_collision_meshes() rclpy.spin(resource_mesh_manager) \ No newline at end of file diff --git a/unilabos/registry/resources/opentrons/plates.yaml b/unilabos/registry/resources/opentrons/plates.yaml index 0a7c4b84..c00b2744 100644 --- a/unilabos/registry/resources/opentrons/plates.yaml +++ b/unilabos/registry/resources/opentrons/plates.yaml @@ -54,8 +54,10 @@ nest_96_wellplate_100ul_pcr_full_skirt: model: type: resource mesh: tecan_nested_tip_rack/meshes/plate.stl + mesh_tf: [0.064, 0.043, 0, -1.5708, 0, 1.5708] children_mesh: generic_labware_tube_10_75/meshes/0_base.stl - + children_mesh_tf: [0.0018, 0.0018, 0, -1.5708,0, 0] + appliedbiosystemsmicroamp_384_wellplate_40ul: description: Applied Biosystems microamp 384 wellplate 40ul class: