From ac30f99379c0b6ae145ccb18df05c46d058d8698 Mon Sep 17 00:00:00 2001
From: rachelmoan <moanrachel516@gmail.com>
Date: Mon, 2 Sep 2024 12:24:15 -0500
Subject: [PATCH] Refactoring the guided mrmp policy to act as one big path
 tracker for all the robots

---
 .../planners/multirobot/db_guided_mrmp.py     | 163 +++++++++++++++---
 guided_mrmp/simulator.py                      |   1 +
 2 files changed, 139 insertions(+), 25 deletions(-)

diff --git a/guided_mrmp/planners/multirobot/db_guided_mrmp.py b/guided_mrmp/planners/multirobot/db_guided_mrmp.py
index 5447325..50cba25 100644
--- a/guided_mrmp/planners/multirobot/db_guided_mrmp.py
+++ b/guided_mrmp/planners/multirobot/db_guided_mrmp.py
@@ -1,23 +1,26 @@
 """
 Database-guided multi-robot motion planning
+
+This module is essentially one big path tracking algorithm. 
+It uses MPC to path track each of the robots, while looking ahead to 
+identify and resolve conflicts.
 """
-import random
+
 import pygame
-import os
 from shapely.geometry import Polygon, Point
 
-
-
 from guided_mrmp.utils import Env
 from guided_mrmp.utils.helpers import *
 
 from guided_mrmp.conflict_resolvers import TrajOptResolver,TrajOptDBResolver
 
-from guided_mrmp.utils import Roomba
+from guided_mrmp.controllers.utils import get_ref_trajectory, compute_path_from_wp
+
+from guided_mrmp.controllers.mpc import MPC
 
 
-T = 1  # Prediction Horizon [s]
-DT = 0.2  # discretization step [s]
+T = 5  # Prediction Horizon [s]
+DT = .5  # discretization step [s]
 
 class GuidedMRMP:
     def __init__(self, env, robots, dynamics_models):
@@ -29,28 +32,73 @@ class GuidedMRMP:
         self.robots = robots
         self.dynamics_models = dynamics_models
         self.env = env
-        self.current_guides = []*len(robots)
-        self.current_trajs = []*len(robots)
+        self.guide_paths = [[]]*len(robots)
+
+        self.K = int(T / DT)
+
+    
+        for idx,r in enumerate(self.robots):
+            xs = []
+            ys = []
+            for node in r.rrtpath:
+                xs.append(node[0])
+                ys.append(node[1])
+
+            waypoints = [xs,ys]
+
+            self.guide_paths[idx] = compute_path_from_wp(waypoints[0], waypoints[1], .05)
+
+    def ego_to_global(self, robot, mpc_out):
+        """
+        transforms optimized trajectory XY points from ego (robot) reference
+        into global (map) frame
+
+        Args:
+            mpc_out ():
+        """
+        # Extract x, y, and theta from the state
+        x = robot.current_position[0]
+        y = robot.current_position[1]
+        theta = robot.current_position[2]
+
+        # Rotation matrix to transform points from ego frame to global frame
+        Rotm = np.array([
+            [np.cos(theta), -np.sin(theta)],
+            [np.sin(theta), np.cos(theta)]
+        ])
+
+        # Initialize the trajectory array (only considering XY points)
+        trajectory = mpc_out[0:2, :]
 
-    def find_all_conflicts(self, dt):
+        # Apply rotation to the trajectory points
+        trajectory = Rotm.dot(trajectory)
+
+        # Translate the points to the robot's position in the global frame
+        trajectory[0, :] += x
+        trajectory[1, :] += y
+
+        return trajectory
+    
+    def find_all_conflicts(self, desired_controls, dt):
         """
         Loop over all the robots, checking for both node conflicts and edge
         conflicts. 
         """
         conflicts = []
 
-        for idx, r1 in enumerate(self.robots):
-            control = r1.next_control
+        for r1_idx, r1 in enumerate(self.robots):
+            control = desired_controls[r1_idx]
             print(f"next control = {control}")
-            next_state = self.dynamics_models[idx].next_state(r1.current_position, control, dt)
+            next_state = self.dynamics_models[r1_idx].next_state(r1.current_position, control, dt)
+
             circ1 = Point(next_state[0],next_state[1])
             circ1 = circ1.buffer(r1.radius)
-            for r2 in self.robots:
+            for r2_idx, r2 in enumerate(self.robots):
                 if r1.label == r2.label:
                     continue
 
-                control = r2.next_control
-                next_state = self.dynamics_models[idx].next_state(r2.current_position, control, dt)
+                control = desired_controls[r2_idx]
+                next_state = self.dynamics_models[r2_idx].next_state(r2.current_position, control, dt)
                 circ2 = Point(next_state[0],next_state[1])
                 circ2 = circ2.buffer(r2.radius)
                 
@@ -68,6 +116,56 @@ class GuidedMRMP:
                 return False
         return True
 
+    def get_next_controls(self,screen):
+        """
+        Get the next control for each robot.
+        """
+
+        next_controls = []
+        for idx, r in enumerate(self.robots):
+
+            print(f"index = {idx}")
+
+            state = r.current_position
+            path = self.guide_paths[idx]
+
+            print(f"state = {state}")
+            print(f"path = {path}")
+
+            # Get Reference_traj -> inputs are in worldframe
+            target_traj = get_ref_trajectory(np.array(state), 
+                                             np.array(path), 
+                                             3.0, 
+                                             T, 
+                                             DT)
+            
+            
+            
+            # For a circular robot (easy dynamics)
+            Q = [20, 20, 20]  # state error cost
+            Qf = [30, 30, 30]  # state final error cost
+            R = [10, 10]  # input cost
+            P = [10, 10]  # input rate of change cost
+            mpc = MPC(self.dynamics_models[idx], T, DT, Q, Qf, R, P)
+
+            # dynamycs w.r.t robot frame
+            curr_state = np.array([0, 0, 0])
+            x_mpc, u_mpc = mpc.step(
+                curr_state,
+                target_traj,
+                r.control
+            )
+
+            print(f"optimized traj = {x_mpc}")
+            self.add_vis_target_traj(screen, r, x_mpc)
+            
+            # only the first one is used to advance the simulation
+            control = [u_mpc.value[0, 0], u_mpc.value[1, 0]]
+            
+            next_controls.append(np.asarray(control))
+        return next_controls
+
+
     def advance(self, screen, state, time, dt=0.1):
         """
         Advance the simulation by one timestep.
@@ -79,11 +177,10 @@ class GuidedMRMP:
         """
 
         # get the next control for each robot
-        for r in self.robots:
-            r.next_control = r.tracker.get_next_control(r.current_position)[1]
+        next_desired_controls = self.get_next_controls(screen)
 
         # find all the conflicts at the next timestep
-        conflicts = self.find_all_conflicts(dt)
+        # conflicts = self.find_all_conflicts(next_desired_controls, dt)
 
         # resolve the conflicts using the database
         # resolver = TrajOptDBResolver(10, 60, conflicts, self.robots)
@@ -106,7 +203,9 @@ class GuidedMRMP:
         #     r.h_history.append(r.state[2])
 
         # return the valid controls
-        controls = [r.next_control for r in self.robots]
+        for r, next_control in zip(self.robots, next_desired_controls):
+            r.control = next_control
+        controls = next_desired_controls
         return controls
 
     def add_vis_guide_paths(self, screen):
@@ -115,11 +214,13 @@ class GuidedMRMP:
         """
         
         for r in self.robots:
-            path = r.rrtpath
-            for node in path:
-                pygame.draw.circle(screen, r.color, (int(node[0]), int(node[1])), 2)
-            for i in range(len(path)-1):
-                pygame.draw.line(screen, r.color, r.rrtpath[i], r.rrtpath[i+1], 2)
+            path = self.guide_paths[r.label]
+            xs = path[0]
+            ys = path[1]
+            # for node in path:
+            #     pygame.draw.circle(screen, r.color, (int(node[0]), int(node[1])), 2)
+            for i in range(len(xs)-1):
+                pygame.draw.line(screen, r.color, (xs[i],ys[i]), (xs[i+1],ys[i+1]), 2)
 
     def add_vis_grid(self, screen, grid_size, top_left, cell_size):
         """
@@ -137,6 +238,18 @@ class GuidedMRMP:
             
             pygame.draw.line(screen, (0,0,0), (xs[0],ys[0]), (xs[1],ys[1]), 2)
 
+    def add_vis_target_traj(self,screen, robot, traj):
+        """
+        Add the visualization to the screen.
+        """
+        traj = self.ego_to_global(robot, traj.value)
+        for i in range(len(traj[0])-1):
+            x = int(traj[0,i])
+            y = int(traj[1,i])
+            next_x = int(traj[0,i+1])
+            next_y = int(traj[1,i+1])
+            pygame.draw.line(screen, (255,0,0), (x,y), (next_x,next_y), 2)
+
 if __name__ == "__main__":
 
     # create the environment
diff --git a/guided_mrmp/simulator.py b/guided_mrmp/simulator.py
index 7e40367..3f4d1ea 100644
--- a/guided_mrmp/simulator.py
+++ b/guided_mrmp/simulator.py
@@ -36,6 +36,7 @@ class Simulator:
         Advance the simulation by dt seconds
         """
         controls = self.policy.advance(screen,self.state, self.time, dt)
+        print(controls)
         for i in range(self.num_robots):
             new_state = self.dynamics_models[i].next_state(self.state[i], controls[i], dt)
             self.robots[i].current_position = new_state
-- 
GitLab