Skip to content

Commit 43998d3

Browse files
authored
refactor: demos rewrite, rai_interfaces import guards, remove legacy packages (#449)
1 parent 13dd7b5 commit 43998d3

37 files changed

+227
-2946
lines changed

examples/agriculture-demo.py

Lines changed: 16 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,13 @@
1515
import argparse
1616

1717
import rclpy
18-
from rai.node import RaiStateBasedLlmNode, describe_ros_image
19-
from rai.tools.ros.native import (
20-
GetCameraImage,
21-
GetMsgFromTopic,
22-
Ros2GenericServiceCaller,
23-
Ros2GetRobotInterfaces,
24-
Ros2ShowMsgInterfaceTool,
25-
)
18+
from langchain_core.messages import HumanMessage
19+
from langchain_core.runnables import Runnable
20+
from rai.agents.conversational_agent import State, create_conversational_agent
21+
from rai.communication.ros2.connectors import ROS2ARIConnector
22+
from rai.tools.ros2.topics import ROS2TopicsToolkit
2623
from rai.tools.time import WaitForSecondsTool
27-
from rclpy.action import ActionClient
24+
from rai.utils.model_initialization import get_llm_model
2825
from rclpy.callback_groups import ReentrantCallbackGroup
2926
from rclpy.executors import MultiThreadedExecutor
3027
from rclpy.node import Node
@@ -34,9 +31,10 @@
3431

3532

3633
class MockBehaviorTreeNode(Node):
37-
def __init__(self, tractor_number: int):
34+
def __init__(self, tractor_number: int, agent: Runnable[State, State]):
3835
super().__init__(f"mock_behavior_tree_node_{tractor_number}")
3936
self.tractor_number = tractor_number
37+
self.agent = agent
4038

4139
# Create a callback group for concurrent execution
4240
self.callback_group = ReentrantCallbackGroup()
@@ -48,11 +46,6 @@ def __init__(self, tractor_number: int):
4846
callback_group=self.callback_group,
4947
)
5048

51-
# Create action client
52-
self.perform_task_client = ActionClient(
53-
self, Task, "/perform_task", callback_group=self.callback_group
54-
)
55-
5649
# Create timer for periodic checks
5750
self.create_timer(
5851
5.0, self.check_tractor_state, callback_group=self.callback_group
@@ -79,18 +72,7 @@ async def check_tractor_state(self):
7972
goal_msg.description = ""
8073
goal_msg.task = "Anomaly detected. Please decide what to do."
8174

82-
self.perform_task_client.wait_for_server()
83-
84-
future = self.perform_task_client.send_goal_async(goal_msg)
85-
await future
86-
87-
goal_handle = future.result()
88-
if goal_handle.accepted:
89-
self.get_logger().info("Goal accepted by perform_task action server")
90-
result = await goal_handle.get_result_async()
91-
self.get_logger().info(f"Result: {result.result}")
92-
else:
93-
self.get_logger().warn("Goal rejected by perform_task action server")
75+
self.agent.invoke(State(messages=[HumanMessage(content=str(goal_msg))]))
9476

9577

9678
def main():
@@ -105,30 +87,9 @@ def main():
10587
args = parser.parse_args()
10688

10789
tractor_number = args.tractor_number
108-
tractor_prefix = f"/tractor{tractor_number}"
10990

11091
rclpy.init()
11192

112-
observe_topics = [
113-
f"{tractor_prefix}/camera_image_color",
114-
]
115-
116-
observe_postprocessors = {
117-
f"{tractor_prefix}/camera_image_color": describe_ros_image
118-
}
119-
120-
topics_allowlist = [
121-
"/rosout",
122-
f"{tractor_prefix}/camera_image_color",
123-
# Services
124-
f"{tractor_prefix}/continue",
125-
f"{tractor_prefix}/current_state",
126-
f"{tractor_prefix}/flash",
127-
f"{tractor_prefix}/replan",
128-
]
129-
130-
actions_allowlist = []
131-
13293
SYSTEM_PROMPT = f"""
13394
You are autonomous tractor {tractor_number} operating in an agricultural field. You are activated whenever the tractor stops due to an unexpected situation. Your task is to call a service based on your assessment of the situation.
13495
@@ -142,35 +103,28 @@ def main():
142103
143104
Important: You must call only one service. The tractor can only handle one service call.
144105
"""
145-
146-
rai_node = RaiStateBasedLlmNode(
147-
observe_topics=observe_topics,
148-
observe_postprocessors=observe_postprocessors,
149-
allowlist=topics_allowlist + actions_allowlist,
106+
connector = ROS2ARIConnector()
107+
agent = create_conversational_agent(
108+
llm=get_llm_model("complex_model"),
150109
system_prompt=SYSTEM_PROMPT,
151110
tools=[
152-
Ros2ShowMsgInterfaceTool,
153-
WaitForSecondsTool,
154-
GetMsgFromTopic,
155-
GetCameraImage,
156-
Ros2GetRobotInterfaces,
157-
Ros2GenericServiceCaller,
111+
*ROS2TopicsToolkit(connector=connector).get_tools(),
112+
WaitForSecondsTool(),
158113
],
159114
)
160115

161-
mock_node = MockBehaviorTreeNode(tractor_number)
116+
mock_node = MockBehaviorTreeNode(tractor_number, agent)
162117

163118
# Use a MultiThreadedExecutor to allow for concurrent execution
164119
executor = MultiThreadedExecutor()
165-
executor.add_node(rai_node)
166120
executor.add_node(mock_node)
167121

168122
try:
169123
executor.spin()
170124
except KeyboardInterrupt:
171125
pass
172126
finally:
173-
rai_node.destroy_node()
127+
connector.shutdown()
174128
mock_node.destroy_node()
175129
rclpy.shutdown()
176130

examples/rosbot-xl-demo.py

Lines changed: 29 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (C) 2024 Robotec.AI
1+
# Copyright (C) 2025 Robotec.AI
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -11,57 +11,24 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import argparse
15-
from pathlib import Path
16-
from typing import Optional
14+
15+
from typing import cast
1716

1817
import rclpy
1918
import rclpy.executors
2019
import rclpy.logging
21-
from rai.node import RaiStateBasedLlmNode
22-
from rai.tools.ros.native import (
23-
GetMsgFromTopic,
24-
Ros2GetRobotInterfaces,
25-
Ros2PubMessageTool,
26-
Ros2ShowMsgInterfaceTool,
27-
)
28-
from rai.tools.ros.native_actions import (
29-
GetTransformTool,
30-
Ros2CancelAction,
31-
Ros2GetActionResult,
32-
Ros2GetLastActionFeedback,
33-
Ros2RunActionAsync,
34-
)
20+
from langchain_core.messages import BaseMessage, HumanMessage
21+
from rai.agents.conversational_agent import State, create_conversational_agent
22+
from rai.communication.ros2.connectors import ROS2ARIConnector
23+
from rai.tools.ros2.actions import ROS2ActionToolkit
24+
from rai.tools.ros2.topics import ROS2TopicsToolkit
3525
from rai.tools.time import WaitForSecondsTool
26+
from rai.utils.model_initialization import get_llm_model
3627
from rai_open_set_vision.tools import GetDetectionTool, GetDistanceToObjectsTool
3728

38-
p = argparse.ArgumentParser()
39-
p.add_argument("--allowlist", type=Path, required=False, default=None)
40-
4129

42-
def main(allowlist: Optional[Path] = None):
30+
def main():
4331
rclpy.init()
44-
# observe_topics = [
45-
# "/camera/camera/color/image_raw",
46-
# ]
47-
#
48-
# observe_postprocessors = {"/camera/camera/color/image_raw": describe_ros_image}
49-
ros2_allowlist = []
50-
if allowlist is not None:
51-
try:
52-
content = allowlist.read_text().strip()
53-
if content:
54-
ros2_allowlist = content.splitlines()
55-
else:
56-
rclpy.logging.get_logger("rosbot_xl_demo").warning(
57-
"Allowlist file is empty"
58-
)
59-
except Exception as e:
60-
rclpy.logging.get_logger("rosbot_xl_demo").error(
61-
f"Failed to read allowlist: {e}"
62-
)
63-
else:
64-
ros2_allowlist = None
6532

6633
SYSTEM_PROMPT = """You are an autonomous robot connected to ros2 environment. Your main goal is to fulfill the user's requests.
6734
Do not make assumptions about the environment you are currently in.
@@ -114,32 +81,31 @@ def main(allowlist: Optional[Path] = None):
11481
(0.92, 1.01, 0.0)
11582
"""
11683

117-
node = RaiStateBasedLlmNode(
118-
observe_topics=None,
119-
observe_postprocessors=None,
120-
allowlist=ros2_allowlist,
84+
connector = ROS2ARIConnector()
85+
ros2_action_toolkit = ROS2ActionToolkit(connector=connector)
86+
ros2_topics_toolkit = ROS2TopicsToolkit(connector=connector)
87+
88+
agent = create_conversational_agent(
89+
llm=get_llm_model("complex_model"),
12190
system_prompt=SYSTEM_PROMPT,
12291
tools=[
123-
Ros2GetRobotInterfaces,
124-
Ros2PubMessageTool,
125-
Ros2RunActionAsync,
126-
Ros2CancelAction,
127-
Ros2GetActionResult,
128-
Ros2GetLastActionFeedback,
129-
Ros2ShowMsgInterfaceTool,
130-
GetTransformTool,
131-
WaitForSecondsTool,
132-
GetMsgFromTopic,
133-
GetDetectionTool,
134-
GetDistanceToObjectsTool,
92+
*ros2_action_toolkit.get_tools(),
93+
*ros2_topics_toolkit.get_tools(),
94+
WaitForSecondsTool(),
95+
GetDetectionTool(connector=connector, node=connector.node),
96+
GetDistanceToObjectsTool(connector=connector, node=connector.node),
13597
],
13698
)
137-
node.declare_parameter("conversion_ratio", 1.0)
99+
connector.node.declare_parameter("conversion_ratio", 1.0)
138100

139-
node.spin()
101+
state = State(messages=[])
102+
while rclpy.ok():
103+
user_input = input("Enter your message: ")
104+
state["messages"].append(HumanMessage(content=user_input))
105+
response = agent.invoke(state)
106+
cast(BaseMessage, response["messages"][-1]).pretty_print()
140107
rclpy.shutdown()
141108

142109

143110
if __name__ == "__main__":
144-
args = p.parse_args()
145-
main(**vars(args))
111+
main()

examples/taxi-demo.launch.py

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)