-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
16 changed files
with
1,294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# RAI HMI | ||
|
||
The RAI HMI (Human-Machine Interface) allows users to converse with the robot and order new tasks to be added to the queue. Communication topics use plaintext, but can be connected from ASR (Automatic Speech Recognition) and to TTS (Text To Speech) nodes to enable a voice interface. | ||
|
||
> **NOTE:** Currently the node is tailored for the Husarion ROSBot XL demo use case. It is expected that it can be generalized to other use cases when `rai_whoami` package is fully developed and integrated. | ||
## ROS 2 Interface | ||
|
||
### Subscribed Topics | ||
|
||
- **`from_human`** (`std_msgs/String`): Incoming plaintext messages from the user. | ||
|
||
### Published Topics | ||
|
||
- **`to_human`** (`std_msgs/String`): Outgoing plaintext messages for the user. | ||
- **`task_addition_requests`** (`std_msgs/String`): Tasks to be added to the queue, in JSON format. | ||
|
||
## Task JSON Schema | ||
|
||
Tasks published on the `task_addition_request` follow the schema below: | ||
|
||
```json | ||
{ | ||
"$schema": "http://json-schema.org/draft-04/schema#", | ||
"type": "object", | ||
"properties": { | ||
"name": { | ||
"type": "string" | ||
}, | ||
"description": { | ||
"type": "string" | ||
}, | ||
"priority": { | ||
"type": "string", | ||
"enum": ["highest", "high", "medium", "low", "lowest"] | ||
}, | ||
"robot": { | ||
"type": "string" | ||
} | ||
}, | ||
"required": ["name", "description", "priority"] | ||
} | ||
``` | ||
|
||
Optional field `"robot"` is currently not implemented and is intended to be used in a fleet setting, where a specific robot can be requested to perform the task in question. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?xml version="1.0"?> | ||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> | ||
<package format="3"> | ||
<name>rai_hmi</name> | ||
<version>0.1.0</version> | ||
<description>A Human-Machine Interface (HMI) to converse with the robot.</description> | ||
<maintainer email="[email protected]">Adam Gotlib</maintainer> | ||
<license>Apache-2.0</license> | ||
|
||
<test_depend>python3-pytest</test_depend> | ||
|
||
<export> | ||
<build_type>ament_python</build_type> | ||
</export> | ||
</package> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright (C) 2024 Robotec.AI | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Copyright (C) 2024 Robotec.AI | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
|
||
import uuid | ||
from typing import List | ||
|
||
from langchain.tools import tool | ||
from rai.agents.conversational_agent import create_conversational_agent | ||
from rai.node import RaiBaseNode | ||
from rai.tools.ros.native import GetCameraImage, Ros2GetRobotInterfaces | ||
from rai.utils.model_initialization import get_llm_model | ||
|
||
from rai_hmi.base import BaseHMINode | ||
from rai_hmi.chat_msgs import MissionMessage | ||
from rai_hmi.task import Task, TaskInput | ||
from rai_hmi.text_hmi_utils import Memory | ||
|
||
|
||
def initialize_agent(hmi_node: BaseHMINode, rai_node: RaiBaseNode, memory: Memory): | ||
llm = get_llm_model(model_type="complex_model") | ||
|
||
@tool | ||
def get_mission_memory(uid: str) -> List[MissionMessage]: | ||
"""List mission memory. It contains the information about running tasks. Mission uid is required.""" | ||
return memory.get_mission_memory(uid) | ||
|
||
@tool | ||
def submit_mission_to_the_robot(task: TaskInput): | ||
"""Use this tool submit the task to the robot. The task will be handled by the executor part of your system.""" | ||
|
||
uid = uuid.uuid4() | ||
hmi_node.execute_mission( | ||
Task( | ||
name=task.name, | ||
description=task.description, | ||
priority=task.priority, | ||
uid=str(uid), | ||
) | ||
) | ||
return f"UID={uid} | Task added to the queue: {task.model_dump_json()}" | ||
|
||
node_tools = tools = [ | ||
Ros2GetRobotInterfaces(node=rai_node), | ||
GetCameraImage(node=rai_node), | ||
] | ||
task_tools = [submit_mission_to_the_robot, get_mission_memory] | ||
tools = hmi_node.tools + node_tools + task_tools | ||
|
||
agent = create_conversational_agent( | ||
llm, tools, hmi_node.system_prompt, logger=hmi_node.get_logger() | ||
) | ||
return agent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Copyright (C) 2024 Robotec.AI | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import re | ||
from abc import abstractmethod | ||
from queue import Queue | ||
|
||
from rclpy.callback_groups import ReentrantCallbackGroup | ||
from rclpy.qos import DurabilityPolicy, HistoryPolicy, QoSProfile, ReliabilityPolicy | ||
from std_msgs.msg import String | ||
|
||
from rai_hmi.base import BaseHMINode | ||
|
||
|
||
def split_message(message: str): | ||
sentences = re.split(r"(?<=\.)\s|[:!]", message) | ||
for sentence in sentences: | ||
if sentence: | ||
yield sentence | ||
|
||
|
||
class GenericVoiceNode(BaseHMINode): | ||
def __init__(self, node_name: str, queue: Queue, robot_description_package: str): | ||
super().__init__(node_name, queue, robot_description_package) | ||
|
||
self.callback_group = ReentrantCallbackGroup() | ||
reliable_qos = QoSProfile( | ||
reliability=ReliabilityPolicy.RELIABLE, | ||
durability=DurabilityPolicy.TRANSIENT_LOCAL, | ||
history=HistoryPolicy.KEEP_ALL, | ||
) | ||
self.hmi_subscription = self.create_subscription( | ||
String, | ||
"/from_human", | ||
self.handle_human_message, | ||
qos_profile=reliable_qos, | ||
) | ||
self.hmi_publisher = self.create_publisher( | ||
String, | ||
"/to_human", | ||
qos_profile=reliable_qos, | ||
callback_group=self.callback_group, | ||
) | ||
|
||
@abstractmethod | ||
def _handle_human_message(self, msg: String): | ||
pass | ||
|
||
def handle_human_message(self, msg: String): | ||
self.processing = True | ||
self._handle_human_message(msg) | ||
self.processing = False |
Oops, something went wrong.