303 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			303 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | from openai import OpenAI | ||
|  | import time | ||
|  | import json | ||
|  | import os | ||
|  | from io import BytesIO | ||
|  | import base64 | ||
|  | 
 | ||
|  | from v2_rl_walk_mujoco import RLWalk | ||
|  | from threading import Thread | ||
|  | import cv2 | ||
|  | from mini_bdx_runtime.camera import Cam | ||
|  | 
 | ||
|  | # TODO mission : find an object ? | ||
|  | 
 | ||
|  | 
 | ||
|  | # Your Tools class | ||
|  | class Tools: | ||
|  |     def __init__(self): | ||
|  |         self.cam = Cam() | ||
|  |         self.rl_walk = RLWalk( | ||
|  |             "/home/bdxv2/BEST_WALK_ONNX_2.onnx", | ||
|  |             cutoff_frequency=40, | ||
|  |         ) | ||
|  | 
 | ||
|  |         Thread(target=self.rl_walk.run, daemon=True).start() | ||
|  | 
 | ||
|  |     # def upload_image(self, image_path: str): | ||
|  |     #     image_name = os.path.basename(image_path) | ||
|  |     #     im = cv2.imread(image_path) | ||
|  |     #     im = cv2.resize(im, (512, 512)) | ||
|  |     #     cv2.imwrite(image_path, im) | ||
|  |     #     # command = ( | ||
|  |     #     #     f"scp {image_path} apirrone@s-nguyen.net:/home/apirrone/webserv/images/" | ||
|  |     #     # ) | ||
|  |     #     command = ( | ||
|  |     #         f"scp {image_path} apirrone@192.168.10.103:/home/apirrone/webserv/images/" | ||
|  |     #     ) | ||
|  |     #     print(command) | ||
|  |     #     url = f"http://s-nguyen.net:4444/images/{image_name}" | ||
|  |     #     os.system(command) | ||
|  |     #     return url | ||
|  | 
 | ||
|  | 
 | ||
|  |     def move_forward(self, seconds=2): | ||
|  |         seconds = max(2, min(seconds, 5)) | ||
|  |         print(f"Moving forward for {seconds} seconds") | ||
|  |         self.rl_walk.last_commands[0] = 0.15 | ||
|  |         time.sleep(seconds) | ||
|  |         self.rl_walk.last_commands[0] = 0.0 | ||
|  |         print("Stopped moving forward") | ||
|  |         return f"Moved forward for {seconds} seconds successfully" | ||
|  | 
 | ||
|  |     def turn_left(self, seconds=2): | ||
|  |         seconds = max(2, min(seconds, 5)) | ||
|  |         print(f"Turning left for {seconds} seconds") | ||
|  |         self.rl_walk.last_commands[2] = 1.0 | ||
|  |         time.sleep(seconds) | ||
|  |         self.rl_walk.last_commands[2] = 0.0 | ||
|  |         print("Stopped turning left") | ||
|  |         return f"Turned left for {seconds} seconds successfully" | ||
|  | 
 | ||
|  |     def turn_right(self, seconds=2): | ||
|  |         seconds = max(2, min(seconds, 5)) | ||
|  |         print(f"Turning right for {seconds} seconds") | ||
|  |         self.rl_walk.last_commands[2] = -1.0 | ||
|  |         time.sleep(seconds) | ||
|  |         self.rl_walk.last_commands[2] = 0.0 | ||
|  |         print("Stopped turning right") | ||
|  |         return f"Turned right for {seconds} seconds successfully" | ||
|  | 
 | ||
|  |     def move_backward(self, seconds=2): | ||
|  |         seconds = max(2, min(seconds, 5)) | ||
|  |         print(f"Moving backward for {seconds} seconds") | ||
|  |         self.rl_walk.last_commands[0] = -0.15 | ||
|  |         time.sleep(seconds) | ||
|  |         self.rl_walk.last_commands[0] = 0.0 | ||
|  |         print("Stopped moving backward") | ||
|  |         return f"Moved backward for {seconds} seconds successfully" | ||
|  | 
 | ||
|  |     def take_picture(self): | ||
|  |         # https://projects.raspberrypi.org/en/projects/getting-started-with-picamera/5 | ||
|  |         print("Taking a picture...") | ||
|  |         image64 = self.cam.get_encoded_image() | ||
|  |         url = ("data:image/jpeg;base64," + image64,) | ||
|  |         time.sleep(1) | ||
|  |         print("Picture taken") | ||
|  |         return url | ||
|  | 
 | ||
|  |     def play_happy_sound(self): | ||
|  |         self.rl_walk.sounds.play_happy() | ||
|  |         return "Played happy sound" | ||
|  | 
 | ||
|  | # Tool instance | ||
|  | tools_instance = Tools() | ||
|  | 
 | ||
|  | openai_tools = [ | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "move_forward", | ||
|  |         "description": "Move forward for a number of seconds", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": { | ||
|  |                 "seconds": { | ||
|  |                     "type": "integer", | ||
|  |                     "description": "Number of seconds to move forward (min 2, max 5)", | ||
|  |                 } | ||
|  |             }, | ||
|  |             "required": ["seconds"], | ||
|  |             "additionalProperties": False, | ||
|  |         }, | ||
|  |     }, | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "move_backward", | ||
|  |         "description": "Move backward for a number of seconds", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": { | ||
|  |                 "seconds": { | ||
|  |                     "type": "integer", | ||
|  |                     "description": "Number of seconds to move backward (min 2, max 5)", | ||
|  |                 } | ||
|  |             }, | ||
|  |             "required": ["seconds"], | ||
|  |             "additionalProperties": False, | ||
|  |         }, | ||
|  |     }, | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "turn_left", | ||
|  |         "description": "Turn left on the spot", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": { | ||
|  |                 "seconds": { | ||
|  |                     "type": "integer", | ||
|  |                     "description": "Number of seconds to turn left (min 2, max 5)", | ||
|  |                 } | ||
|  |             }, | ||
|  |             "required": ["seconds"], | ||
|  |             "additionalProperties": False, | ||
|  |         }, | ||
|  |     }, | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "turn_right", | ||
|  |         "description": "Turn right on the spot", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": { | ||
|  |                 "seconds": { | ||
|  |                     "type": "integer", | ||
|  |                     "description": "Number of seconds to turn right (min 2, max 5)", | ||
|  |                 } | ||
|  |             }, | ||
|  |             "required": ["seconds"], | ||
|  |             "additionalProperties": False, | ||
|  |         }, | ||
|  |     }, | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "take_picture", | ||
|  |         "description": "Take a picture", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": {}, | ||
|  |             # No required properties for taking a picture | ||
|  |         }, | ||
|  |     }, | ||
|  |     { | ||
|  |         "type": "function", | ||
|  |         "name": "play_happy_sound", | ||
|  |         "description": "Play a happy sound", | ||
|  |         "parameters": { | ||
|  |             "type": "object", | ||
|  |             "properties": {}, | ||
|  |             # No required properties for playing a sound | ||
|  |         }, | ||
|  |     } | ||
|  | ] | ||
|  | 
 | ||
|  | # Mapping function names to actual methods | ||
|  | function_map = { | ||
|  |     "move_forward": tools_instance.move_forward, | ||
|  |     "move_backward": tools_instance.move_backward, | ||
|  |     "turn_left": tools_instance.turn_left, | ||
|  |     "turn_right": tools_instance.turn_right, | ||
|  |     "take_picture": tools_instance.take_picture, | ||
|  | } | ||
|  | 
 | ||
|  | messages = [ | ||
|  |     { | ||
|  |         "role": "system", | ||
|  |         "content": ( | ||
|  |             "You are a cute little biped robot that can move around using the following tools: " | ||
|  |             "`move_forward`, `move_backward`, `turn_left`, `turn_right`, 'play_happy_sound' and 'take_picture'. " | ||
|  |             "moving forward for 1 second will make you move forward by about 15 centimeters" | ||
|  |             "turning for 1 second will make you turn about 45 degrees" | ||
|  |             "You can only perform one action at a time. If multiple actions are needed, call them step by step." | ||
|  |             "Explain what you are doing along the way" | ||
|  |             "Always start by taking a picture of the environment so you can see where you are. " | ||
|  |             "When you take a picture, describe what you see in the image. " | ||
|  |             "make sure not to hit any walls or objects. Take pictures regularly to know where you are." | ||
|  |             "Maybe it's a good idea to turn 360 degrees to check all directions. (no need if you already found it)" | ||
|  |             "When given a goal to find something, if you found it, navigate to be in front of it, facing it. You want to be about 1 meter close to it" | ||
|  |             "When you are 1 meter close to the object, play the happy sound" | ||
|  |             "" | ||
|  |         ), | ||
|  |     }, | ||
|  |     # { | ||
|  |     #     "role": "user", | ||
|  |     #     "content": "Find the yellow vaccum cleaner !", | ||
|  |     # }, | ||
|  |     { | ||
|  |         "role": "user", | ||
|  |         "content": "Find the waste bin and turn around it. Play the happy sound when you are done", | ||
|  |     }, | ||
|  | ] | ||
|  | 
 | ||
|  | 
 | ||
|  | # Mapping function names to actual methods | ||
|  | function_map = { | ||
|  |     "move_forward": tools_instance.move_forward, | ||
|  |     "move_backward": tools_instance.move_backward, | ||
|  |     "turn_left": tools_instance.turn_left, | ||
|  |     "turn_right": tools_instance.turn_right, | ||
|  |     "take_picture": tools_instance.take_picture, | ||
|  |     "play_happy_sound": tools_instance.play_happy_sound, | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | client = OpenAI() | ||
|  | 
 | ||
|  | 
 | ||
|  | def call_function(name, args): | ||
|  |     if name == "move_forward": | ||
|  |         return function_map[name](args["seconds"]) | ||
|  |     elif name == "move_backward": | ||
|  |         return function_map[name](args["seconds"]) | ||
|  |     elif name == "turn_left": | ||
|  |         return function_map[name](args["seconds"]) | ||
|  |     elif name == "turn_right": | ||
|  |         return function_map[name](args["seconds"]) | ||
|  |     elif name == "take_picture": | ||
|  |         return function_map[name]() | ||
|  |     elif name == "play_happy_sound": | ||
|  |         return function_map[name]() | ||
|  |     else: | ||
|  |         raise ValueError(f"Unknown function name: {name}") | ||
|  | 
 | ||
|  | 
 | ||
|  | while True: | ||
|  |     response = client.responses.create( | ||
|  |         model="gpt-4o-mini", | ||
|  |         input=messages, | ||
|  |         tools=openai_tools, | ||
|  |     ) | ||
|  | 
 | ||
|  |     if len(response.output) == 1 and response.output[0].type == "function_call": | ||
|  |         print("Only function call, no text response") | ||
|  |     else: | ||
|  |         try: | ||
|  |             print(response.output[0].content[0].text) | ||
|  |         except: | ||
|  |             print("Error occurred while processing response") | ||
|  |     for tool_call in response.output: | ||
|  |         if tool_call.type != "function_call": | ||
|  |             continue | ||
|  | 
 | ||
|  |         name = tool_call.name | ||
|  |         args = json.loads(tool_call.arguments) | ||
|  | 
 | ||
|  |         result = call_function(name, args)[0] | ||
|  |         messages.append(tool_call) | ||
|  |         if tool_call.name == "take_picture": | ||
|  |             # result is an image URL | ||
|  | 
 | ||
|  |             # Add an optional prompt or let GPT interpret the image | ||
|  |             messages.append( | ||
|  |                 { | ||
|  |                     "role": "user", | ||
|  |                     "content": [{"type": "input_image", "image_url": result}], | ||
|  |                 } | ||
|  |             ) | ||
|  | 
 | ||
|  |             messages.append( | ||
|  |                 { | ||
|  |                     "type": "function_call_output", | ||
|  |                     "call_id": tool_call.call_id, | ||
|  |                     "output": "Image taken and provided above.", | ||
|  |                 } | ||
|  |             ) | ||
|  |         else: | ||
|  | 
 | ||
|  |             messages.append( | ||
|  |                 { | ||
|  |                     "type": "function_call_output", | ||
|  |                     "call_id": tool_call.call_id, | ||
|  |                     "output": str(result), | ||
|  |                 } | ||
|  |             ) |