Skip to content

Commit fe05d7b

Browse files
Added calibration movement sequence based on tank reference
1 parent a99f6e1 commit fe05d7b

File tree

1 file changed

+220
-4
lines changed

1 file changed

+220
-4
lines changed

arm_commander/commander_moveit.py

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import arm_commander.tools.pose_tools as pose_tools
3333
from arm_commander.states import GeneralCommanderStates, ControllerState
3434

35+
from moveit_calibration_detector.srv import TargetDimension, IsTargetDetected
36+
3537
class 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

Comments
 (0)