Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add agent side ROS #82

Open
wants to merge 5 commits into
base: rosllm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,18 @@ make html
```

There are also several helpful [tutorials](tutorials/) to help you get started with running and customizing Agent.


## ROS interface

To run the agent with Flask interface with ROS

In conda environment using the following command:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify where the user should change directory

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to follow the tutorials in last step


```
python ../src/agent/start.py task=ros_task method=direct [email protected]=human
```

Replace the ``[email protected]=human`` with your actual model.

In ROS environment follow the instruction in the ros_pange_agent packge
16 changes: 16 additions & 0 deletions Agent/configs/task/ros_task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @package _global_
agent:
prompt_builder:
template_paths:
- default


task:
_target_: agent.tasks.ros_task.ROSTask
name: simplebot_test_env
description:
subtask: null
version: v0.1

max_env_steps: 1
max_episodes: 100
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add newline at end of file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

3 changes: 3 additions & 0 deletions Agent/src/agent/prompts/templates/default/direct_prompt.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Now please answer the question.
Answer in the format
Answer: <answer>
2 changes: 1 addition & 1 deletion Agent/src/agent/prompts/templates/default/trajectory.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Here is what happened so far:
{%- if action %}
Action: {{action}}
{%- endif %}
Observation: {{memory.retrieve({memory.mem_keys.OBSERVATION: 1.0})}}
Current: {{memory.retrieve({memory.mem_keys.OBSERVATION: 1.0})}}
67 changes: 67 additions & 0 deletions Agent/src/agent/tasks/ros_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import requests


class RosApi:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want this to be a ROS specific interface, ideally this should be able to communicate with other environments - maybe HttpApi?

Make sure to update filename too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added: To change communication port, in HEBO/Agent/src/agent/tasks/http_api.py replace http://localhost:5000 with with your actual port.


# Defined on ros side
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inconsistent indentation - use black to format code

default_timeout = 3 * 60 # default timeout is 3 minutes

def __init__(self, timeout=None):
self.timeout = self.default_timeout if timeout is None else timeout
self._response = None

@staticmethod
def bad_response(obs):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation?

return {
"success": False,
"done": True,
"reward": 0,
"obs": obs,
}

def send_action(self, action):
url = "http://localhost:5000/llmreq"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

port number should be a parameter somewhere that the user can specify themselves, we can default to 5000

also llmreq not good name, replace with something better, we're not sending a request to an llm in this function, we are sending the action from the llm to the environment

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to llmact

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add
To change communication port, in HEBO/Agent/src/agent/tasks/http_api.py replace

default_ip = 'localhost'
default_port = 5000

with your actual IP address and port.

self._response = None # reset response
try:
data = {"action": action}
resp = requests.post(url, json=data, timeout=self.timeout)
response = resp.json()
except requests.exceptions.Timeout:
response = self.bad_response("Request timeout.")
except requests.exceptions.RequestException as e:
response = self.bad_response(f"Request exception: {e}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe include the traceback error, you can get this using

import traceback
tb_err = traceback.format_exc()

self._response = response

def get_env_observation(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why get_env_observation? Is get_observation not cleaner?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to get_observation

url = "http://localhost:5000/rosenv"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need better name, and port as parameter

also, documentation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cahnge to llmobs

self._response = None # reset response
try:
data = {"": ""}
resp = requests.post(url, json=data, timeout=self.timeout)
response = resp.json()
except requests.exceptions.Timeout:
response = self.bad_response("Request timeout.")
except requests.exceptions.RequestException as e:
response = self.bad_response(f"Request exception: {e}")
self._response = response
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these functions look very similar, think about re-using code


return response

def get_feedback(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we need get_feedback method, this will be handled on the ROS side

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

url = "http://localhost:5000/rosfdb"
self._response = None # reset response
try:
data = {"": ""}
resp = requests.post(url, json=data, timeout=self.timeout)
response = resp.json()
except requests.exceptions.Timeout:
response = self.bad_response("Request timeout.")
except requests.exceptions.RequestException as e:
response = self.bad_response(f"Request exception: {e}")
self._response = response

return response

def receive_response(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation? also, where is this used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

assert self._response is not None, "did not receive a response"
return self._response
51 changes: 51 additions & 0 deletions Agent/src/agent/tasks/ros_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from agent.tasks import Task
from .ros_api import RosApi
from typing import Any, Dict
import warnings


class ROSTask(Task):

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ros_api = RosApi()
print(self.ros_api)
self.response = {"reward": 0.0,
"done": False,
"obs": ""}
self.possible_actions = []

def answer_parser(self, raw_response: str):
return raw_response

def is_complete(self):
return self.response['done']

def reset(self, next_subtask: str | None = None) -> Dict[str, str]:
"""Reset the environment and return the initial observation."""

if next_subtask is not None:
warnings.warn("ros_task does not support subtasks, ignoring subtask")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a warning case or failing case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed


response= self.ros_api.get_env_observation()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format code with black


return {
"_text_obs": response,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the underscore? looks ugly, maybe this is to do with ninja or related to agent code?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

"_available_actions": self.possible_actions
}

def get_observation(self):
obs = self.ros_api.get_env_observation()
fdb = self.ros_api.get_feedback()
Comment on lines +38 to +39
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont see any need for two function, this can be merged into a single function and then we can handle the human-feedback on the ROS side

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

return {
"_available_actions": obs,
"_available_actions": fdb
}

def step(self, action):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation?

print(action)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove print statement

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

self.ros_api.send_action(action)
self.response = self.ros_api.receive_response()
print(self.response['obs'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove print statement

Also, it would be good to have a way to save this data, @AntGro does the agent code have functionality for automatically saving prompts/responses? Perhaps this is a flag that we can set somewhere?

Otherwise, I suggest we add this - would be useful to add somewhere in the agent code, but I am not sure where would be best?

return {}, self.response["reward"], self.response["done"]

15 changes: 2 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ Huawei, Noah's Ark Lab.
- [Enhancing Reinforcement Learning Agents with Local Guides](RLLG)
- [Sauté RL and Simmer RL: Safe Reinforcement Learning Using Safety State Augmentation ](./SIMMER)
- [Model-Based Offline Reinforcement Learning with Pessimism-Modulated Dynamics Belief](./PMDB)
- Robotics Research
- [ROS-LLM: A ROS framework for embodied AI with task feedback and structured reasoning](./ROSLLM)
Comment on lines -20 to -21
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert, we want this to appear

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added


Further instructions are provided in the README files associated to each project.

Expand Down Expand Up @@ -91,7 +89,7 @@ design, and logic synthesis for electronic design automation.
## [RDUCB: High Dimensional Bayesian Optimisation with Random Decompositions](./RDUCB)

<p align="center">
<img src="./RDUCB/figures/ToyProblem.PNG" width="400" />
<img src="./RDUCB/figures/ToyProblem.PNG" width="400" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove trailing whitespace

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from HEBO readme, I don't think we should change it

</p>

Codebase associated
Expand Down Expand Up @@ -168,7 +166,7 @@ efficiency and QoR values.

<p float="center">
<img src="./T-LBO/figures/LSBO.png" width="400" />
<img src="./T-LBO/figures/magnets.png" width="400" />
<img src="./T-LBO/figures/magnets.png" width="400" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove trailing whitespace

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from HEBO readme, I don't think we should change it

</p>

Codebase associated
Expand Down Expand Up @@ -306,12 +304,3 @@ policy-dependent reweighting factor, termed *Pessimism-Modulated Dynamics Belief
iterative regularized policy optimization algorithm for the game, with guarantee of monotonous improvement under certain
condition. To make practical, we further devise an offline RL algorithm to approximately find the solution. Empirical
results show that the proposed approach achieves state-of-the-art performance on a wide range of benchmark tasks.

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Codebase Contributors

<strong> Current contributors: </strong> Antoine Grosnit, Alexandre Max Maravel, Taher Jafferjee, Wenlong Lyu, Kaiyang Guo, Juliusz Ziomek, Paul Daoudi, Merwan Barlier, Christopher E. Mower.

<strong> Alumni / External contributors: </strong> Alexander I. Cowen-Rivers, Kamil Dreczkowski, Aivar Sootla, Ryan Rhys Griffiths, Zhi Wang, Ludovic Dos Santos, Bogdan Robu, Christophe Prieur.

Comment on lines -309 to -317
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not have been removed, I am not sure how this happened? Anyway, please revert and also @W9YBZ you can add your name to current contributors.

28 changes: 28 additions & 0 deletions ROSLLM/ros_agent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.0.2)
project(ros_agent)

find_package(catkin REQUIRED COMPONENTS
rospy
std_msgs
message_generation
)

# catkin_python_setup()

add_service_files(
FILES
HandleAgentAction.srv
)

generate_messages(
DEPENDENCIES
std_msgs
)

catkin_package(
CATKIN_DEPENDS message_runtime
)

include_directories(
${catkin_INCLUDE_DIRS}
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add new line at end of document

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

33 changes: 33 additions & 0 deletions ROSLLM/ros_agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Agent interface

This packge allow you to communicate with the Agent

## Documentation

### To start the Flask communication node:
In ROS environment run

```
python ../scripts/ros_agent_node.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use python to run ROS nodes, we will use ROS properly, please either use rosrun or roslaunch

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

```

Please refer the document in the agent for strating the agent.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


### To stop the node:
Tpye ``exit`` in the human input
Comment on lines +16 to +17
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo and add space after section header

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed


## Examples:
To publish current time to the obsercvation:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

In ROS environment run

```
python ../example/publish_time.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use rosrun/roslaunch

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

```


To strat the action service:
In ROS environment run

```
python ../example/agentactionservice.py
```
29 changes: 29 additions & 0 deletions ROSLLM/ros_agent/example/agentactionservice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont like the naming for this file, use underscore for spaces, also example should be test to be consistent with other packages

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

import rospy
from ros_agent.srv import HandleAgentAction, HandleAgentActionResponse

def handle_action(req):
"""
Process the action request and return the response.
This function simulates action processing by returning a success status,
a message, and a reward.
"""
print("Received action request: {}".format(req.action))

# Here you would add the logic to process the action, e.g., controlling a robot or running an algorithm
response_message = "Action processed successfully"
reward = 1.0 # Example fixed reward; adjust based on actual action processing logic

return HandleAgentActionResponse(success=True, response=response_message, reward=reward)

def action_service():
rospy.init_node('agent_action_service')

# Create the service 'handle_agent_action' and specify the handler function
s = rospy.Service('handle_agent_action', HandleAgentAction, handle_action)

print("Service 'handle_agent_action' ready to handle requests.")
rospy.spin() # Keep the service open.

if __name__ == "__main__":
action_service()
21 changes: 21 additions & 0 deletions ROSLLM/ros_agent/example/publish_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
import datetime

def publish_timestamp():
rospy.init_node('timestamp_publisher', anonymous=True)
publisher = rospy.Publisher('agent_environment', String, queue_size=10)
rate = rospy.Rate(5) # Frequency of 1 Hz

while not rospy.is_shutdown():
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
rospy.loginfo(f"Publishing current timestamp: {current_time}")
publisher.publish(current_time)
rate.sleep()

if __name__ == '__main__':
try:
publish_timestamp()
except rospy.ROSInterruptException:
pass
21 changes: 21 additions & 0 deletions ROSLLM/ros_agent/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package format="2">
<name>ros_agent</name>
<version>0.0.0</version>
<description>The ros_agent package</description>

<maintainer email="[email protected]">hyu</maintainer>
<maintainer email="[email protected]">cmower</maintainer>

<license>TODO</license>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MIT


<author email="[email protected]">hyu</author>
<author email="[email protected]">cmower</author>

<buildtool_depend>catkin</buildtool_depend>
<depend>rospy</depend>
<depend>std_msgs</depend>
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

</package>
25 changes: 25 additions & 0 deletions ROSLLM/ros_agent/scripts/command_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST'])
def receive_string():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation?

if request.method == 'POST':
# Receive the JSON data
data = request.get_json()

# Extract the string from the JSON data
received_string = data.get('msg')

# Prompt the user to input another string
user_input_string = input(f"{received_string}\n")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the only point for this script to get input from the user for feedback/task-description? If so, then we will move to using chat window and this script becomes redundant

let me know if this script has any other useage? otherwise remove

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed command_window.py


# Prepare the response JSON data
response_data = {
'received_string': received_string,
'user_input_string': user_input_string
}
return jsonify(response_data)

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002, debug=True, use_reloader=False)
Loading