From 20966e8ce14144ea7d7a9c3d917d9e8916031ae3 Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Tue, 9 Jul 2024 21:47:48 +0100 Subject: [PATCH 1/5] feat: only describe the features of guest1 and guest2, and only do it to each other. --- .../src/receptionist/states/introduce.py | 22 ++++++++++++++----- .../states/introduce_and_seat_guest.py | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/tasks/receptionist/src/receptionist/states/introduce.py b/tasks/receptionist/src/receptionist/states/introduce.py index 2f14012ea..07e9eaa9a 100644 --- a/tasks/receptionist/src/receptionist/states/introduce.py +++ b/tasks/receptionist/src/receptionist/states/introduce.py @@ -11,7 +11,9 @@ from typing import Dict, List, Any, Optional -def stringify_guest_data(guest_data: Dict[str, Any], guest_id: str) -> str: +def stringify_guest_data( + guest_data: Dict[str, Any], guest_id: str, describe_features: bool +) -> str: """Converts the guest data for a specified guest into a string that can be used for the robot to introduce the guest to the other guests/host. @@ -50,7 +52,7 @@ def stringify_guest_data(guest_data: Dict[str, Any], guest_id: str) -> str: guest_str += f"{relevant_guest_data['name']}, their favourite drink is {relevant_guest_data['drink']}. " - if not relevant_guest_data["detection"]: + if not relevant_guest_data["detection"] or not describe_features: return guest_str filtered_attributes = {} @@ -179,16 +181,19 @@ def isSingular(attribute: str): class GetStrGuestData(smach.State): - def __init__(self, guest_id: str): + def __init__(self, guest_id: str, describe_features: bool = False): super().__init__( outcomes=["succeeded"], input_keys=["guest_data"], output_keys=["guest_str"], ) self._guest_id = guest_id + self._describe_features = describe_features def execute(self, userdata: UserData) -> str: - guest_str = stringify_guest_data(userdata.guest_data, self._guest_id) + guest_str = stringify_guest_data( + userdata.guest_data, self._guest_id, self._describe_features + ) userdata.guest_str = guest_str return "succeeded" @@ -229,6 +234,7 @@ def __init__( self, guest_to_introduce: str, guest_to_introduce_to: Optional[str] = None, + describe_features: Optional[bool] = False, everyone: Optional[bool] = False, ): super().__init__( @@ -241,7 +247,9 @@ def __init__( if everyone: smach.StateMachine.add( "GetStrGuestData", - GetStrGuestData(guest_id=guest_to_introduce), + GetStrGuestData( + guest_id=guest_to_introduce, describe_features=describe_features + ), transitions={"succeeded": "SayIntroduce"}, ) smach.StateMachine.add( @@ -260,7 +268,9 @@ def __init__( else: smach.StateMachine.add( "GetStrGuestData", - GetStrGuestData(guest_id=guest_to_introduce), + GetStrGuestData( + guest_id=guest_to_introduce, describe_features=describe_features + ), transitions={"succeeded": "GetGuestName"}, ) diff --git a/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py b/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py index 8579e637b..41fada9d5 100644 --- a/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py +++ b/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py @@ -1041,6 +1041,7 @@ def execute(self, userdata): Introduce( guest_to_introduce=guest_id, guest_to_introduce_to=guest_to_introduce_to, + describe_features=guest_to_introduce_to != "host", ), transitions={ "succeeded": ( From a9699e45edc9f8e4e334486a3e6e8608a01a279e Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Tue, 9 Jul 2024 21:53:14 +0100 Subject: [PATCH 2/5] feat: try and use the least amount of words possible. --- .../src/receptionist/states/introduce.py | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tasks/receptionist/src/receptionist/states/introduce.py b/tasks/receptionist/src/receptionist/states/introduce.py index 07e9eaa9a..d2915ca3f 100644 --- a/tasks/receptionist/src/receptionist/states/introduce.py +++ b/tasks/receptionist/src/receptionist/states/introduce.py @@ -109,6 +109,9 @@ def stringify_guest_data( if len(top_4_attributes) < 4: top_4_attributes.append(sorted_attributes[4]) + wearing_items = [] + not_wearing_items = [] + for i in range(len(top_4_attributes)): attribute_name = top_4_attributes[i] attribute_value = filtered_attributes[top_4_attributes[i]] @@ -117,25 +120,43 @@ def stringify_guest_data( if attribute_name == "hair": hair_shape = attribute_value["hair_shape"] hair_colour = attribute_value["hair_colour"] - guest_str += f"They have {hair_shape} and {hair_colour} . " + guest_str += f"They have {hair_shape} and {hair_colour}. " elif attribute_name == "facial_hair": if confidence < 0: guest_str += "They don't have facial hair. " else: guest_str += "They have facial hair. " else: + attribute = attribute_value["attribute"] if confidence < 0: - if isSingular(attribute_value["attribute"]): - guest_str += ( - f"They are not wearing a {attribute_value['attribute']}." - ) + if isSingular(attribute): + not_wearing_items.append(f"a {attribute}") else: - guest_str += f"They are not wearing {attribute_value['attribute']}." + not_wearing_items.append(attribute) else: - if isSingular(attribute_value["attribute"]): - guest_str += f"They are wearing a {attribute_value['attribute']}." + if isSingular(attribute): + wearing_items.append(f"a {attribute}") else: - guest_str += f"They are wearing {attribute_value['attribute']}." + wearing_items.append(attribute) + + def grammatical_concat(items): + if len(items) > 1: + return ", ".join(items[:-1]) + " and " + items[-1] + elif items: + return items[0] + else: + return "" + + # Combine wearing and not wearing items into guest_str + if wearing_items: + guest_str += "They are wearing " + grammatical_concat(wearing_items) + if not_wearing_items: + if wearing_items: + guest_str += "They are also not wearing " + grammatical_concat( + not_wearing_items + ) + else: + guest_str += "They are not wearing " + grammatical_concat(not_wearing_items) return guest_str From b13a364c7353720a8999e4c8e37c53dcab94897f Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Wed, 10 Jul 2024 11:47:11 +0100 Subject: [PATCH 3/5] feat: look left to guest. --- tasks/receptionist/src/receptionist/state_machine.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks/receptionist/src/receptionist/state_machine.py b/tasks/receptionist/src/receptionist/state_machine.py index 8d7dee20e..f1f8be629 100755 --- a/tasks/receptionist/src/receptionist/state_machine.py +++ b/tasks/receptionist/src/receptionist/state_machine.py @@ -280,6 +280,16 @@ def _guide_guest(self, guest_id: int) -> None: smach.StateMachine.add( f"SAY_WAIT_GUEST_{guest_id}", Say(text="Please wait here on my left."), + transitions={ + "succeeded": f"LOOK_EYES_{guest_id}", + "preempted": "failed", + "aborted": "failed", + }, + ) + + smach.StateMachine.add( + f"LOOK_EYES_{guest_id}", + PlayMotion(motion_name="look_very_left"), transitions={ "succeeded": f"INTRODUCE_AND_SEAT_GUEST_{guest_id}", "preempted": "failed", From c438609c351ccea8734cb1e0651a7ea86a20b563 Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Wed, 10 Jul 2024 11:48:18 +0100 Subject: [PATCH 4/5] fix: correct grammar --- .../src/receptionist/states/introduce.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tasks/receptionist/src/receptionist/states/introduce.py b/tasks/receptionist/src/receptionist/states/introduce.py index d2915ca3f..b0f091012 100644 --- a/tasks/receptionist/src/receptionist/states/introduce.py +++ b/tasks/receptionist/src/receptionist/states/introduce.py @@ -48,9 +48,7 @@ def stringify_guest_data( }, ) - guest_str = "" - - guest_str += f"{relevant_guest_data['name']}, their favourite drink is {relevant_guest_data['drink']}. " + guest_str = f"{relevant_guest_data['name']}, their favourite drink is {relevant_guest_data['drink']}. " if not relevant_guest_data["detection"] or not describe_features: return guest_str @@ -149,14 +147,18 @@ def grammatical_concat(items): # Combine wearing and not wearing items into guest_str if wearing_items: - guest_str += "They are wearing " + grammatical_concat(wearing_items) + guest_str += "They are wearing " + grammatical_concat(wearing_items) + ". " if not_wearing_items: if wearing_items: - guest_str += "They are also not wearing " + grammatical_concat( - not_wearing_items + guest_str += ( + "They are also not wearing " + + grammatical_concat(not_wearing_items) + + "." ) else: - guest_str += "They are not wearing " + grammatical_concat(not_wearing_items) + guest_str += ( + "They are not wearing " + grammatical_concat(not_wearing_items) + "." + ) return guest_str @@ -255,7 +257,7 @@ def __init__( self, guest_to_introduce: str, guest_to_introduce_to: Optional[str] = None, - describe_features: Optional[bool] = False, + describe_features: bool = False, everyone: Optional[bool] = False, ): super().__init__( From 7bf5456f8649dfc42e8d1fbf43f2c9c41bafc7d7 Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Wed, 10 Jul 2024 11:48:40 +0100 Subject: [PATCH 5/5] fix: make looking more consistent. --- .../states/introduce_and_seat_guest.py | 60 ++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py b/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py index 41fada9d5..54193976e 100644 --- a/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py +++ b/tasks/receptionist/src/receptionist/states/introduce_and_seat_guest.py @@ -466,7 +466,7 @@ class GetLookPoint(smach.State): def __init__(self, guest_id: str): smach.State.__init__( self, - outcomes=["succeeded"], + outcomes=["succeeded", "failed"], input_keys=["matched_face_detections"], output_keys=["look_point"], ) @@ -476,13 +476,14 @@ def execute(self, userdata): if len(userdata.matched_face_detections) == 0: userdata.look_point = PointStamped() rospy.logwarn(f"Failed to find guest: {self._guest_id}") - return "succeeded" + return "failed" for detection in userdata.matched_face_detections: if detection.name == self._guest_id: look_point = PointStamped( point=detection.point, header=Header(frame_id="map") ) + look_point.point.z = 0.75 # fixed height userdata.look_point = look_point return "succeeded" @@ -518,10 +519,13 @@ def execute(self, userdata): return "succeeded_sofa" if len(userdata.empty_seat_detections) > 0: - userdata.seat_position = PointStamped( + seat_position = PointStamped( point=userdata.empty_seat_detections[0].point, header=Header(frame_id="map"), ) + seat_position.point.z = 0.5 # fixed height + userdata.seat_position = seat_position + return "succeeded_chair" return "failed" @@ -633,7 +637,20 @@ def execute(self, userdata): smach.StateMachine.add( f"GET_LOOK_POINT_{guest_to_introduce_to}", GetLookPoint(guest_to_introduce_to), - transitions={"succeeded": f"LOOK_AT_{guest_to_introduce_to}"}, + transitions={ + "succeeded": f"LOOK_AT_{guest_to_introduce_to}", + "failed": f"LOOK_CENTRE_BACKUP_{guest_to_introduce_to}", + }, + ) + + smach.StateMachine.add( + f"LOOK_CENTRE_BACKUP_{guest_to_introduce_to}", + PlayMotion(motion_name="look_centre"), + transitions={ + "succeeded": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + "aborted": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + "preempted": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + }, ) # Look at the guest to introduce to @@ -654,6 +671,7 @@ def execute(self, userdata): Introduce( guest_to_introduce=guest_id, guest_to_introduce_to=guest_to_introduce_to, + describe_features=guest_to_introduce_to != "host", ), transitions={ "succeeded": f"LOOK_AT_WAITING_GUEST_{guest_id}_{guest_to_introduce_to}", @@ -675,6 +693,7 @@ def execute(self, userdata): Introduce( guest_to_introduce=guest_to_introduce_to, guest_to_introduce_to=guest_id, + describe_features=guest_to_introduce_to != "host", ), transitions={ "succeeded": ( @@ -692,7 +711,17 @@ def execute(self, userdata): transitions={ "succeeded_sofa": "SAY_SOFA", "succeeded_chair": "SAY_CHAIR", - "failed": "SAY_ANY", + "failed": "LOOK_CENTRE_SEAT", + }, + ) + + smach.StateMachine.add( + "LOOK_CENTRE_SEAT", + PlayMotion(motion_name="look_centre"), + transitions={ + "succeeded": "SAY_ANY", + "aborted": "SAY_ANY", + "preempted": "SAY_ANY", }, ) @@ -897,7 +926,7 @@ def __init__(self, guest_id: str): def execute(self, userdata): if len(userdata.matched_face_detections) == 0: userdata.look_point = PointStamped() - return "succeeded" + return "failed" for detection in userdata.matched_face_detections: if detection.name == self._guest_id: @@ -938,10 +967,12 @@ def execute(self, userdata): return "succeeded_sofa" if len(userdata.empty_seat_detections) > 0: - userdata.seat_position = PointStamped( + seat_position = PointStamped( point=userdata.empty_seat_detections[0][0].point, header=Header(frame_id="map"), ) + seat_position.point.z = 0.5 + userdata.seat_position = seat_position return "succeeded_chair" return "failed" @@ -1022,7 +1053,20 @@ def execute(self, userdata): smach.StateMachine.add( f"GET_LOOK_POINT_{guest_to_introduce_to}", GetLookPoint(guest_to_introduce_to), - transitions={"succeeded": f"LOOK_AT_{guest_to_introduce_to}"}, + transitions={ + "succeeded": f"LOOK_AT_{guest_to_introduce_to}", + "failed": "LOOK_CENTRE", + }, + ) + + smach.StateMachine.add( + "LOOK_CENTRE", + PlayMotion(motion_name="look_centre"), + transitions={ + "succeeded": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + "aborted": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + "preempted": f"INTRODUCE_{guest_id}_TO_{guest_to_introduce_to}", + }, ) smach.StateMachine.add(