|
| 1 | +# Robotarium example: formation preservation and cyclic pursuit with two subteams |
| 2 | +# @author Anqi Li |
| 3 | +# @date April 8, 2019 |
| 4 | + |
| 5 | +import rps.robotarium as robotarium |
| 6 | +import rps.utilities.graph as graph |
| 7 | +import rps.utilities.transformations as transformations |
| 8 | +from rps.utilities.barrier_certificates import * |
| 9 | +from rps.utilities.misc import at_pose |
| 10 | + |
| 11 | +import time |
| 12 | + |
| 13 | +import numpy as np |
| 14 | +from numpy.linalg import norm |
| 15 | + |
| 16 | +from rmp import RMPRoot, RMPNode |
| 17 | +from rmp_leaf import CollisionAvoidanceDecentralized, GoalAttractorUni, FormationDecentralized |
| 18 | + |
| 19 | +from matplotlib import pyplot as plt |
| 20 | + |
| 21 | +# Instantiate Robotarium object |
| 22 | + |
| 23 | +# number of robots |
| 24 | +N = 5 + 3 |
| 25 | +N_cyclic_pursuit = 5 |
| 26 | +cyclic_pursuit_index = [i for i in range(N_cyclic_pursuit)] |
| 27 | +formation_control_index = [5, 6, 7] |
| 28 | +goal_2_goal_index = 5 |
| 29 | + |
| 30 | +# goal configurations |
| 31 | +theta = np.arange(N) * 2 * np.pi / N_cyclic_pursuit |
| 32 | +x_g = np.stack((np.cos(theta), np.sin(theta))).T * 0.8 |
| 33 | +x_g[-3, :] = np.array([-0.8, -0.8]) |
| 34 | +x_g[-2, :] = np.array([-0.8, -1.3]) |
| 35 | +x_g[-1, :] = np.array([-1.3, -0.8]) |
| 36 | + |
| 37 | + |
| 38 | +# initialize the robotarium |
| 39 | +rb = robotarium.Robotarium(number_of_agents=N, show_figure=True, save_data=False, update_time=0.1) |
| 40 | + |
| 41 | +# the algorithm uses single-integrator dynamics, so we'll need these mappings. |
| 42 | +si_to_uni_dyn, uni_to_si_states = transformations.create_single_integrator_to_unicycle(projection_distance=0.04) |
| 43 | + |
| 44 | + |
| 45 | +# barrier certificate to avoid collisions. used for driving to the initial configs, |
| 46 | +# not used during the algorthim |
| 47 | +si_barrier_cert = create_single_integrator_barrier_certificate(N) |
| 48 | + |
| 49 | +def at_position(states, poses, position_error=0.01): |
| 50 | + pes = norm(states[:2, :] - poses[:2, :], 2, axis=0) |
| 51 | + done = np.nonzero(pes <= position_error) |
| 52 | + return done |
| 53 | + |
| 54 | +# -------------------------------------------------------------------------------------- |
| 55 | +# build the RMPtree |
| 56 | + |
| 57 | +r = RMPRoot('root') |
| 58 | + |
| 59 | +robots = [] |
| 60 | + |
| 61 | + |
| 62 | +def create_mappings(i): |
| 63 | + phi = lambda y, i=i: np.array([[y[2 * i, 0]], [y[2 * i + 1, 0]]]) |
| 64 | + J = lambda y, i=i: np.concatenate(( |
| 65 | + np.zeros((2, 2 * i)), |
| 66 | + np.eye(2), |
| 67 | + np.zeros((2, 2 * (N - i - 1)))), axis=1) |
| 68 | + J_dot = lambda y, y_dot: np.zeros((2, 2 * N)) |
| 69 | + |
| 70 | + return phi, J, J_dot |
| 71 | + |
| 72 | + |
| 73 | +for i in range(N): |
| 74 | + phi, J, J_dot = create_mappings(i) |
| 75 | + robot = RMPNode('robot_' + str(i), r, phi, J, J_dot) |
| 76 | + robots.append(robot) |
| 77 | + |
| 78 | + |
| 79 | +theta = 0.002 |
| 80 | +Rot = np.array([[np.cos(theta), np.sin(theta)], [-np.sin(theta), np.cos(theta)]]) |
| 81 | + |
| 82 | +x_g = np.dot(x_g, Rot) |
| 83 | + |
| 84 | + |
| 85 | +weight_cp = 5 |
| 86 | +cps = [] |
| 87 | +for i in cyclic_pursuit_index: |
| 88 | + cp = GoalAttractorUni( |
| 89 | + 'cp_robot_' + str(i), |
| 90 | + robots[i], |
| 91 | + y_g=x_g[i], |
| 92 | + w_u = 10, |
| 93 | + w_l = 0.01, |
| 94 | + sigma = 0.1, |
| 95 | + alpha = 1, |
| 96 | + gain = 1, |
| 97 | + eta = 1) |
| 98 | + cps.append(cp) |
| 99 | + |
| 100 | +iacas = [] |
| 101 | +for i in range(N): |
| 102 | + for j in range(N): |
| 103 | + if i == j: |
| 104 | + continue |
| 105 | + iaca = CollisionAvoidanceDecentralized( |
| 106 | + 'ca_robot_' + str(i) + '_robot_' + str(j), |
| 107 | + robots[i], |
| 108 | + robots[j], |
| 109 | + R=0.18, |
| 110 | + eta=1) |
| 111 | + iacas.append(iaca) |
| 112 | + |
| 113 | + |
| 114 | +weight_fc = 10 |
| 115 | +dd = 0.6 |
| 116 | +fcs = [] |
| 117 | +for i in formation_control_index: |
| 118 | + for j in formation_control_index: |
| 119 | + if i == j: |
| 120 | + continue |
| 121 | + fc = FormationDecentralized( |
| 122 | + 'fc_robot_' + str(i) + '_robot_' + str(j), |
| 123 | + robots[i], |
| 124 | + robots[j], |
| 125 | + d=dd, |
| 126 | + w=weight_fc) |
| 127 | + fcs.append(fc) |
| 128 | + |
| 129 | + |
| 130 | +ga = GoalAttractorUni( |
| 131 | + 'ga_robot_' + str(goal_2_goal_index), |
| 132 | + robots[goal_2_goal_index], |
| 133 | + np.array([1.3, 1.3]), |
| 134 | + w_u = 10, |
| 135 | + w_l = 1, |
| 136 | + sigma = 0.1, |
| 137 | + alpha = 10, |
| 138 | + gain = 1, |
| 139 | + eta = 2) |
| 140 | + |
| 141 | +# ------------------------------------------------------ |
| 142 | +# drive to the initial configurations |
| 143 | + |
| 144 | +x_uni = rb.get_poses() |
| 145 | +si_velocities = np.zeros((2, N)) |
| 146 | +rb.set_velocities(np.arange(N), si_to_uni_dyn(si_velocities, x_uni)) |
| 147 | +rb.step() |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | +for k in range(2000): |
| 152 | + x_uni = rb.get_poses() |
| 153 | + x_si = uni_to_si_states(x_uni) |
| 154 | + |
| 155 | + if np.size(at_position(x_si, x_g.T)) == N: |
| 156 | + print('done!') |
| 157 | + break |
| 158 | + |
| 159 | + si_velocities = (x_g.T - x_si) |
| 160 | + si_velocities = si_barrier_cert(si_velocities, x_si) |
| 161 | + rb.set_velocities(np.arange(N), si_to_uni_dyn(si_velocities, x_uni)) |
| 162 | + rb.step() |
| 163 | + |
| 164 | + |
| 165 | +time.sleep(1) |
| 166 | + |
| 167 | +# ----------------------------------------------- |
| 168 | +# main simulation |
| 169 | + |
| 170 | +dt = rb.time_step |
| 171 | +si_velocities = np.zeros((2, N)) |
| 172 | + |
| 173 | +magnitude_limit = 0.2 |
| 174 | +for k in range(6000): |
| 175 | + |
| 176 | + # Get the poses of the robots and convert to single-integrator poses |
| 177 | + x_uni = rb.get_poses() |
| 178 | + x_si = uni_to_si_states(x_uni) |
| 179 | + |
| 180 | + x = x_si.T.flatten() |
| 181 | + x_dot = si_velocities.T.flatten() |
| 182 | + |
| 183 | + r.set_root_state(x, x_dot) |
| 184 | + r.pushforward() |
| 185 | + x_g = np.dot(x_g, Rot) |
| 186 | + [cps[i].update_goal(x_g[i]) for i in range(N_cyclic_pursuit)] |
| 187 | + [leaf.update() for leaf in iacas] |
| 188 | + [leaf.update() for leaf in fcs] |
| 189 | + r.pullback() |
| 190 | + try: |
| 191 | + a = r.resolve() |
| 192 | + si_accelerations = a.reshape(-1, 2).T |
| 193 | + # simulate double-integrator dynamics |
| 194 | + si_velocities = si_velocities + si_accelerations * dt |
| 195 | + norms = norm(si_velocities, axis=0) |
| 196 | + idxs_to_normalize = (norms > magnitude_limit) |
| 197 | + si_velocities[:, idxs_to_normalize] *= magnitude_limit * (1 / norms[idxs_to_normalize]) |
| 198 | + except: |
| 199 | + si_velocities = np.zeros((2, N)) |
| 200 | + print(x_si) |
| 201 | + print('Warning: no sol found, emergency break') |
| 202 | + break |
| 203 | + |
| 204 | + # Set the velocities of agents 1,...,N |
| 205 | + rb.set_velocities(np.arange(N), si_to_uni_dyn(si_velocities, x_uni)) |
| 206 | + |
| 207 | + # Iterate the simulation |
| 208 | + try: |
| 209 | + rb.step() |
| 210 | + except: |
| 211 | + rb.call_at_scripts_end() |
| 212 | + exit(0) |
| 213 | + |
| 214 | + |
| 215 | + |
| 216 | +# Always call this function at the end of your script!!!! |
| 217 | +rb.call_at_scripts_end() |
0 commit comments