diff --git a/.gitignore b/.gitignore index 94cb7771..acdb8a17 100644 --- a/.gitignore +++ b/.gitignore @@ -123,8 +123,8 @@ src/bsk_rl/_dat/simplemaps_worldcities/ # Sphinx documentation /docs/build/* -/docs/source/API Reference/* -/docs/source/Examples/* +/docs/source/api_reference/* +/docs/source/examples/* # executed ipynb cache !/docs/build/doctrees diff --git a/docs/Makefile b/docs/Makefile index 028fc57c..bbb99d99 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -20,7 +20,7 @@ help: @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) clean: - rm -rf "source/Examples" "source/API Reference" "build" + rm -rf "source/examples" "source/api_reference" "build" view: diff --git a/docs/build/doctrees/nbsphinx/Examples/simple_environment.ipynb b/docs/build/doctrees/nbsphinx/Examples/simple_environment.ipynb index 875e6d3f..9e5c9d8f 100644 --- a/docs/build/doctrees/nbsphinx/Examples/simple_environment.ipynb +++ b/docs/build/doctrees/nbsphinx/Examples/simple_environment.ipynb @@ -4,25 +4,571 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Satellite Configuration" + "# Getting Started\n", + "This tutorial demonstrates the configuration and use of a simple BSK-RL environment.\n", + "BSK-RL and dependencies should already be installed at this point (see [Installation](../install.rst)\n", + "if you haven't installed the package yet).\n", + "\n", + "## Load Modules\n", + "In this tutorial, the environment will be created with `gym.make`, so it is necessary to\n", + "import the top-level `bsk_rl` module as well as `gym` and `bsk_rl` components." ] }, { "cell_type": "code", "execution_count": 1, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:10.447229Z", + "iopub.status.busy": "2024-05-29T22:28:10.446836Z", + "iopub.status.idle": "2024-05-29T22:28:11.239280Z", + "shell.execute_reply": "2024-05-29T22:28:11.238867Z" + } + }, + "outputs": [], + "source": [ + "from Basilisk.architecture import bskLogging\n", + "bskLogging.setDefaultLogLevel(bskLogging.BSK_WARNING)\n", + "\n", + "import gymnasium as gym\n", + "import numpy as np\n", + "import bsk_rl\n", + "from bsk_rl import act, data, obs, scene, sats\n", + "from bsk_rl.sim import dyn, fsw" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If no errors were raised, you have a functional installation of `bsk_rl`.\n", + "\n", + "## Configure the Satellite\n", + "[Satellites](../api_reference/sats/index.rst) are configurable agents in the environment.\n", + "To make a new environment, start by specifying the [observations](../api_reference/obs/index.rst)\n", + "and [actions](../api_reference/act/index.rst) of a satellite type, as well as the underlying\n", + "Basilisk [simulation](../api_reference/sim/index.rst) models used by the satellite." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.241269Z", + "iopub.status.busy": "2024-05-29T22:28:11.241094Z", + "iopub.status.idle": "2024-05-29T22:28:11.243274Z", + "shell.execute_reply": "2024-05-29T22:28:11.243012Z" + } + }, + "outputs": [], + "source": [ + "class MyScanningSatellite(sats.AccessSatellite):\n", + " observation_spec = [\n", + " obs.SatProperties(\n", + " dict(prop=\"storage_level_fraction\"),\n", + " dict(prop=\"battery_charge_fraction\")\n", + " ),\n", + " obs.Eclipse(),\n", + " ]\n", + " action_spec = [\n", + " act.Scan(duration=60.0),\n", + " act.Charge(duration=120.0),\n", + " ]\n", + " dyn_type = dyn.ContinuousImagingDynModel\n", + " fsw_type = fsw.ContinuousImagingFSWModel" + ] + }, + { + "cell_type": "markdown", "metadata": {}, + "source": [ + "Based on this class specification, a list of configurable parameters for the satellite\n", + "can be generated." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.244786Z", + "iopub.status.busy": "2024-05-29T22:28:11.244685Z", + "iopub.status.idle": "2024-05-29T22:28:11.248606Z", + "shell.execute_reply": "2024-05-29T22:28:11.248361Z" + } + }, "outputs": [ + { + "data": { + "text/plain": [ + "{'hs_min': 0.0,\n", + " 'maxCounterValue': 4,\n", + " 'thrMinFireTime': 0.02,\n", + " 'desatAttitude': 'sun',\n", + " 'controlAxes_B': [1, 0, 0, 0, 1, 0, 0, 0, 1],\n", + " 'thrForceSign': 1,\n", + " 'K': 7.0,\n", + " 'Ki': -1,\n", + " 'P': 35.0,\n", + " 'imageAttErrorRequirement': 0.01,\n", + " 'imageRateErrorRequirement': None,\n", + " 'inst_pHat_B': [0, 0, 1],\n", + " 'batteryStorageCapacity': 288000.0,\n", + " 'storedCharge_Init': ()>,\n", + " 'disturbance_vector': None,\n", + " 'dragCoeff': 2.2,\n", + " 'imageTargetMaximumRange': -1,\n", + " 'instrumentBaudRate': 8000000.0,\n", + " 'instrumentPowerDraw': -30.0,\n", + " 'basePowerDraw': 0.0,\n", + " 'wheelSpeeds': ()>,\n", + " 'maxWheelSpeed': inf,\n", + " 'u_max': 0.2,\n", + " 'rwBasePower': 0.4,\n", + " 'rwMechToElecEfficiency': 0.0,\n", + " 'rwElecToMechEfficiency': 0.5,\n", + " 'panelArea': 1.0,\n", + " 'panelEfficiency': 0.2,\n", + " 'nHat_B': array([0, 1, 0]),\n", + " 'mass': 330,\n", + " 'width': 1.38,\n", + " 'depth': 1.04,\n", + " 'height': 1.58,\n", + " 'sigma_init': ()>,\n", + " 'omega_init': ()>,\n", + " 'rN': None,\n", + " 'vN': None,\n", + " 'oe': Basilisk.utilities.orbitalMotion.ClassicElements>,\n", + " 'mu': 398600436000000.0,\n", + " 'dataStorageCapacity': 160000000.0,\n", + " 'storageUnitValidCheck': True,\n", + " 'storageInit': 0,\n", + " 'thrusterPowerDraw': 0.0,\n", + " 'transmitterBaudRate': -8000000.0,\n", + " 'transmitterNumBuffers': 100,\n", + " 'transmitterPowerDraw': -15.0}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "MyScanningSatellite.default_sat_args()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When instantiating a satellite, these parameters can be overriden with a constant or \n", + "rerandomized every time the environment is reset using the ``sat_args`` dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.265615Z", + "iopub.status.busy": "2024-05-29T22:28:11.265500Z", + "iopub.status.idle": "2024-05-29T22:28:11.268017Z", + "shell.execute_reply": "2024-05-29T22:28:11.267758Z" + } + }, + "outputs": [], + "source": [ + "sat_args = {}\n", + "\n", + "# Set some parameters as constants\n", + "sat_args[\"imageAttErrorRequirement\"] = 0.05\n", + "# sat_args[\"imageRateErrorRequirement\"] = 0.01\n", + "sat_args[\"dataStorageCapacity\"] = 1e10\n", + "sat_args[\"instrumentBaudRate\"] = 1e7\n", + "\n", + "# Randomize the initial storage level on every reset\n", + "sat_args[\"storageInit\"] = lambda: np.random.uniform(0.25, 0.75) * 1e10\n", + "\n", + "# Make the satellite\n", + "sat = MyScanningSatellite(name=\"EO1\", sat_args=sat_args)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Making the Environment\n", + "For this example, we will be using the single-agent [SatelliteTasking](../api_reference/index.rst) \n", + "environment.\n", + "\n", + "TODO add more" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.269572Z", + "iopub.status.busy": "2024-05-29T22:28:11.269465Z", + "iopub.status.idle": "2024-05-29T22:28:11.373697Z", + "shell.execute_reply": "2024-05-29T22:28:11.373377Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,270 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[mCalling env.reset() to get observation space\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,270 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[mResetting environment with seed=2345897560\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,370 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,371 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[mEnvironment reset\u001b[0m\n" + ] + } + ], + "source": [ + "env = gym.make(\n", + " \"SatelliteTasking-v1\",\n", + " satellite=sat,\n", + " scenario=scene.UniformNadirScanning(),\n", + " rewarder=data.ScanningTimeReward(),\n", + " log_level=\"INFO\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interacting with the Environment\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.375353Z", + "iopub.status.busy": "2024-05-29T22:28:11.375226Z", + "iopub.status.idle": "2024-05-29T22:28:11.624402Z", + "shell.execute_reply": "2024-05-29T22:28:11.624144Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,458 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[mResetting environment with seed=0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,622 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,622 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[mEnvironment reset\u001b[0m\n" + ] + } + ], + "source": [ + "observation, info = env.reset(seed=0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.625947Z", + "iopub.status.busy": "2024-05-29T22:28:11.625842Z", + "iopub.status.idle": "2024-05-29T22:28:11.645303Z", + "shell.execute_reply": "2024-05-29T22:28:11.645045Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,626 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[93;1m=== STARTING STEP ===\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,626 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[maction_nadir_scan tasked for 60.0 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,627 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[msetting timed terminal event at 60.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,627 \u001b[0m\u001b[msim.simulator \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<0.00> \u001b[0m\u001b[mRunning simulation at most to 600.00 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,631 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[mtimed termination at 60.0 for action_nadir_scan\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,631 \u001b[0m\u001b[mdata.base \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[mData reward: {'EO1_11923516688': 0.0}\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,632 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,632 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[mStep reward: 0.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,632 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[93;1m=== STARTING STEP ===\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,633 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[maction_nadir_scan tasked for 60.0 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,633 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[msetting timed terminal event at 120.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,633 \u001b[0m\u001b[msim.simulator \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<60.00> \u001b[0m\u001b[mRunning simulation at most to 660.00 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,637 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[mtimed termination at 120.0 for action_nadir_scan\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,637 \u001b[0m\u001b[mdata.base \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[mData reward: {'EO1_11923516688': 20.0}\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,638 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,638 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[mStep reward: 20.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,638 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[93;1m=== STARTING STEP ===\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,638 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[maction_nadir_scan tasked for 60.0 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,638 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[msetting timed terminal event at 180.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,639 \u001b[0m\u001b[msim.simulator \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<120.00> \u001b[0m\u001b[mRunning simulation at most to 720.00 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,642 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[mtimed termination at 180.0 for action_nadir_scan\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,642 \u001b[0m\u001b[mdata.base \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[mData reward: {'EO1_11923516688': 60.0}\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,643 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,643 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[mStep reward: 60.0\u001b[0m\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "True\n" + "0.5102387397\n", + "0.5902387397\n" + ] + } + ], + "source": [ + "print(observation[0])\n", + "for _ in range(3):\n", + " observation, reward, terminated, truncated, info = env.step(action=0)\n", + "print(observation[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "execution": { + "iopub.execute_input": "2024-05-29T22:28:11.646834Z", + "iopub.status.busy": "2024-05-29T22:28:11.646727Z", + "iopub.status.idle": "2024-05-29T22:28:11.657637Z", + "shell.execute_reply": "2024-05-29T22:28:11.657381Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,647 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[93;1m=== STARTING STEP ===\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,647 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[maction_charge tasked for 120.0 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,647 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[msetting timed terminal event at 300.0\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,647 \u001b[0m\u001b[msim.simulator \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<180.00> \u001b[0m\u001b[mRunning simulation at most to 780.00 seconds\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,655 \u001b[0m\u001b[36msats.satellite.EO1 \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<300.00> \u001b[0m\u001b[36mEO1: \u001b[0m\u001b[mtimed termination at 300.0 for action_charge\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,655 \u001b[0m\u001b[mdata.base \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<300.00> \u001b[0m\u001b[mData reward: {'EO1_11923516688': 0.0}\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,656 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<300.00> \u001b[0m\u001b[mSatellites requiring retasking: ['EO1_11923516688']\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[90;3m2024-05-29 15:28:11,656 \u001b[0m\u001b[mgym \u001b[0m\u001b[mINFO \u001b[0m\u001b[33m<300.00> \u001b[0m\u001b[mStep reward: 0.0\u001b[0m\n" ] } ], "source": [ - "if 1 > 0:\n", - " print(\"True\")" + "observation, reward, terminated, truncated, info = env.step(action=1)\n" ] } ], diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index 8a6495fc..10abaf09 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -48,6 +48,9 @@ a.el:visited { } } +div.nboutput.container div.output_area.stderr { + background: rgb(233, 242, 249); +} code span.pre, code { @@ -307,4 +310,8 @@ figure { .rst-content .highlighted { background-color: #333; } + + div.nboutput.container div.output_area.stderr { + background: rgb(9, 11, 12); + } } \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 8d5de467..bbae07dd 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -279,12 +279,6 @@ def run(self, source_dir=None): ) for notebook in nb_paths: - print( - "COPYING", - notebook, - "TO", - self.base_doc_dir / index_path / notebook.name, - ) shutil.copy(notebook, self.base_doc_dir / index_path / notebook.name) # Recursively go through all directories in source, documenting what is available. @@ -297,5 +291,5 @@ def run(self, source_dir=None): sys.path.append(os.path.abspath("../..")) -PackageCrawler(Path("../../src/bsk_rl/"), Path("./API Reference/")).run() -PackageCrawler(Path("../../examples"), Path("./Examples/"), filter_all=False).run() +PackageCrawler(Path("../../src/bsk_rl/"), Path("./api_reference/")).run() +PackageCrawler(Path("../../examples"), Path("./examples/"), filter_all=False).run() diff --git a/docs/source/index.rst b/docs/source/index.rst index cf424527..4cfb2c5e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,8 +5,8 @@ BSK-RL: Environments Spacecraft Planning and Scheduling :hidden: install - Examples/index - API Reference/index + examples/index + api_reference/index release_notes publications citation @@ -15,18 +15,28 @@ BSK-RL: Environments Spacecraft Planning and Scheduling .. warning:: - The 1.0.0 release has significant changes from previous versions. See the :doc:`Release Notes ` for more information. + The 1.0.0 release has significant changes from previous versions. See the + :doc:`Release Notes ` for more information. -**BSK-RL** (`Basilisk `_ + `Reinforcement Learning `_) is a Python package for constructing `Gymnasium `_ environments for spacecraft tasking problems. It is built on top of `Basilisk `_, a modular and fast spacecraft simulation framework, making the simulation environments high-fidelity and computationally efficient. BSK-RL also includes a collection of agents, training scripts, and examples for working with these environments. +**BSK-RL** (`Basilisk `_ + +`Reinforcement Learning `_) is a +Python package for constructing `Gymnasium `_ +environments for spacecraft tasking problems. It is built on top of +`Basilisk `_, a modular and fast spacecraft +simulation framework, making the simulation environments high-fidelity and computationally +efficient. BSK-RL also includes a collection of agents, training scripts, and examples +for working with these environments. Quickstart ---------- Installation ^^^^^^^^^^^^ -Complete installation instructions and common troubleshooting tips can be found :doc:`here `. To install BSK-RL: +Complete installation instructions and common troubleshooting tips can be found +:doc:`here `. To install BSK-RL: -#. Install the `Basilisk `_ spacecraft simulation framework. +#. Install the `Basilisk `_ spacecraft simulation + framework. #. Clone BSK-RL. .. code-block:: console @@ -47,6 +57,10 @@ Complete installation instructions and common troubleshooting tips can be found Construct an Environment ^^^^^^^^^^^^^^^^^^^^^^^^ + +A more detailed tutorial can be found at :doc:`examples/simple_environment`. To construct +an environment + #. Import the necessary modules. .. code-block:: python @@ -113,8 +127,14 @@ Construct an Environment Acknowledgements ---------------- -BSK-RL is developed by the `Autonomous Vehicle Systems (AVS) Lab `_ at the University of Colorado Boulder. The AVS Lab is part of the `Colorado Center for Astrodynamics Research (CCAR) `_ and the `Department of Aerospace Engineering Sciences `_. +BSK-RL is developed by the `Autonomous Vehicle Systems (AVS) Lab `_ +at the University of Colorado Boulder. The AVS Lab is part of the `Colorado Center for Astrodynamics Research (CCAR) `_ +and the `Department of Aerospace Engineering Sciences `_. -Development has been supported by NASA Space Technology Graduate Research Opportunity (NSTGRO) grants, 80NSSC20K1162 and 80NSSC23K1182. This work has also been supported by Air Force Research Lab grant FA9453-22-2-0050. +Development has been supported by NASA Space Technology Graduate Research Opportunity +(NSTGRO) grants, 80NSSC20K1162 and 80NSSC23K1182. This work has also been supported by +Air Force Research Lab grant FA9453-22-2-0050. -Development of this software has utilized the Alpine high performance computing resource at the University of Colorado Boulder. Alpine is jointly funded by the University of Colorado Boulder, the University of Colorado Anschutz, and Colorado State University. \ No newline at end of file +Development of this software has utilized the Alpine high performance computing resource +at the University of Colorado Boulder. Alpine is jointly funded by the University of +Colorado Boulder, the University of Colorado Anschutz, and Colorado State University. \ No newline at end of file diff --git a/examples/simple_environment.ipynb b/examples/simple_environment.ipynb index 875e6d3f..e9911fa7 100644 --- a/examples/simple_environment.ipynb +++ b/examples/simple_environment.ipynb @@ -4,25 +4,174 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Satellite Configuration" + "# Getting Started\n", + "This tutorial demonstrates the configuration and use of a simple BSK-RL environment.\n", + "BSK-RL and dependencies should already be installed at this point (see [Installation](../install.rst)\n", + "if you haven't installed the package yet).\n", + "\n", + "## Load Modules\n", + "In this tutorial, the environment will be created with `gym.make`, so it is necessary to\n", + "import the top-level `bsk_rl` module as well as `gym` and `bsk_rl` components." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], + "outputs": [], "source": [ - "if 1 > 0:\n", - " print(\"True\")" + "from Basilisk.architecture import bskLogging\n", + "bskLogging.setDefaultLogLevel(bskLogging.BSK_WARNING)\n", + "\n", + "import gymnasium as gym\n", + "import numpy as np\n", + "import bsk_rl\n", + "from bsk_rl import act, data, obs, scene, sats\n", + "from bsk_rl.sim import dyn, fsw" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If no errors were raised, you have a functional installation of `bsk_rl`.\n", + "\n", + "## Configure the Satellite\n", + "[Satellites](../api_reference/sats/index.rst) are configurable agents in the environment.\n", + "To make a new environment, start by specifying the [observations](../api_reference/obs/index.rst)\n", + "and [actions](../api_reference/act/index.rst) of a satellite type, as well as the underlying\n", + "Basilisk [simulation](../api_reference/sim/index.rst) models used by the satellite." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class MyScanningSatellite(sats.AccessSatellite):\n", + " observation_spec = [\n", + " obs.SatProperties(\n", + " dict(prop=\"storage_level_fraction\"),\n", + " dict(prop=\"battery_charge_fraction\")\n", + " ),\n", + " obs.Eclipse(),\n", + " ]\n", + " action_spec = [\n", + " act.Scan(duration=60.0),\n", + " act.Charge(duration=120.0),\n", + " ]\n", + " dyn_type = dyn.ContinuousImagingDynModel\n", + " fsw_type = fsw.ContinuousImagingFSWModel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Based on this class specification, a list of configurable parameters for the satellite\n", + "can be generated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MyScanningSatellite.default_sat_args()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When instantiating a satellite, these parameters can be overriden with a constant or \n", + "rerandomized every time the environment is reset using the ``sat_args`` dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sat_args = {}\n", + "\n", + "# Set some parameters as constants\n", + "sat_args[\"imageAttErrorRequirement\"] = 0.05\n", + "# sat_args[\"imageRateErrorRequirement\"] = 0.01\n", + "sat_args[\"dataStorageCapacity\"] = 1e10\n", + "sat_args[\"instrumentBaudRate\"] = 1e7\n", + "\n", + "# Randomize the initial storage level on every reset\n", + "sat_args[\"storageInit\"] = lambda: np.random.uniform(0.25, 0.75) * 1e10\n", + "\n", + "# Make the satellite\n", + "sat = MyScanningSatellite(name=\"EO1\", sat_args=sat_args)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Making the Environment\n", + "For this example, we will be using the single-agent [SatelliteTasking](../api_reference/index.rst) \n", + "environment.\n", + "\n", + "TODO add more" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "env = gym.make(\n", + " \"SatelliteTasking-v1\",\n", + " satellite=sat,\n", + " scenario=scene.UniformNadirScanning(),\n", + " rewarder=data.ScanningTimeReward(),\n", + " log_level=\"INFO\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interacting with the Environment\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "observation, info = env.reset(seed=0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(observation[0])\n", + "for _ in range(3):\n", + " observation, reward, terminated, truncated, info = env.step(action=0)\n", + "print(observation[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "observation, reward, terminated, truncated, info = env.step(action=1)\n" ] } ], diff --git a/src/bsk_rl/gym.py b/src/bsk_rl/gym.py index 54a1efba..338f7eae 100644 --- a/src/bsk_rl/gym.py +++ b/src/bsk_rl/gym.py @@ -40,7 +40,7 @@ def __init__( communicator: Optional[CommunicationMethod] = None, sim_rate: float = 1.0, max_step_duration: float = 600.0, - failure_penalty: float = -100, + failure_penalty: float = -1.0, time_limit: float = float("inf"), terminate_on_time_limit: bool = False, log_level: Union[int, str] = logging.WARNING, @@ -140,7 +140,7 @@ class MinimumEnv(*types): def _configure_logging(self, log_level, log_dir=None): if isinstance(log_level, str): log_level = log_level.upper() - logger = logging.getLogger("bsk_rl.env") + logger = logging.getLogger("bsk_rl") logger.setLevel(log_level) # Ensure each process has its own logger to avoid conflicts when printing @@ -372,8 +372,12 @@ def step( truncated = self._get_truncated() info = self._get_info() logger.info(f"Step reward: {reward}") - logger.info(f"Episode terminated: {terminated}") - logger.info(f"Episode truncated: {truncated}") + if terminated or truncated: + logger.info(f"Episode terminated: {terminated}") + logger.info(f"Episode truncated: {truncated}") + else: + logger.debug(f"Episode terminated: {terminated}") + logger.debug(f"Episode truncated: {truncated}") logger.debug(f"Step info: {info}") logger.debug(f"Step observation: {observation}") return observation, reward, terminated, truncated, info diff --git a/src/bsk_rl/sim/simulator.py b/src/bsk_rl/sim/simulator.py index 27a4a61a..8f6601f0 100644 --- a/src/bsk_rl/sim/simulator.py +++ b/src/bsk_rl/sim/simulator.py @@ -91,7 +91,9 @@ def run(self) -> None: simulation_time = mc.sec2nano( min(self.sim_time + self.max_step_duration, self.time_limit) ) - logger.info(f"Running simulation to {simulation_time*mc.NANO2SEC:.2f} seconds") + logger.info( + f"Running simulation at most to {simulation_time*mc.NANO2SEC:.2f} seconds" + ) self.ConfigureStopTime(simulation_time) self.ExecuteSimulation() diff --git a/src/bsk_rl/utils/logging_config.py b/src/bsk_rl/utils/logging_config.py index 87769c07..bf9a83ca 100644 --- a/src/bsk_rl/utils/logging_config.py +++ b/src/bsk_rl/utils/logging_config.py @@ -99,8 +99,8 @@ def format(self, record): sat_name = None sat_color = None try: - if record.name.split(".")[4] == "satellites": - sat_name = record.name.split(".")[5] + if record.name.split(".")[1] == "sats": + sat_name = record.name.split(".")[3] if sat_name not in self.satellite_colors: self.satellite_colors[sat_name] = len(self.satellite_colors) % len( sat_color_cycle @@ -109,7 +109,7 @@ def format(self, record): except IndexError: pass - record.shortname = ".".join(record.name.split(".")[3:]) + record.shortname = ".".join(record.name.split(".")[1:]) fstr += style_string("%(shortname)-30s ", color=sat_color, no_format=no_format) fstr += style_string( "%(levelname)-10s ",