YvesP commited on
Commit
3c74b8c
1 Parent(s): ec6896d

Add application file

Browse files
Files changed (5) hide show
  1. TwoDimEnv.py +94 -0
  2. app.py +181 -0
  3. longModel.zip +3 -0
  4. params.py +33 -0
  5. requirements.txt +60 -0
TwoDimEnv.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gymnasium as gym
2
+ from params import *
3
+
4
+
5
+ class TwoDimEnv(gym.Env):
6
+ """
7
+ Custom 2D-Environment that follows gym interface.
8
+ This is a simple 2D-env where the agent must go to the 0,0 point,
9
+ it can go right or straight in a continuous action space, but cannot change speed
10
+ """
11
+ def __init__(self):
12
+ super(TwoDimEnv, self).__init__()
13
+
14
+ # agent cannot be further away from target than space_limits
15
+ self.space_limits = SPACE_LIMITS
16
+
17
+ # Initialize the agent position
18
+ rho_init = np.random.random() * SPACE_LIMITS
19
+ theta_init = np.random.random() * 2 * PI - PI
20
+ self.rho_init, self.theta_init, self.z_init = rho_init, theta_init, Z_INIT
21
+ self.agent_pos = rho_init * np.exp(1j * theta_init)
22
+ self.agent_z = Z_INIT
23
+
24
+ # agent abs speed is constant, may change of direction on the plane, but doesnt change in the z-axis
25
+ self.agent_speed = SPEED_RHO * np.exp(1j*SPEED_ANGLE)
26
+ self.agent_speed_z = SPEED_Z
27
+ self.agent_max_angle = MAX_ANGLE
28
+
29
+ #
30
+ self.agent_previous_pos = self.agent_pos
31
+
32
+
33
+ def reset(self, training=True, rho_init=RHO_INIT, theta_init=THETA_INIT, z_init=Z_INIT):
34
+ """
35
+ :input: TwoDimEnv
36
+ :return: TwoDimEnv
37
+ """
38
+ # Initialize the agent position and speed
39
+ self.agent_pos = rho_init * np.exp(1j * theta_init)
40
+ self.agent_speed = SPEED_RHO * np.exp(1j * SPEED_ANGLE)
41
+ self.agent_z = z_init
42
+
43
+ # init step index
44
+ self.step_index = 0
45
+
46
+ return self.get_obs()
47
+
48
+ def step(self, action):
49
+ '''
50
+ the Gym step
51
+ :param action:
52
+ :return:
53
+ '''
54
+
55
+ self.step_index += 1
56
+
57
+ # Agent changes of direction according to its action: action[0] in [-1; 1]
58
+ self.agent_speed *= np.exp(1j * self.agent_max_angle * action[0], dtype=complex)
59
+ self.agent_previous_pos = self.agent_pos
60
+ self.agent_pos += self.agent_speed
61
+ self.agent_z -= self.agent_speed_z
62
+
63
+ # Are we done?
64
+ done = bool(self.agent_z <= 0)
65
+
66
+ # get normalized obs
67
+ obs = self.get_obs()
68
+
69
+ return obs, 0., done, {}
70
+
71
+ def render(self, **kwargs):
72
+ pass
73
+
74
+ def close(self):
75
+ pass
76
+
77
+ # calculates normalized obs
78
+ def get_obs(self):
79
+ '''
80
+ normalises the observation
81
+ :return: normalised observation
82
+ '''
83
+ agent_dist = np.abs(self.agent_pos) / self.space_limits
84
+ agent_angle = np.angle(self.agent_pos) / (2 * PI)
85
+ if agent_angle < 0:
86
+ agent_angle += 1
87
+ agent_speed = np.angle(self.agent_speed) / (2 * PI)
88
+ if agent_speed < 0:
89
+ agent_speed += 1
90
+ agent_z = (Z_INIT-self.agent_z) / Z_INIT
91
+ obs = np.array([agent_dist, agent_angle, agent_speed, agent_z]).astype(np.float32)
92
+ return obs
93
+
94
+
app.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import streamlit as st
3
+ from params import *
4
+ from stable_baselines3 import SAC
5
+ import pandas as pd
6
+ import pydeck as pdk
7
+ from TwoDimEnv import TwoDimEnv
8
+
9
+ import gymnasium as gym
10
+
11
+ def run_episode(env, model, lat_tg, lon_tg, rho_init=RHO_INIT, theta_init=THETA_INIT, zed=Z_INIT):
12
+ '''
13
+ runs the episode given an observation init ... and an env and a model
14
+ :param env: TwoDimEnv
15
+ :param model:
16
+ :param lat_tg: lattitude de la target (0,0)
17
+ :param lon_tg: longitude de la target (0,0)
18
+ :param rho_init:
19
+ :param theta_init:
20
+ :param zed:
21
+ :return:
22
+ '''
23
+ obs = env.reset(training=False, rho_init=rho_init, theta_init=theta_init, z_init=zed)
24
+ step = 0
25
+ lat, lon = rhotheta_to_latlon(MOVE_TO_METERS * rho_init, theta_init, lat_tg, lon_tg)
26
+ path = [[lon, lat, MOVE_TO_METERS * zed]]
27
+ traj = [lat, lon, MOVE_TO_METERS * zed]
28
+ while step < zed:
29
+ step += 1
30
+ action, _ = model.predict(obs, deterministic=True)
31
+ obs, _, _, _ = env.step(action)
32
+ rho = obs[0] * env.space_limits
33
+ theta = obs[1] * 2 * PI
34
+ lat, lon = rhotheta_to_latlon(MOVE_TO_METERS*rho, theta, lat_tg, lon_tg)
35
+ traj = np.vstack((traj, [lat, lon, MOVE_TO_METERS*(zed - step)]))
36
+ path.append([lon, lat, MOVE_TO_METERS*(zed - step)])
37
+ df_col = pd.DataFrame(traj, columns=['lat', 'lon', 'zed'])
38
+ df_path = pd.DataFrame([{
39
+ 'color': [0, 0, 200, 120],
40
+ 'path': path
41
+ }])
42
+ return df_path, df_col
43
+
44
+ def rhotheta_to_latlon(rho, theta, lat_tg, lon_tg):
45
+ '''
46
+ transforms polar coordinates into lat, lon
47
+ :param rho:
48
+ :param theta:
49
+ :param lat_tg: latitude de la target (0,0)
50
+ :param lon_tg: longitude de la target (0,0)
51
+ :return:
52
+ '''
53
+ z = rho * np.exp(1j * theta)
54
+ lat = np.imag(z)*360/(40075*1000) + lat_tg
55
+ lon = np.real(z)*360/(40075*1000*np.cos(PI/180*lat)) + lon_tg
56
+ return lat, lon
57
+
58
+
59
+ def get_layers(df, df_past, df_target, df_path, df_col):
60
+ '''
61
+ renders the layers to be displayed with df in different formats as entries
62
+ :param df:
63
+ :param df_past:
64
+ :param df_target:
65
+ :param df_path:
66
+ :param df_col:
67
+ :return:
68
+ '''
69
+ return [
70
+ pdk.Layer(
71
+ 'ScatterplotLayer',
72
+ data=df,
73
+ get_position='[lon, lat]',
74
+ get_color='[200, 30, 0, 160, 40]',
75
+ get_radius=10,
76
+ ),
77
+ pdk.Layer(
78
+ 'ScatterplotLayer',
79
+ data=df_target,
80
+ get_position='[lon, lat]',
81
+ get_color='[200, 30, 0]',
82
+ get_radius=65,
83
+ ),
84
+ pdk.Layer(
85
+ 'ScatterplotLayer',
86
+ data=df_target,
87
+ get_position='[lon, lat]',
88
+ get_color='[255, 255, 255]',
89
+ get_radius=45,
90
+ ),
91
+ pdk.Layer(
92
+ 'ScatterplotLayer',
93
+ data=df_target,
94
+ get_position='[lon, lat]',
95
+ get_color='[0, 0, 200]',
96
+ get_radius=25,
97
+ ),
98
+ pdk.Layer(
99
+ 'ScatterplotLayer',
100
+ data=df_past,
101
+ get_position='[lon, lat]',
102
+ get_color='[200, 30, 0, 40]',
103
+ get_radius=10,
104
+ ),
105
+ pdk.Layer(
106
+ type="PathLayer",
107
+ data=df_path,
108
+ pickable=True,
109
+ get_color="color",
110
+ width_scale=20,
111
+ width_min_pixels=1,
112
+ get_path="path",
113
+ get_width=1,
114
+ ),
115
+ pdk.Layer(
116
+ type="ColumnLayer",
117
+ data=df_col,
118
+ get_position=['lon', 'lat'],
119
+ get_elevation="zed",
120
+ elevation_scale=1,
121
+ radius=10,
122
+ get_fill_color=[160, 20, 0, 10],
123
+ pickable=True,
124
+ auto_highlight=True
125
+ ),
126
+ ]
127
+
128
+
129
+ def show():
130
+ '''
131
+ shows the i-PADS in Streamlit
132
+ :return:
133
+ '''
134
+ env = TwoDimEnv()
135
+ model = SAC.load("longModel")
136
+ st.title('Intelligent PADS by hexamind')
137
+ st.write('This is a quick demo of an autonomous Parachute (Precision Air Delivery System) controlled by Reinforcement learning. ')
138
+ st.text('<- Set the starting point')
139
+
140
+ st.sidebar.write("Where do you want the parachute to start from?")
141
+ rho = st.sidebar.slider('What distance? (in m)', 0, 3000, 1500) / MOVE_TO_METERS
142
+ theta = 2*PI/360 * st.sidebar.slider('What angle?', 0, 360, 90)
143
+ zed = int(st.sidebar.slider('What elevation? (in m)', 0, 1200, 600) / MOVE_TO_METERS)
144
+
145
+ location = st.sidebar.radio("Location", ['San Francisco', 'Paris', 'Puilaurens'])
146
+ lat_tg = LOC[location]['lat']
147
+ lon_tg = LOC[location]['lon']
148
+ df_path, df_col = run_episode(env, model, lat_tg, lon_tg, rho_init=rho, theta_init=theta, zed=zed)
149
+ st.sidebar.write(
150
+ 'If you like to play, you will probably find some starting points where the parachute is out of control :) '
151
+ 'No worries, we have plenty more efficient models at www.hexamind.com ')
152
+
153
+ df_target = pd.DataFrame({'lat': [lat_tg], 'lon': [lon_tg]})
154
+ deck_map = st.empty()
155
+ pitch = st.slider('pitch', 0, 100, 50)
156
+ initial_view_state = pdk.ViewState(
157
+ latitude=lat_tg,
158
+ longitude=lon_tg,
159
+ zoom=12,
160
+ pitch=pitch
161
+ )
162
+ deck_map.pydeck_chart(pdk.Deck(
163
+ map_style='mapbox://styles/mapbox/light-v9',
164
+ initial_view_state=initial_view_state
165
+ ))
166
+ df_pathi = df_path.copy()
167
+ for i in range(zed):
168
+ df_pathi['path'][0] = df_path['path'][0][0:i+1]
169
+ layers = get_layers(df_col[i:i+1], df_col[0:i], df_target, df_pathi, df_col[0:i+1])
170
+ deck_map.pydeck_chart(pdk.Deck(
171
+ map_style='mapbox://styles/mapbox/light-v9',
172
+ initial_view_state=initial_view_state,
173
+ layers=layers
174
+ ))
175
+ time.sleep(TIMESLEEP)
176
+
177
+
178
+ show()
179
+
180
+ # to uncomment for debug
181
+ #show_print()
longModel.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:70dc33275fa1df891ec4b106e71b63c2b8ad3f9ebd7728352d7ddfff1e0e895d
3
+ size 3018346
params.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ PI = np.float32(np.pi)
4
+
5
+ EPISODE_LENGTH = 100 # number of steps per episode
6
+
7
+ SPACE_LIMITS = 250 # playground limits for the PADS
8
+ LANDING_LIMITS = 10 # error tolerated for landing
9
+ LANDING_TARGET = 50 # error tolerated for the target when landing
10
+
11
+ RHO_INIT = 10 # default para's 2D position
12
+ THETA_INIT = PI / 2
13
+ Z_INIT = 100 #
14
+
15
+ SPEED_RHO = 5 # units forward per step
16
+ SPEED_ANGLE = 0
17
+ SPEED_Z = 1 # units down per step
18
+
19
+ MOVE_TO_METERS = 8 # how many meters (down or horizontally) per unit in a step
20
+
21
+ MAX_ANGLE = PI / 3
22
+
23
+ LOC = {'Paris':
24
+ {'lat': 48.865879, 'lon': 2.319827},
25
+ 'Fonsorbes':
26
+ {'lat': 43.54, 'lon': 1.25},
27
+ 'San Francisco':
28
+ {'lat': 37.7737283, 'lon': -122.4342383},
29
+ 'Puilaurens':
30
+ {'lat': 42.8119596, 'lon': 2.2590985},
31
+ }
32
+
33
+ TIMESLEEP = 0.2
requirements.txt ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ altair==4.2.2
2
+ attrs==23.1.0
3
+ blinker==1.6.2
4
+ cachetools==5.3.1
5
+ certifi==2023.5.7
6
+ charset-normalizer==3.1.0
7
+ click==8.1.3
8
+ cloudpickle==2.2.1
9
+ contourpy==1.0.6
10
+ cycler==0.11.0
11
+ decorator==5.1.1
12
+ entrypoints==0.4
13
+ fonttools==4.38.0
14
+ gitdb==4.0.10
15
+ GitPython==3.1.31
16
+ gym==0.21.0
17
+ gymnasium==0.28.1
18
+ gym-notices==0.0.8
19
+ idna==3.4
20
+ importlib-metadata==4.13.0
21
+ Jinja2==3.1.2
22
+ jsonschema==4.17.3
23
+ kiwisolver==1.4.4
24
+ markdown-it-py==2.1.0
25
+ MarkupSafe==2.1.2
26
+ matplotlib==3.5.3
27
+ mdurl==0.1.2
28
+ numpy==1.21.6
29
+ packaging==23.0
30
+ pandas==1.3.5
31
+ Pillow==9.5.0
32
+ protobuf==3.20.3
33
+ pyarrow==12.0.0
34
+ pydeck==0.8.1b0
35
+ Pygments==2.15.1
36
+ Pympler==1.0.1
37
+ pyparsing==3.0.9
38
+ pyrsistent==0.19.3
39
+ python-dateutil==2.8.2
40
+ pytz==2022.7.1
41
+ pytz-deprecation-shim==0.1.0.post0
42
+ requests==2.28.2
43
+ rich==13.2.0
44
+ semver==2.13.0
45
+ six==1.16.0
46
+ smmap==5.0.0
47
+ setuptools==66.0.0
48
+ stable-baselines3==1.7.0
49
+ streamlit==1.22.0
50
+ tenacity==8.2.2
51
+ toml==0.10.2
52
+ toolz==0.12.0
53
+ torch==1.13.1
54
+ tornado==6.2
55
+ typing_extensions==4.4.0
56
+ tzdata==2022.7
57
+ tzlocal==4.2
58
+ urllib3==1.26.16
59
+ validators==0.20.0
60
+ zipp==3.11.0