3232import arm_commander .tools .pose_tools as pose_tools
3333from arm_commander .states import GeneralCommanderStates , ControllerState
3434
35+ from moveit_calibration_detector .srv import TargetDimension , IsTargetDetected
36+
3537class GeneralCommander ():
3638 """
3739 GeneralCommander: an interface for robot commander, with this class specifically interfacing
@@ -192,12 +194,15 @@ def _cb_handle_result_status(self, msg):
192194 self .commander_state .message = 'NO ERROR'
193195 return
194196 elif msg .status .status in [GoalStatus .ABORTED ]:
195- self .commander_state = GeneralCommanderStates .ABORTED
196- rospy .logerr (f'The commander: received ABORTED result' )
197+ self .commander_state = GeneralCommanderStates .ABORTED
198+ rospy .logerr (f'The commander: received ABORTED result' )
199+ self .abort_move ()
200+ rospy .logerr (f'The commander: the previous goal has been preempted - abort_move() called in-case move was called asynchrously and goal preemption was external' )
197201 elif msg .status .status in [GoalStatus .PREEMPTED ]:
198202 # just ignore the preempted goal - should not happen (TODO: assumption is wrong)
199203 self .commander_state = GeneralCommanderStates .ABORTED
200- rospy .logerr (f'The commander: the previous goal has been preempted' )
204+ self .abort_move ()
205+ rospy .logerr (f'The commander: the previous goal has been preempted - abort_move() called in-case move was called asynchrously and goal preemption was external' )
201206 else :
202207 self .commander_state = GeneralCommanderStates .ERROR
203208 self .commander_state .message = MOVEIT_ERROR_CODE_MAP [msg .result .error_code .val ]
@@ -509,6 +514,7 @@ def abort_move(self, wait=True) -> None:
509514 self .move_group .clear_pose_targets ()
510515 if wait :
511516 self .wait_for_busy_end ()
517+ self .reset_state ()
512518
513519 # a blocking call to wait until the current command has left the BUSY state
514520 def wait_for_busy_end (self ) -> GeneralCommanderStates :
@@ -1180,6 +1186,30 @@ def add_sphere_to_scene(self, object_name:str, radius:float, xyz:list, reference
11801186 self .object_name_color_list [object_name ] = rgba
11811187 self ._pub_transform_object (object_name , object_pose )
11821188
1189+ # add a box (a box of given dimension, position and orientation)
1190+ def add_box_to_scene_by_pose (self , object_name :str , dimensions :list , pose :Pose , reference_frame :str = None , rgba = None ) -> None :
1191+ """ Add a box to the scene for collision avoidance in path planning
1192+
1193+ :param object_name: The name given to the new scene object
1194+ :type object_name: str
1195+ :param dimensions: The dimensions of the box as a list of 3 floats
1196+ :type radius: list
1197+ :param pose: The Pose of the box center
1198+ :type pose: Pose
1199+ :param reference_frame: The frame of reference of the xyz and rpy
1200+ :type reference_frame: str, default to WORLD_REFERENCE_LINK
1201+ """
1202+ reference_frame = self .WORLD_REFERENCE_LINK if reference_frame is None else reference_frame
1203+ object_pose = PoseStamped ()
1204+ object_pose .pose .position = pose .position
1205+ object_pose .pose .orientation = pose .orientation
1206+ object_pose .header .frame_id = reference_frame
1207+ object_pose .header .stamp = rospy .Time .now ()
1208+ self .scene .add_box (object_name , object_pose , size = dimensions )
1209+ self ._wait_for_scene_update (lambda : object_name in self .scene .get_known_object_names ())
1210+ self .object_name_color_list [object_name ] = rgba
1211+ self ._pub_transform_object (object_name , object_pose , reference_frame )
1212+
11831213 # add a box (a box of given dimension, position and orientation)
11841214 def add_box_to_scene (self , object_name :str , dimensions :list , xyz :list , rpy :list = [0 , 0 , 0 ], reference_frame :str = None , rgba = None ) -> None :
11851215 """ Add a box to the scene for collision avoidance in path planning
@@ -1201,6 +1231,192 @@ def add_box_to_scene(self, object_name:str, dimensions:list, xyz:list, rpy:list=
12011231 self ._wait_for_scene_update (lambda : object_name in self .scene .get_known_object_names ())
12021232 self .object_name_color_list [object_name ] = rgba
12031233 self ._pub_transform_object (object_name , object_pose , reference_frame )
1234+
1235+ def set_charuco_detector_params (self , longest_board_size :float = 0.1970 , measured_marker_size :float = 0.0170 ) -> bool :
1236+ target_dim_srv_topic = '/handeye_target_charuco_detector/set_target_dimension'
1237+ rospy .wait_for_service (target_dim_srv_topic )
1238+ target_dim_srv = rospy .ServiceProxy (target_dim_srv_topic , TargetDimension )
1239+ response = target_dim_srv (longest_board_size , measured_marker_size )
1240+ return response .success
1241+
1242+ def is_target_visible (self , timeout :float = 10.0 ) -> bool :
1243+ target_visible_srv_topic = '/handeye_target_charuco_detector/is_target_detected'
1244+ rospy .wait_for_service (target_visible_srv_topic )
1245+ target_visible_srv = rospy .ServiceProxy (target_visible_srv_topic , IsTargetDetected )
1246+ start_time = rospy .get_time ()
1247+ while (rospy .get_time () - start_time < timeout ) and not rospy .is_shutdown ():
1248+ response = target_visible_srv ()
1249+ rospy .logerr (f'Tag visible: { response .detected } , waiting { rospy .get_time () - start_time :.1f} /{ timeout } seconds' )
1250+ if response .detected :
1251+ return True
1252+ time .sleep (0.5 )
1253+ return False
1254+
1255+ def capture_target_sequence (self , script_file :str ) -> bool :
1256+
1257+ import yaml
1258+ # Read
1259+ # TODO: set the path properly
1260+ with open (f'/home/qcr/cgras2025_ws/src/scripts/calibration/{ script_file } ' , newline = '' ) as the_file :
1261+ joint_state_dict = yaml .safe_load (the_file )
1262+ joint_values = joint_state_dict ['joint_values' ]
1263+ print (f"Number of locations read: { len (joint_values )} " )
1264+
1265+ for idx , joint_position in enumerate (joint_values ):
1266+ print (f"Moving to joint position { idx + 1 } /{ len (joint_values )} : { joint_position } \n " )
1267+
1268+ try :
1269+ self .move_to_joint_pose (joint_position )
1270+ # TODO: Capture the pose
1271+ except Exception as e :
1272+ print (f"Failed to move to joint position { idx + 1 } /{ len (joint_values )} : { joint_position } " )
1273+ print (e )
1274+ return False
1275+
1276+ time .sleep (1 )
1277+
1278+ return True
1279+
1280+
1281+
1282+ def calibrate_tank (self , reference_frame :str = None , reference_pose :Pose = None ) -> Pose :
1283+ self .move_to_named_pose ('named_poses.stow' , True )
1284+ center = Pose ()
1285+
1286+ ## New 3 Tag Setup - Tank 1
1287+ tag_A_script = 'tank-1-tag-A.yaml'
1288+ tag_B_script = 'tank-1-tag-B.yaml'
1289+ tag_C_script = 'tank-1-tag-C.yaml'
1290+ ## New 3 Tag Setup - Tank 0
1291+ tag_D_script = 'tank-0-tag-D.yaml'
1292+ tag_E_script = 'tank-0-tag-E.yaml'
1293+ tag_F_script = 'tank-0-tag-F.yaml'
1294+
1295+ calibration_sequence = ()
1296+ tank_ready_named_pose = ''
1297+ tag_1 = ''
1298+ tag_2 = ''
1299+ tag_3 = ''
1300+ tag_1_script = ''
1301+ tag_2_script = ''
1302+ tag_3_script = ''
1303+
1304+ if reference_frame .endswith ('0' ):
1305+ tank_ready_named_pose = 'named_poses.tank-0'
1306+ tag_1 = 'named_poses.tag-D'
1307+ tag_2 = 'named_poses.tag-E'
1308+ tag_3 = 'named_poses.tag-F'
1309+
1310+ tag_1_script = tag_D_script
1311+ tag_2_script = tag_E_script
1312+ tag_3_script = tag_F_script
1313+
1314+ elif reference_frame .endswith ('1' ):
1315+ tank_ready_named_pose = 'named_poses.tank-1'
1316+ tag_1 = 'named_poses.tag-A'
1317+ tag_2 = 'named_poses.tag-B'
1318+ tag_3 = 'named_poses.tag-C'
1319+
1320+ tag_1_script = tag_A_script
1321+ tag_2_script = tag_B_script
1322+ tag_3_script = tag_C_script
1323+
1324+ # Move to Ready Pose
1325+ self .move_to_named_pose ('named_poses.ready' , True )
1326+
1327+ # Set the detector to tank tag size
1328+ detector_init_success = self .set_charuco_detector_params (longest_board_size = 0.1970 , measured_marker_size = 0.0170 )
1329+ if not detector_init_success :
1330+ rospy .logerr ('The commander (calibrate_tank): failed to set the charuco detector parameters' )
1331+ return center # TODO: return error code
1332+
1333+ # Move to Tank Ready Pose
1334+ self .move_to_named_pose (tank_ready_named_pose , True )
1335+
1336+ # Move to Tag 1
1337+ self .move_to_named_pose (tag_1 , True )
1338+ # -- Ensure tag is visible
1339+ self .is_target_visible (timeout = 10.0 )
1340+ # -- Calibration sequence for tag_1
1341+ sequence_result = self .capture_target_sequence (tag_1_script )
1342+ if not sequence_result :
1343+ rospy .logerr ('The commander (calibrate_tank): failed to capture the target sequence for tag 1' )
1344+ # TODO: return to safe pose
1345+ return center
1346+
1347+ # Move to Tag 2
1348+ self .move_to_named_pose (tag_2 , True )
1349+ # -- Ensure tag is visible
1350+ self .is_target_visible (timeout = 10.0 )
1351+ # -- Calibration sequence for tag_2
1352+ sequence_result = self .capture_target_sequence (tag_2_script )
1353+ if not sequence_result :
1354+ rospy .logerr ('The commander (calibrate_tank): failed to capture the target sequence for tag 1' )
1355+ # TODO: return to safe pose
1356+ return center
1357+
1358+ # Move to Tag 3
1359+ self .move_to_named_pose (tag_3 , True )
1360+ # -- Ensure tag is visible
1361+ self .is_target_visible (timeout = 10.0 )
1362+ # -- Calibration sequence for tag_3
1363+ sequence_result = self .capture_target_sequence (tag_3_script )
1364+ if not sequence_result :
1365+ rospy .logerr ('The commander (calibrate_tank): failed to capture the target sequence for tag 1' )
1366+ # TODO: return to safe pose
1367+ return center
1368+
1369+ # Move to Tank Ready Pose
1370+ self .move_to_named_pose (tank_ready_named_pose , True )
1371+ self .move_to_named_pose ('named_poses.stow' , True )
1372+
1373+ if reference_frame .endswith ('0' ):
1374+ # lookup transform from /robot_footprint to /tank_center
1375+ center_tf = self .tf_buffer .lookup_transform (self .WORLD_REFERENCE_LINK , 'tank_center' , rospy .Time (0 ), rospy .Duration (4.0 ))
1376+ center .position .x = center_tf .transform .translation .x
1377+ center .position .y = center_tf .transform .translation .y
1378+ center .position .z = center_tf .transform .translation .z
1379+ center .orientation .x = center_tf .transform .rotation .x
1380+ center .orientation .y = center_tf .transform .rotation .y
1381+ center .orientation .z = center_tf .transform .rotation .z
1382+ center .orientation .w = center_tf .transform .rotation .w
1383+
1384+ print (f'Calibrating tank { reference_frame } with WORLD_REFERENCE_LINK { self .WORLD_REFERENCE_LINK } and center: { center } ' )
1385+ #input('Press Enter to continue...')
1386+
1387+ #center.position.x = -0.029
1388+ #center.position.y = -1.184
1389+ #center.position.z = 0.197
1390+ #center.orientation.x = 0.009
1391+ #center.orientation.y = 0.003
1392+ #center.orientation.z = 0.999
1393+ #center.orientation.w = -0.036
1394+ #- Rotation: in Quaternion [0.009, 0.003, 0.999, -0.036]
1395+ elif reference_frame .endswith ('1' ):
1396+ # lookup transform from /robot_footprint to /tank_center
1397+ center_tf = self .tf_buffer .lookup_transform (self .WORLD_REFERENCE_LINK , 'tank_center' , rospy .Time (0 ), rospy .Duration (4.0 ))
1398+ center .position .x = center_tf .transform .translation .x
1399+ center .position .y = center_tf .transform .translation .y
1400+ center .position .z = center_tf .transform .translation .z
1401+ center .orientation .x = center_tf .transform .rotation .x
1402+ center .orientation .y = center_tf .transform .rotation .y
1403+ center .orientation .z = center_tf .transform .rotation .z
1404+ center .orientation .w = center_tf .transform .rotation .w
1405+
1406+ print (f'Calibrating tank { reference_frame } with WORLD_REFERENCE_LINK { self .WORLD_REFERENCE_LINK } and center: { center } ' )
1407+ #input('Press Enter to continue...')
1408+
1409+ #center.position.x = -0.006
1410+ #center.position.y = 1.188
1411+ #center.position.z = 0.218
1412+ #center.orientation.x = -0.000
1413+ #center.orientation.y = 0.000
1414+ #center.orientation.z = 0.007
1415+ #center.orientation.w = 1.000
1416+ #- Rotation: in Quaternion [-0.000, 0.000, 0.007, 1.000]
1417+
1418+ return center
1419+
12041420
12051421 # returns a list of current objects that have been added to the commander
12061422 """ Returns a list of current objects that have been added to the commander
@@ -1519,4 +1735,4 @@ def init_logger():
15191735 logger .addHandler (ch )
15201736 return logger
15211737
1522- logger = init_logger ()
1738+ logger = init_logger ()
0 commit comments