Source code for scenarioFormationMeanOEFeedback

#
#  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 sets up a formation flying scenario with two spacecraft. The deputy spacecraft keeps a given
mean orbital element difference based on Lyapunov control theory.

This script is found in the folder ``basilisk/examples`` and executed by using::

      python3 scenarioFormationMeanOEFeedback.py

The simulation layout is shown in the following illustration. Two spacecraft are orbiting the earth at
close distance. Only :math:`J_2` gravity perturbation is included. Each spacecraft sends a :ref:`simpleNav`
output message of type :ref:`NavAttMsgPayload` message at a certain period
to :ref:`meanOEFeedback`, where mean orbital element difference is calculated and necessary control force is output to
extForceTorque module.

.. image:: /_images/static/test_scenarioFormationMeanOEFeedback.svg
   :align: center


Illustration of Simulation Results
----------------------------------

::

    show_plots = True, useClassicElem = True

In this case, target orbital element difference is set based on classical orbital element.
This resulting feedback control error is shown below.


.. image:: /_images/Scenarios/scenarioFormationMeanOEFeedback11.svg
   :align: center

::

    show_plots = True, useClassicElem = False

In this case, target orbital element difference is set based on equinoctial orbital element.
This resulting feedback control error is shown below.

.. image:: /_images/Scenarios/scenarioFormationMeanOEFeedback20.svg
   :align: center


"""


import math
import os

import matplotlib.pyplot as plt
import numpy as np
from Basilisk import __path__
from Basilisk.fswAlgorithms import meanOEFeedback
from Basilisk.simulation import extForceTorque
from Basilisk.simulation import simpleNav
from Basilisk.simulation import spacecraft
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import macros
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities import simIncludeGravBody
from Basilisk.utilities import unitTestSupport
from Basilisk.utilities import vizSupport

bskPath = __path__[0]
fileName = os.path.basename(os.path.splitext(__file__)[0])


[docs]def run(show_plots, useClassicElem, numOrbits): """ At the end of the python script you can specify the following example parameters. Args: show_plots (bool): Determines if the script should display plots useClassicElem (bool): Determines if classic orbital element is used numOrbits (float): Number of orbits the simulation should run """ scSim = SimulationBaseClass.SimBaseClass() scSim.SetProgressBar(True) # ----- dynamics ----- # dynProcessName = "dynProcess" dynTaskName = "dynTask" dynProcess = scSim.CreateNewProcess(dynProcessName, 2) dynTimeStep = macros.sec2nano(15.0) dynProcess.addTask(scSim.CreateNewTask(dynTaskName, dynTimeStep)) # sc scObject = spacecraft.Spacecraft() scObject2 = spacecraft.Spacecraft() scObject.ModelTag = "scObject" scObject2.ModelTag = "scObject2" I = [900., 0., 0., 0., 800., 0., 0., 0., 600.] scObject.hub.mHub = 500.0 scObject.hub.r_BcB_B = [[0.0], [0.0], [0.0]] scObject.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I) scObject2.hub.mHub = 500.0 scObject2.hub.r_BcB_B = [[0.0], [0.0], [0.0]] scObject2.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I) scSim.AddModelToTask(dynTaskName, scObject, 2) scSim.AddModelToTask(dynTaskName, scObject2, 2) # grav gravFactory = simIncludeGravBody.gravBodyFactory() earth = gravFactory.createEarth() earth.isCentralBody = True mu = earth.mu earth.useSphericalHarmonicsGravityModel(bskPath + '/supportData/LocalGravData/GGM03S.txt', 2) gravFactory.addBodiesTo(scObject) gravFactory.addBodiesTo(scObject2) # extObj extFTObject2 = extForceTorque.ExtForceTorque() extFTObject2.ModelTag = "externalDisturbance2" scObject2.addDynamicEffector(extFTObject2) scSim.AddModelToTask(dynTaskName, extFTObject2, 3) # simple nav simpleNavObject = simpleNav.SimpleNav() simpleNavObject2 = simpleNav.SimpleNav() simpleNavObject.scStateInMsg.subscribeTo(scObject.scStateOutMsg) simpleNavObject2.scStateInMsg.subscribeTo(scObject2.scStateOutMsg) scSim.AddModelToTask(dynTaskName, simpleNavObject, 1) scSim.AddModelToTask(dynTaskName, simpleNavObject2, 1) # ----- fsw ----- # fswProcessName = "fswProcess" fswTaskName = "fswTask" fswProcess = scSim.CreateNewProcess(fswProcessName, 1) fswTimeStep = macros.sec2nano(15.0) fswProcess.addTask(scSim.CreateNewTask(fswTaskName, fswTimeStep)) # meanOEFeedback meanOEFeedbackObj = meanOEFeedback.meanOEFeedback() meanOEFeedbackObj.ModelTag = "meanOEFeedback" meanOEFeedbackObj.chiefTransInMsg.subscribeTo(simpleNavObject.transOutMsg) meanOEFeedbackObj.deputyTransInMsg.subscribeTo(simpleNavObject2.transOutMsg) extFTObject2.cmdForceInertialInMsg.subscribeTo(meanOEFeedbackObj.forceOutMsg) meanOEFeedbackObj.K = [1e7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e7] meanOEFeedbackObj.targetDiffOeMean = [0.000, 0.000, 0.000, 0.0003, 0.0002, 0.0001] if useClassicElem: meanOEFeedbackObj.oeType = 0 # 0: classic else: meanOEFeedbackObj.oeType = 1 # 1: equinoctial meanOEFeedbackObj.mu = orbitalMotion.MU_EARTH*1e9 # [m^3/s^2] meanOEFeedbackObj.req = orbitalMotion.REQ_EARTH*1e3 # [m] meanOEFeedbackObj.J2 = orbitalMotion.J2_EARTH # [] scSim.AddModelToTask(fswTaskName, meanOEFeedbackObj, 1) # ----- Setup spacecraft initial states ----- # oe = orbitalMotion.ClassicElements() oe.a = 11000 * 1e3 # meters oe.e = 0.4 oe.i = 10.0 * macros.D2R oe.Omega = 00.0 * macros.D2R oe.omega = 70.0 * macros.D2R M = 0.0 * macros.D2R E = orbitalMotion.M2E(M, oe.e) oe.f = orbitalMotion.E2f(E, oe.e) rN, vN = orbitalMotion.elem2rv(mu, oe) orbitalMotion.rv2elem(mu, rN, vN) scObject.hub.r_CN_NInit = rN # m scObject.hub.v_CN_NInit = vN # m/s oe2 = orbitalMotion.ClassicElements() oe2.a = oe.a*(1 + 0.0001) oe2.e = oe.e + 0.0002 oe2.i = oe.i - 0.0003 oe2.Omega = oe.Omega + 0.0004 oe2.omega = oe.omega + 0.0005 M2 = M + 0.0006 E2 = orbitalMotion.M2E(M2, oe.e) oe2.f = orbitalMotion.E2f(E2, oe.e) rN2, vN2 = orbitalMotion.elem2rv(mu, oe2) scObject2.hub.r_CN_NInit = rN2 # m scObject2.hub.v_CN_NInit = vN2 # m/s # ----- log ----- # orbit_period = 2*math.pi/math.sqrt(mu/oe.a**3) simulationTime = orbit_period*numOrbits simulationTime = macros.sec2nano(simulationTime) numDataPoints = 1000 samplingTime = unitTestSupport.samplingTime(simulationTime, dynTimeStep, numDataPoints) dataLog = scObject.scStateOutMsg.recorder(samplingTime) dataLog2 = scObject2.scStateOutMsg.recorder(samplingTime) scSim.AddModelToTask(dynTaskName, dataLog) scSim.AddModelToTask(dynTaskName, dataLog2) # if this scenario is to interface with the BSK Viz, uncomment the following lines # to save the BSK data to a file, uncomment the saveFile line below viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, [scObject, scObject2] # , saveFile=fileName ) # ----- execute sim ----- # scSim.InitializeSimulation() scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() # ----- pull ----- # pos = dataLog.r_BN_N vel = dataLog.v_BN_N pos2 = dataLog2.r_BN_N vel2 = dataLog2.v_BN_N timeData = dataLog.times()*macros.NANO2SEC/orbit_period # ----- plot ----- # # classical oe (figure1) plt.figure(1) oed_cl = np.empty((len(pos[:, 0]), 6)) for i in range(0, len(pos[:, 0])): # spacecraft 1 (chief) oe_cl_osc = orbitalMotion.rv2elem(mu, pos[i], vel[i]) oe_cl_mean = orbitalMotion.ClassicElements() orbitalMotion.clMeanOscMap(orbitalMotion.REQ_EARTH*1e3, orbitalMotion.J2_EARTH, oe_cl_osc, oe_cl_mean, -1) # spacecraft 2 (deputy) oe2_cl_osc = orbitalMotion.rv2elem(mu, pos2[i], vel2[i]) oe2_cl_mean = orbitalMotion.ClassicElements() orbitalMotion.clMeanOscMap(orbitalMotion.REQ_EARTH*1e3, orbitalMotion.J2_EARTH, oe2_cl_osc, oe2_cl_mean, -1) # calculate oed oed_cl[i, 0] = (oe2_cl_mean.a - oe_cl_mean.a)/oe_cl_mean.a # delta a (normalized) oed_cl[i, 1] = oe2_cl_mean.e - oe_cl_mean.e # delta e oed_cl[i, 2] = oe2_cl_mean.i - oe_cl_mean.i # delta i oed_cl[i, 3] = oe2_cl_mean.Omega - oe_cl_mean.Omega # delta Omega oed_cl[i, 4] = oe2_cl_mean.omega - oe_cl_mean.omega # delta omega E_tmp = orbitalMotion.f2E(oe_cl_mean.f, oe_cl_mean.e) E2_tmp = orbitalMotion.f2E(oe2_cl_mean.f, oe2_cl_mean.e) oed_cl[i, 5] = orbitalMotion.E2M( E2_tmp, oe2_cl_mean.e) - orbitalMotion.E2M(E_tmp, oe_cl_mean.e) # delta M for j in range(3, 6): while(oed_cl[i, j] > math.pi): oed_cl[i, j] = oed_cl[i, j] - 2*math.pi while(oed_cl[i, j] < -math.pi): oed_cl[i, j] = oed_cl[i, j] + 2*math.pi plt.plot(timeData, oed_cl[:, 0], label="da") plt.plot(timeData, oed_cl[:, 1], label="de") plt.plot(timeData, oed_cl[:, 2], label="di") plt.plot(timeData, oed_cl[:, 3], label="dOmega") plt.plot(timeData, oed_cl[:, 4], label="domega") plt.plot(timeData, oed_cl[:, 5], label="dM") plt.legend() plt.xlabel("time [orbit]") plt.ylabel("mean orbital element difference") figureList = {} pltName = fileName + "1" + str(int(useClassicElem)) figureList[pltName] = plt.figure(1) # equinoctial oe (figure2) plt.figure(2) oed_eq = np.empty((len(pos[:, 0]), 6)) for i in range(0, len(pos[:, 0])): # spacecraft 1 (chief) oe_cl_osc = orbitalMotion.rv2elem(mu, pos[i], vel[i]) oe_cl_mean = orbitalMotion.ClassicElements() orbitalMotion.clMeanOscMap(orbitalMotion.REQ_EARTH*1e3, orbitalMotion.J2_EARTH, oe_cl_osc, oe_cl_mean, -1) oe_eq_mean = orbitalMotion.EquinoctialElements() orbitalMotion.clElem2eqElem(oe_cl_mean, oe_eq_mean) # spacecraft 2 (deputy) oe2_cl_osc = orbitalMotion.rv2elem(mu, pos2[i], vel2[i]) oe2_cl_mean = orbitalMotion.ClassicElements() orbitalMotion.clMeanOscMap(orbitalMotion.REQ_EARTH*1e3, orbitalMotion.J2_EARTH, oe2_cl_osc, oe2_cl_mean, -1) oe2_eq_mean = orbitalMotion.EquinoctialElements() orbitalMotion.clElem2eqElem(oe2_cl_mean, oe2_eq_mean) # calculate oed oed_eq[i, 0] = (oe2_eq_mean.a - oe_eq_mean.a)/oe_eq_mean.a # delta a (normalized) oed_eq[i, 1] = oe2_eq_mean.P1 - oe_eq_mean.P1 # delta P1 oed_eq[i, 2] = oe2_eq_mean.P2 - oe_eq_mean.P2 # delta P2 oed_eq[i, 3] = oe2_eq_mean.Q1 - oe_eq_mean.Q1 # delta Q1 oed_eq[i, 4] = oe2_eq_mean.Q2 - oe_eq_mean.Q2 # delta Q2 oed_eq[i, 5] = oe2_eq_mean.l - oe_eq_mean.l # delta l while(oed_eq[i, 5] > math.pi): oed_eq[i, 5] = oed_eq[i, 5] - 2*math.pi while(oed_eq[i, 5] < -math.pi): oed_eq[i, 5] = oed_eq[i, 5] + 2*math.pi plt.plot(timeData, oed_eq[:, 0], label="da") plt.plot(timeData, oed_eq[:, 1], label="dP1") plt.plot(timeData, oed_eq[:, 2], label="dP2") plt.plot(timeData, oed_eq[:, 3], label="dQ1") plt.plot(timeData, oed_eq[:, 4], label="dQ2") plt.plot(timeData, oed_eq[:, 5], label="dl") plt.legend() plt.xlabel("time [orbit]") plt.ylabel("mean orbital element difference") pltName = fileName + "2" + str(int(useClassicElem)) figureList[pltName] = plt.figure(2) if(show_plots): plt.show() plt.close("all") return pos, vel, pos2, vel2, numDataPoints, figureList
if __name__ == "__main__": run( True, # show_plots True, # useClassicElem 40 # number of orbits )