@@ -90,9 +90,10 @@ def __init__(self, moveit_group_name:str, world_link:str=None) -> None:
9090 # internal states
9191 self .commander_state :GeneralCommanderStates = GeneralCommanderStates .INIT
9292 self .cached_result :MoveGroupActionResult = None
93- # define the walls as the workspace limits
93+ # record the list of walls of the workspace
9494 self .wall_name_list = []
95-
95+ # record the collision objects, including the walls, added to the commander and their color
96+ self .object_name_color_list = {}
9697 # publisher for the planning scene
9798 self .scene_pub = rospy .Publisher ('planning_scene' , PlanningScene , queue_size = 10 )
9899 self .tf_pub = tf .TransformBroadcaster ()
@@ -101,47 +102,73 @@ def __init__(self, moveit_group_name:str, world_link:str=None) -> None:
101102 self .the_constraints = Constraints ()
102103 self .the_constraints .name = 'commander'
103104 # set timeout
104- self .timer = rospy .Timer (rospy .Duration (1 / self .TF_PUB_RATE ), self ._cb_timer )
105-
105+ self .timer = rospy .Timer (rospy .Duration (1 / self .TF_PUB_RATE ), self ._cb_timer_tf )
106+ self . timer_scene = rospy . Timer ( rospy . Duration ( 1.0 ), self . _cb_timer_scene )
106107 # -----------------------------------------------------
107108 # -- Internal functions for publishing states and poses
108109
109- # callback for waking up an IDLE robot agent
110- def _cb_timer (self , event ):
110+ # Internal functions: callback for publishing the object transformation and color of workspace
111+ def _cb_timer_tf (self , event ):
111112 self ._pub_transform_all_objects ()
112113
113- # publish colors of the wall objects
114- def _pub_workspace_color (self ):
114+ def _cb_timer_scene (self , event ):
115+ self ._pub_objects_color ()
116+
117+ # publish colors of the other collision objects
118+ def _pub_objects_color (self ):
115119 p = PlanningScene ()
116120 p .is_diff = True
117- for wall in self .wall_name_list :
118- color = self ._set_object_color (wall , 0.6 , 0 , 1 , 0.2 )
121+ for object_name in self .object_name_color_list .keys ():
122+ rgba = self .object_name_color_list [object_name ]
123+ if rgba is None :
124+ continue
125+ color = self ._set_object_color (object_name , * rgba )
119126 p .object_colors .append (color )
120127 self .scene_pub .publish (p )
128+
121129
122130 # publish the transform of all added objects
131+ # def _pub_transform_all_objects(self):
132+ # object_list = self.scene.get_known_object_names()
133+ # object_poses = self.scene.get_object_poses(object_list)
134+ # for object_name in object_list:
135+ # pose_of_object = object_poses[object_name]
136+ # rospy.logwarn(f'_pub_transform_all_objects: {object_name} {type(pose_of_object)}')
137+ # self._pub_transform_object(object_name, pose_of_object)
138+
123139 def _pub_transform_all_objects (self ):
124- object_list = self .scene .get_known_object_names ()
125- object_poses = self . scene . get_object_poses ( object_list )
126- for object_name in object_list :
127- pose_of_object = object_poses [object_name ]
128- self ._pub_transform_object (object_name , pose_of_object )
140+ objects = self .scene .get_objects ()
141+ for object_name in objects . keys ():
142+ pose_of_object = objects [ object_name ]. pose
143+ frame = objects [object_name ]. header . frame_id
144+ self ._pub_transform_object (object_name , pose_of_object , frame )
129145
130146 # publish the transform of a specific named object
131- def _pub_transform_object (self , name , pose ):
147+ def _pub_transform_object (self , name , pose , frame = None ):
148+ """ publish the transform of an object
149+
150+ :param name: name of the object
151+ :type name: str
152+ :param pose: the pose of the object
153+ :type pose: Pose, PoseStamped, list of 6 or 7
154+ :param frame: the frame against which the pose is defined, ignored if PoseStamped is provided, defaults to None
155+ :type frame: str, optional
156+ """
132157 if type (pose ) == Pose :
133158 pose = conversions .pose_to_list (pose )
134159 elif type (pose ) == PoseStamped :
160+ frame = pose .header .frame_id
135161 pose = conversions .pose_to_list (pose .pose )
136162 xyz = pose [:3 ]
137163 if type (pose ) != list :
138164 rospy .logerr (f'{ __class__ .__name__ } : parameter (pose) is not list of length 6 or 7 or a Pose object -> fix the parameter at behaviour construction' )
139165 raise TypeError (f'A parameter is invalid' )
140166 if len (pose ) == 6 :
141- q = tf .transformations .quaternion_from_euler ([ pose [3 :] ])
167+ q = tf .transformations .quaternion_from_euler (* pose [3 :])
142168 elif len (pose ) == 7 :
143169 q = pose [3 :]
144- self .tf_pub .sendTransform (xyz , q , rospy .Time .now (), name , self .WORLD_REFERENCE_LINK )
170+ frame = self .WORLD_REFERENCE_LINK if frame is None else frame
171+ self .tf_pub .sendTransform (xyz , q , rospy .Time .now (), name , frame )
145172
146173 def _cb_move_group_result (self , msg :MoveGroupActionResult ):
147174 rospy .loginfo (f'The commander (move_group result callback): { GOAL_STATUS_MAP [msg .status .status ]} message: { MOVEIT_ERROR_CODE_MAP [msg .result .error_code .val ]} ' )
@@ -1030,7 +1057,6 @@ def servo_robot(self, twist: Twist, frame: str = "", time: float = 0) -> bool:
10301057 def reset_world (self ):
10311058 """Remove all scene objects that have been added through the commander
10321059 """
1033-
10341060 self .move_group .detach_object (self .END_EFFECTOR_LINK )
10351061 self .scene .remove_attached_object (self .END_EFFECTOR_LINK )
10361062 self .scene .remove_world_object ()
@@ -1043,33 +1069,9 @@ def _wait_for_scene_update(self, func, timeout=5.0):
10431069 return
10441070 rospy .sleep (0.2 )
10451071 rospy .logwarn (f'{ __class__ .__name__ } (_wait_for_scene_update): timeout ({ timeout } seconds)' )
1046-
1047- # # add an object (defined with a meshed model, pose, scale) to the scene
1048- # def add_object_to_scene(self, object_name:str, model_file:str, object_scale:list, xyz:list, rpy:list):
1049- # """Add an object (defined with a meshed model, pose, scale) to the scene for collision avoidance in path planning
1050-
1051- # :param object_name: The name given to the new scene object
1052- # :type object_name: str
1053- # :param model_file: The path to the file that defines the mesh of the object
1054- # :type model_file: str
1055- # :param object_scale: The list of scale in 3 dimension
1056- # :type object_scale: list
1057- # :param xyz: The position of the object in the world/default reference frame
1058- # :type xyz: list
1059- # :param rpy: The orientation of the object in euler angles in the world/default reference frame
1060- # :type rpy: list
1061- # """
1062- # self.scene.remove_world_object(object_name)
1063- # object_pose = conversions.list_to_pose_stamped(xyz + rpy, self.WORLD_REFERENCE_LINK)
1064- # self.scene.add_mesh(
1065- # object_name, object_pose,
1066- # model_file, object_scale
1067- # )
1068- # self._wait_for_scene_update(lambda: object_name in self.scene.get_known_object_names())
1069- # self._pub_transform_object(object_name, object_pose)
10701072
10711073 # add an object (defined with a meshed model, pose, scale) to the scene
1072- def add_object_to_scene (self , object_name :str , model_file :str , object_scale :list , xyz :list , rpy :list , reference_frame :str = None ):
1074+ def add_object_to_scene (self , object_name :str , model_file :str , object_scale :list , xyz :list , rpy :list , reference_frame :str = None , rgba = None ):
10731075 """Add an object (defined with a meshed model, pose, scale) to the scene for collision avoidance in path planning
10741076
10751077 :param object_name: The name given to the new scene object
@@ -1094,10 +1096,11 @@ def add_object_to_scene(self, object_name:str, model_file:str, object_scale:list
10941096 model_file , object_scale
10951097 )
10961098 self ._wait_for_scene_update (lambda : object_name in self .scene .get_known_object_names ())
1099+ self .object_name_color_list [object_name ] = rgba
10971100 self ._pub_transform_object (object_name , object_pose )
10981101
10991102 # add an object (a shpere of given radius and position)
1100- def add_sphere_to_scene (self , object_name :str , radius :float , xyz :list , reference_frame :str = None ):
1103+ def add_sphere_to_scene (self , object_name :str , radius :float , xyz :list , reference_frame :str = None , rgba = None ):
11011104 """Add an object to the scene for collision avoidance in path planning
11021105
11031106 :param object_name: The name given to the new scene object
@@ -1114,10 +1117,11 @@ def add_sphere_to_scene(self, object_name:str, radius:float, xyz:list, reference
11141117 object_pose = conversions .list_to_pose_stamped (xyz + [0 , 0 , 0 ], reference_frame )
11151118 self .scene .add_sphere (object_name , object_pose , radius )
11161119 self ._wait_for_scene_update (lambda : object_name in self .scene .get_known_object_names ())
1120+ self .object_name_color_list [object_name ] = rgba
11171121 self ._pub_transform_object (object_name , object_pose )
11181122
11191123 # add a box (a box of given dimension, position and orientation)
1120- def add_box_to_scene (self , object_name :str , dimensions :list , xyz :list , rpy :list = [0 , 0 , 0 ], reference_frame :str = None ):
1124+ def add_box_to_scene (self , object_name :str , dimensions :list , xyz :list , rpy :list = [0 , 0 , 0 ], reference_frame :str = None , rgba = None ):
11211125 """ Add a box to the scene for collision avoidance in path planning
11221126
11231127 :param object_name: The name given to the new scene object
@@ -1131,12 +1135,12 @@ def add_box_to_scene(self, object_name:str, dimensions:list, xyz:list, rpy:list=
11311135 :param reference_frame: The frame of reference of the xyz and rpy
11321136 :type reference_frame: str, default to WORLD_REFERENCE_LINK
11331137 """
1134-
11351138 reference_frame = self .WORLD_REFERENCE_LINK if reference_frame is None else reference_frame
1136- object_pose = conversions .list_to_pose_stamped (xyz + rpy , reference_frame )
1139+ object_pose = conversions .list_to_pose_stamped (xyz + rpy , reference_frame )
11371140 self .scene .add_box (object_name , object_pose , size = dimensions )
11381141 self ._wait_for_scene_update (lambda : object_name in self .scene .get_known_object_names ())
1139- self ._pub_transform_object (object_name , object_pose )
1142+ self .object_name_color_list [object_name ] = rgba
1143+ self ._pub_transform_object (object_name , object_pose , reference_frame )
11401144
11411145 # returns a list of current objects that have been added to the commander
11421146 """ Returns a list of current objects that have been added to the commander
@@ -1146,15 +1150,22 @@ def list_object_names(self) -> list:
11461150 return self .scene .get_known_object_names ()
11471151
11481152 # remove object given the name
1149- def remove_object (self , object_name : str ) -> None :
1153+ def remove_object (self , object_name ) -> None :
11501154 """ Remove object given the name or all objects in the scene if None
11511155
1152- :param object_name: The name of the object, None for removing all objects
1156+ :param object_name: The name of the object, a list of names of multiple objects, or None for removing all objects
11531157 :type object_name: str
11541158 """
1155-
1156- self .scene .remove_world_object (object_name )
1157- self ._wait_for_scene_update (lambda : object_name not in self .scene .get_known_object_names ())
1159+ if object_name is None :
1160+ self .scene .remove_world_object (object_name )
1161+ self .object_name_color_list .clear ()
1162+ return
1163+ if type (object_name ) not in [list , tuple ]:
1164+ object_name = [object_name ]
1165+ for name in object_name :
1166+ self .scene .remove_world_object (name )
1167+ self ._wait_for_scene_update (lambda : name not in self .scene .get_known_object_names ())
1168+ del self .object_name_color_list [name ]
11581169
11591170 # attach an object to the end_effector
11601171 def attach_object_to_end_effector (self , object_name :str ) -> bool :
@@ -1221,7 +1232,6 @@ def get_object_pose_as_xyzrpy(self, object_name:str, print=False) -> list:
12211232 :return: The pose of the queried object as a list of 6 floats (xyzrpy)
12221233 :rtype: list
12231234 """
1224-
12251235 object_pose = self .get_object_pose (object_name )
12261236 object_pose_as_list = conversions .pose_to_list (object_pose )
12271237 xyzrpy = object_pose_as_list [:3 ]
@@ -1231,10 +1241,10 @@ def get_object_pose_as_xyzrpy(self, object_name:str, print=False) -> list:
12311241 return xyzrpy
12321242
12331243 # sets up the limits of the workspace using 6 walls
1234- def set_workspace_walls (self , min_x :float , min_y :float , min_z :float , max_x :float , max_y :float , max_z :float ):
1244+ def set_workspace_walls (self , min_x :float , min_y :float , min_z :float , max_x :float , max_y :float , max_z :float , rgba = [ 0.6 , 0.0 , 0.6 , 0.05 ] ):
12351245 """A convenient function for setting up a workspace as a space surrounded by 6 walls
12361246
1237- :param min_x: The minimum x of the workspace
1247+ :param min_x: The minimum x of the workspace, or None to remove the workspace walls
12381248 :type min_x: float
12391249 :param min_y: The minimum y of the workspace
12401250 :type min_y: float
@@ -1247,24 +1257,26 @@ def set_workspace_walls(self, min_x:float, min_y:float, min_z:float, max_x:float
12471257 :param max_z: The maximum z of the workspace
12481258 :type max_z: flost
12491259 """
1260+ if min_x is None : # remove the walls
1261+ self ._remove_all_walls ()
1262+ return
12501263 thickness = 0.02
12511264 size_x = max_x - min_x
12521265 size_y = max_y - min_y
12531266 size_z = max_z - min_z
12541267 cx = (max_x + min_x ) / 2
12551268 cy = (max_y + min_y ) / 2
12561269 cz = (max_z + min_z ) / 2
1257- self ._create_wall ('ws_top' , self .WORLD_REFERENCE_LINK , cx , cy , max_z , size_x , size_y , thickness )
1258- self ._create_wall ('ws_bottom' , self .WORLD_REFERENCE_LINK , cx , cy , min_z , size_x , size_y , thickness )
1259- self ._create_wall ('ws_side_1' , self .WORLD_REFERENCE_LINK , cx , min_y , cz , size_x , thickness , size_z )
1260- self ._create_wall ('ws_side_2' , self .WORLD_REFERENCE_LINK , cx , max_y , cz , size_x , thickness , size_z )
1261- self ._create_wall ('ws_side_3' , self .WORLD_REFERENCE_LINK , min_x , cy , cz , thickness , size_y , size_z )
1262- self ._create_wall ('ws_side_4' , self .WORLD_REFERENCE_LINK , max_x , cy , cz , thickness , size_y , size_z )
1270+ self ._create_wall ('ws_top' , self .WORLD_REFERENCE_LINK , cx , cy , max_z , size_x , size_y , thickness , rgba )
1271+ self ._create_wall ('ws_bottom' , self .WORLD_REFERENCE_LINK , cx , cy , min_z , size_x , size_y , thickness , rgba )
1272+ self ._create_wall ('ws_side_1' , self .WORLD_REFERENCE_LINK , cx , min_y , cz , size_x , thickness , size_z , rgba )
1273+ self ._create_wall ('ws_side_2' , self .WORLD_REFERENCE_LINK , cx , max_y , cz , size_x , thickness , size_z , rgba )
1274+ self ._create_wall ('ws_side_3' , self .WORLD_REFERENCE_LINK , min_x , cy , cz , thickness , size_y , size_z , rgba )
1275+ self ._create_wall ('ws_side_4' , self .WORLD_REFERENCE_LINK , max_x , cy , cz , thickness , size_y , size_z , rgba )
12631276 rospy .sleep (1.0 ) # wait for the walls to be registered
1264- self ._pub_workspace_color ()
12651277
12661278 # internal function for creating a wall
1267- def _create_wall (self , name , frame_id , x , y , z , size_x , size_y , size_z ):
1279+ def _create_wall (self , name , frame_id , x , y , z , size_x , size_y , size_z , rgba = None ):
12681280 self .scene .remove_world_object (name )
12691281 box_pose = PoseStamped ()
12701282 box_pose .header .frame_id = frame_id
@@ -1275,6 +1287,12 @@ def _create_wall(self, name, frame_id, x, y, z, size_x, size_y, size_z):
12751287 self .scene .add_box (name , box_pose , size = (size_x , size_y , size_z ))
12761288 if name not in self .wall_name_list :
12771289 self .wall_name_list .append (name )
1290+ self .object_name_color_list [name ] = rgba
1291+
1292+ # internal function for removing all walls
1293+ def _remove_all_walls (self ):
1294+ self .remove_object (self .wall_name_list )
1295+ self .wall_name_list .clear ()
12781296
12791297 # internal function for setting the colour of a wall
12801298 def _set_object_color (self , name , r , g , b , a = 0.9 ):
0 commit comments