#
# ISC License
#
# Copyright (c) 2016, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
r"""
Overview
--------
This script executes a parametric analysis of the control law examined in :ref:`scenarioDragRendezvous`,
considering the performance of that control
law across both increasing initial displacements and variations in atmospheric density.
This script is found in the folder ``src/examples`` and executed by using::
python3 scenarioDragSensitivity.py
The simulation layout is identical to that used in :ref:`scenarioDragRendezvous`. The simulator used in
that scenario is run using a grid
of true anomaly offsets and atmospheric densities using Python's multiprocessing library, and a
set of surface plots reflecting the controls' terminal performance
and trajectories are produced.
Illustration of Simulation Results
----------------------------------
In this scenario, the differential drag scenario used in :ref:`scenarioDragRendezvous` is examined
across a range of initial along-track orbit offsets and atmospheric densities.
The resulting Hill-frame trajectories corresponding to every fifth simulation run are shown in the following image.
.. image:: /_images/Scenarios/scenarioDragSensitivity_hillTrajectories.svg
:align: center
To visualize the sensitivity of terminal position and velocity errors to both increasing baseline and
variations in density, the following surface plots - which show the
scale of terminal errors as a function of atmospheric density and maneuver baseline - are shown below:
.. image:: /_images/Scenarios/scenarioDragSensitivity_positionSensitivity.svg
:align: center
.. image:: /_images/Scenarios/scenarioDragSensitivity_velocitySensitivity.svg
:align: center
"""
import multiprocessing as mp
import os
import pickle
import numpy as np
from matplotlib import pyplot as plt
from scenarioDragRendezvous import drag_simulator
fileName = os.path.basename(os.path.splitext(__file__)[0])
[docs]
def sim_wrapper(arg):
"""
Because multiprocessing.map only considers single-input functions as targets for maps,
this function maps args and kwargs from the dicts provided by multiprocessing to the inputs
demanded by drag_simulator.
"""
args, kwargs = arg
result = drag_simulator(*args, **kwargs)
return result
def drag_sensitivity_analysis(ctrlType, nuOffsetNum, densityNum, rerunSims=False):
alt_offsets= [0] # np.arange(-100,100,10)
nu_offsets = np.arange(0.001, 1, (1-0.001)/nuOffsetNum)
density_multipliers = np.logspace(-1, 0.4, num=densityNum)
pool = mp.Pool(mp.cpu_count())
X,Y,Z = np.meshgrid(alt_offsets, nu_offsets, density_multipliers)
positions = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
kwargs = {'ctrlType': ctrlType}
arg = [(position, kwargs) for position in positions]
if rerunSims:
sim_results = pool.map(sim_wrapper, arg)
with open(ctrlType+"_sweep_results.pickle", "wb") as output_file:
pickle.dump(sim_results, output_file,-1)
else:
with open(ctrlType+"_sweep_results.pickle", "rb") as fp:
sim_results = pickle.load(fp)
pool.close()
[docs]
def results_to_ranges_and_plot(results_list):
"""
Converts a results dict from scenarioDragRendezvous to a set of initial and final distance and speed errors,
and returns a plot of all of the Hill-frame trajectories.
"""
fig = plt.figure()
pos_errs = np.empty(len(results_list))
vel_errs = np.empty(len(results_list))
init_relpos = np.empty(len(results_list))
init_relvel = np.empty(len(results_list))
dens_list = np.empty(len(results_list))
density_colorvals = np.logspace(-1,0.4,num=20)
# normalizer = cmap.norma
for ind, result in enumerate(results_list):
hill_pos = result['relState.r_DC_H']
hill_vel = result['relState.v_DC_H']
init_relpos[ind] = np.linalg.norm(hill_pos[1,:])
init_relvel[ind] = np.linalg.norm(hill_vel[1,:])
pos_errs[ind] = np.linalg.norm(hill_pos[-1,:])
vel_errs[ind] = np.linalg.norm(hill_vel[-1,:])
dens_list[ind] = result['dens_mult']
if ind%5==0:
plt.plot(hill_pos[:,0],hill_pos[:,1] )
plt.grid()
plt.xlabel('Hill X (m)')
plt.ylabel('Hill Y (m)')
return init_relpos, init_relvel, pos_errs, vel_errs, dens_list, fig
def comparison_sweep(savePickle):
with open("lqr_sweep_results.pickle", "rb") as fp:
lqr_sim_results = pickle.load(fp)
if not savePickle:
os.remove("lqr_sweep_results.pickle")
figlist = {}
lqr_init_range, lqr_init_vel, lqr_err_range, lqr_err_vel, lqr_dens, lqr_fig = results_to_ranges_and_plot(lqr_sim_results)
figlist[fileName+'_hillTrajectories'] = lqr_fig
unique_ranges = np.unique(lqr_init_range.round(decimals=2))
x_shape = len(unique_ranges)
unique_dens = np.unique(lqr_dens)
y_shape = len(unique_dens)
import matplotlib.colors as colors
fig = plt.figure()
X,Y = np.meshgrid(unique_ranges, unique_dens)
Z = lqr_err_range.reshape([x_shape, y_shape])
Z_vel = lqr_err_vel.reshape([x_shape, y_shape])
# Position error contours
contour = plt.contourf(X.T,Y.T,Z, 30,norm=colors.LogNorm(Z.min(), Z.max()))
plt.ylabel('Density Multiplier')
plt.xlabel('Initial Displacement (m)')
cbar = fig.colorbar(contour)
cbar.set_label("Terminal Positioning Error (m)")
figlist[fileName+'_positionSensitivity'] = fig
# Velocity error contours
fig2 = plt.figure()
contour2 = plt.contourf(X.T,Y.T,Z_vel, 30,norm=colors.LogNorm(Z.min(), Z.max()))
plt.ylabel('Density Multiplier')
plt.xlabel('Initial Displacement (m)')
cbar2 = fig2.colorbar(contour2)
cbar2.set_label("Terminal Velocity Error (m/s)")
figlist[fileName+'_velocitySensitivity'] = fig2
return figlist
def run(show_plots, nuOffsetNum, densityNum, rerunSims=False, savePickle=False):
drag_sensitivity_analysis('lqr', nuOffsetNum, densityNum, rerunSims=rerunSims)
plots = comparison_sweep(savePickle)
if show_plots:
plt.show()
return plots
if __name__=='__main__':
nuOffsetNum = 15
densityNum = 20
run(True, nuOffsetNum, densityNum, rerunSims=True, savePickle=False)