|
| 1 | +import os |
| 2 | +import time |
| 3 | +import threading |
| 4 | + |
| 5 | +import rclpy |
| 6 | +from rclpy.node import Node |
| 7 | +from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy |
| 8 | +from geometry_msgs.msg import Point |
| 9 | +from geometry_msgs.msg import Quaternion |
| 10 | +from behavior_msgs.msg import AI2RCommandMessage |
| 11 | +from behavior_msgs.msg import AI2RObjectMessage |
| 12 | +from behavior_msgs.msg import AI2RStatusMessage |
| 13 | +from behavior_msgs.msg import AI2RNavigationMessage |
| 14 | +from behavior_msgs.msg import AI2RReceiveObjectMessage |
| 15 | + |
| 16 | +import cv2 |
| 17 | +import numpy as np |
| 18 | +from typing import List |
| 19 | +import json |
| 20 | + |
| 21 | +ros2 = {} |
| 22 | +initialized = False |
| 23 | +loggedFailure = False |
| 24 | + |
| 25 | +behavior_counter = 0 |
| 26 | +behaviors_baseline = ["SCAN", "GOTO", "RECEIVE OBJECT", "GOTO", "PLACE CHARGE ON DOOR", "GOTO"] |
| 27 | + |
| 28 | +goto_person_param = ("Person1", AI2RNavigationMessage.DEFAULT, "") |
| 29 | +goto_door_param = ("DoorPanel1", AI2RNavigationMessage.FRONT, "") |
| 30 | +goto_barrier_param = ("Barrier1", AI2RNavigationMessage.BEHIND, "") |
| 31 | +receive_charge_param = ("Charge1",) |
| 32 | +parameters = [None, goto_person_param, receive_charge_param, goto_door_param, None, goto_barrier_param] |
| 33 | + |
| 34 | +def behavior_message_callback(msg): |
| 35 | + global initialized # Access the global variables |
| 36 | + global loggedFailure |
| 37 | + global behavior_counter |
| 38 | + global behaviors_baseline |
| 39 | + global parameters |
| 40 | + robot_pose = msg.robot_mid_feet_under_pelvis_pose_in_world |
| 41 | + |
| 42 | + if not initialized: |
| 43 | + # --------- Scene ----------- |
| 44 | + scene_objects = msg.objects |
| 45 | + print("Objects in the scene:") |
| 46 | + if scene_objects: # This checks if the list is not empty |
| 47 | + for obj in scene_objects: |
| 48 | + id = obj.object_name |
| 49 | + print(f"{id}") |
| 50 | + pose_in_world = obj.object_pose_in_world |
| 51 | + pose_wrt_robot = obj.object_pose_in_robot_frame # This is the pose specified wrt to robot_pose |
| 52 | + else: |
| 53 | + print("-") |
| 54 | + |
| 55 | + # --------- Behaviors ----------- |
| 56 | + behaviors = msg.available_behaviors |
| 57 | + print("Available behaviors:") |
| 58 | + if behaviors: |
| 59 | + for behavior in behaviors: |
| 60 | + print(behavior) |
| 61 | + else: |
| 62 | + print("-") |
| 63 | + |
| 64 | + # --------- Monitoring ----------- |
| 65 | + #if (msg.completed_behavior != "-"): |
| 66 | + print("Completed Behavior: " , msg.completed_behavior, " behavior_in_progress: " , msg.behavior_in_progress) |
| 67 | + failed_behavior = msg.failed_behavior |
| 68 | + failure = msg.failure |
| 69 | + if failed_behavior != "-" and loggedFailure == False: |
| 70 | + print("[FAILURE] -----------") |
| 71 | + print("Failed behavior: " + failed_behavior) |
| 72 | + |
| 73 | + failure_info = { |
| 74 | + "Failed behavior": failed_behavior, |
| 75 | + "Description": failure.action_name, |
| 76 | + "Type": failure.action_type |
| 77 | + } |
| 78 | + |
| 79 | + if failure.missing_frame: |
| 80 | + failure_info["Missing Frame"] = failure.reference_frame |
| 81 | + if failure.collision_name != "-": |
| 82 | + failure_info["Collision with"] = failure.collision_name |
| 83 | + position_error = failure.position_error |
| 84 | + error_vector = np.array([position_error.x, position_error.y, position_error.z]) |
| 85 | + norm = np.linalg.norm(error_vector) |
| 86 | + if norm > failure.position_tolerance: |
| 87 | + failure_info["Position error"] = norm |
| 88 | + |
| 89 | + json_filename = 'failure_info.json' |
| 90 | + with open(json_filename, 'a') as json_file: |
| 91 | + json.dump(failure_info, json_file, indent=4) |
| 92 | + loggedFailure = True |
| 93 | + |
| 94 | + # --------- Coordination ----------- |
| 95 | + waiting_for_command = False |
| 96 | + if msg.behavior_in_progress == "-": |
| 97 | + waiting_for_command = True |
| 98 | + |
| 99 | + if waiting_for_command or not initialized: |
| 100 | + behavior_command = AI2RCommandMessage() |
| 101 | + # DECIDE what behavior to execute based on reasoning. For example can decide to scan environment to detect objects |
| 102 | + behavior_command.behavior_to_execute = behaviors_baseline[behavior_counter] |
| 103 | + behavior_command.adapting_behavior = False |
| 104 | + |
| 105 | + if (behavior_command.behavior_to_execute == "GOTO"): |
| 106 | + behavior_command.adapting_behavior = True |
| 107 | + new_goto_behavior = AI2RNavigationMessage() |
| 108 | + goto_parameters = parameters[behavior_counter] |
| 109 | + |
| 110 | + # Set the reference frame name - can copy from scene_objects.obj_name |
| 111 | + new_goto_behavior.target_object = goto_parameters[0] |
| 112 | + # Set the distance to the object |
| 113 | + new_goto_behavior.distance_to_object = 1.0 |
| 114 | + new_goto_behavior.pov_object = goto_parameters[2] |
| 115 | + new_goto_behavior.spatial_relation = goto_parameters[1] |
| 116 | + if new_goto_behavior.spatial_relation == AI2RNavigationMessage.DEFAULT or goto_parameters[2] == "": |
| 117 | + new_goto_behavior.pov_object = "walkingFrame" |
| 118 | + |
| 119 | + behavior_command.navigation = new_goto_behavior |
| 120 | + |
| 121 | + if (behavior_command.behavior_to_execute == "RECEIVE OBJECT"): |
| 122 | + behavior_command.adapting_behavior = True |
| 123 | + new_receive_behavior = AI2RReceiveObjectMessage() |
| 124 | + new_receive_behavior.object_name = parameters[behavior_counter][0] |
| 125 | + new_receive_behavior.side = bytes([1]) |
| 126 | + |
| 127 | + behavior_command.receive_object = new_receive_behavior |
| 128 | + |
| 129 | + print("Commanded Behavior: " + behavior_command.behavior_to_execute) |
| 130 | + ros2["behavior_publisher"].publish(behavior_command) |
| 131 | + initialized = True |
| 132 | + loggedFailure = False |
| 133 | + behavior_counter += 1 |
| 134 | + print("Completed Behavior: " , msg.completed_behavior, " behavior_in_progress: " , msg.behavior_in_progress) |
| 135 | + |
| 136 | +def main(args=None): |
| 137 | + rclpy.init(args=args) |
| 138 | + |
| 139 | + qos_profile_reliable = QoSProfile( |
| 140 | + reliability=QoSReliabilityPolicy.BEST_EFFORT, |
| 141 | + history=QoSHistoryPolicy.KEEP_LAST, |
| 142 | + depth=1 |
| 143 | + ) |
| 144 | + |
| 145 | + node = rclpy.create_node('behavior_coordination_node') |
| 146 | + ros2["node"] = node |
| 147 | + |
| 148 | + behavior_subscriber = node.create_subscription(AI2RStatusMessage, |
| 149 | + '/ihmc/behavior_tree/ai2r_status', |
| 150 | + behavior_message_callback, qos_profile_reliable) |
| 151 | + ros2["behavior_subscriber"] = behavior_subscriber |
| 152 | + |
| 153 | + behavior_publisher = node.create_publisher(AI2RCommandMessage, |
| 154 | + '/ihmc/behavior_tree/ai2r_command', |
| 155 | + qos_profile_reliable) |
| 156 | + ros2["behavior_publisher"] = behavior_publisher |
| 157 | + |
| 158 | + # Wait until interrupt |
| 159 | + try: |
| 160 | + rclpy.spin(node) |
| 161 | + except KeyboardInterrupt: |
| 162 | + node.destroy_node() |
| 163 | + |
| 164 | +if __name__ == '__main__': |
| 165 | + main() |
0 commit comments