Skip to main content
This tutorial walks through scenarioFormationBasic.py, which demonstrates how to simulate multiple spacecraft in a single Basilisk process: a 3-axis attitude-controlled servicer and two tumbling debris objects.

What this scenario demonstrates

  • Creating and configuring multiple spacecraft.Spacecraft() instances in one simulation
  • Synchronizing the numerical integration of coupled spacecraft with syncDynamicsIntegration
  • Using hillPoint to align a spacecraft with its Hill (local vertical / local horizontal) frame
  • Adding free-spinning reaction wheels to a debris object
  • Attaching cameras and lights to spacecraft for Vizard visualization
  • Connecting FSW modules for RW-based attitude control

Simulation layout

The scenario creates three spacecraft in a single simProcess:
  • Servicer (scObject) — attitude-controlled with 3 Honeywell HR16 RWs, Hill-frame pointing
  • Debris 1 (scObject2) — two free-spinning RWs, passive tumble, in a 2:1 centered ellipse relative to the servicer
  • Debris 2 (scObject3) — inert rigid body, lead-follower configuration

Running the scenario

python3 scenarioFormationBasic.py

Step-by-step walkthrough

1

Create the simulation container and task

from Basilisk.utilities import SimulationBaseClass, macros

simTaskName = "simTask"
simProcessName = "simProcess"

scSim = SimulationBaseClass.SimBaseClass()
simulationTime = macros.min2nano(40.)  # [ns] - 40-minute simulation

dynProcess = scSim.CreateNewProcess(simProcessName)
simulationTimeStep = macros.sec2nano(.5)  # [ns] - 0.5-second step
dynProcess.addTask(scSim.CreateNewTask(simTaskName, simulationTimeStep))
2

Create all spacecraft objects

Each spacecraft is an independent Spacecraft instance with its own mass and inertia.
from Basilisk.simulation import spacecraft, svIntegrators
from Basilisk.utilities import unitTestSupport

# Servicer
scObject = spacecraft.Spacecraft()
scObject.ModelTag = "Servicer"
I = [900., 0., 0., 0., 800., 0., 0., 0., 600.]  # [kg*m^2]
scObject.hub.mHub = 750.0  # [kg]
scObject.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I)

# Debris 1
scObject2 = spacecraft.Spacecraft()
scObject2.ModelTag = "Debris"
I2 = [600., 0., 0., 0., 650., 0., 0., 0, 450.]  # [kg*m^2]
scObject2.hub.mHub = 350.0  # [kg]
scObject2.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I2)

# Debris 2
scObject3 = spacecraft.Spacecraft()
scObject3.ModelTag = "DebrisSat"
I3 = [600., 0., 0., 0., 650., 0., 0., 0, 450.]  # [kg*m^2]
scObject3.hub.mHub = 350.0  # [kg]
scObject3.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I3)
3

Synchronize integration and set the integrator

By default each Spacecraft integrates independently. Calling syncDynamicsIntegration makes the debris integration lock-step with the servicer. This matters when a dynamic effector couples both objects.
# Sync Debris integration to Servicer
scObject.syncDynamicsIntegration(scObject2)

# Override the integrator on the primary spacecraft (RK4 is also the default)
integratorObject = svIntegrators.svIntegratorRK4(scObject)
scObject.setIntegrator(integratorObject)
# Note: do NOT call setIntegrator on scObject2 after syncing — it will raise an error
Only the primary spacecraft (the one you called syncDynamicsIntegration on) should have its integrator changed after syncing. Trying to change the secondary’s integrator will raise an error.
4

Add all spacecraft to the task

scSim.AddModelToTask(simTaskName, scObject)
scSim.AddModelToTask(simTaskName, scObject2)
scSim.AddModelToTask(simTaskName, scObject3)
5

Set up gravity for all spacecraft

from Basilisk.utilities import simIncludeGravBody

gravFactory = simIncludeGravBody.gravBodyFactory()
earth = gravFactory.createEarth()
earth.isCentralBody = True
mu = earth.mu

gravFactory.addBodiesTo(scObject)
gravFactory.addBodiesTo(scObject2)
gravFactory.addBodiesTo(scObject3)
6

Add reaction wheels to the servicer and free-spin RWs to debris

The servicer carries three controlled Honeywell HR16 RWs. The debris object has two freely spinning RWs — no motor torque is applied to them.
from Basilisk.simulation import reactionWheelStateEffector
from Basilisk.utilities import simIncludeRW
from Basilisk.architecture import messaging

# Servicer RWs
rwFactory = simIncludeRW.rwFactory()
varRWModel = messaging.BalancedWheels

RW1 = rwFactory.create('Honeywell_HR16', [1, 0, 0], maxMomentum=50.,
                        Omega=100., RWModel=varRWModel)  # [RPM]
RW2 = rwFactory.create('Honeywell_HR16', [0, 1, 0], maxMomentum=50.,
                        Omega=200., RWModel=varRWModel)  # [RPM]
RW3 = rwFactory.create('Honeywell_HR16', [0, 0, 1], maxMomentum=50.,
                        Omega=300., rWB_B=[0.5, 0.5, 0.5],  # [m] offset
                        RWModel=varRWModel)

rwStateEffector = reactionWheelStateEffector.ReactionWheelStateEffector()
rwFactory.addToSpacecraft("chiefRW", rwStateEffector, scObject)
scSim.AddModelToTask(simTaskName, rwStateEffector, 4)  # priority 4 > scObject

# Debris free-spinning RWs (no motor torque)
rwFactory2 = simIncludeRW.rwFactory()
rwFactory2.create('Honeywell_HR16', [1, 0, 0], maxMomentum=50., Omega=1000.0)
rwFactory2.create('Honeywell_HR16', [0, 1, 0], maxMomentum=50., Omega=-1000.0)
rwStateEffector2 = reactionWheelStateEffector.ReactionWheelStateEffector()
rwFactory2.addToSpacecraft("debrisRW", rwStateEffector2, scObject2)
scSim.AddModelToTask(simTaskName, rwStateEffector2, 5)
The integer priority passed to AddModelToTask() controls execution order within the task. Effectors must execute before the spacecraft dynamics module so use a higher priority number for RW effectors.
7

Set up navigation and Hill-frame guidance

The servicer uses hillPoint rather than inertial3D — it aligns the spacecraft body to the Hill (LVLH) frame.
from Basilisk.simulation import simpleNav
from Basilisk.fswAlgorithms import hillPoint, attTrackingError, mrpFeedback, rwMotorTorque

sNavObject = simpleNav.SimpleNav()
sNavObject.ModelTag = "SimpleNavigation"
sNavObject.scStateInMsg.subscribeTo(scObject.scStateOutMsg)
scSim.AddModelToTask(simTaskName, sNavObject)

attGuidance = hillPoint.hillPoint()
attGuidance.ModelTag = "hillPoint"
attGuidance.transNavInMsg.subscribeTo(sNavObject.transOutMsg)
scSim.AddModelToTask(simTaskName, attGuidance)

attError = attTrackingError.attTrackingError()
attError.ModelTag = "attErrorInertial3D"
# Rotate reference so the 3rd body axis points along-track
attError.sigma_R0R = [0.414214, 0.0, 0.0]
attError.attRefInMsg.subscribeTo(attGuidance.attRefOutMsg)
attError.attNavInMsg.subscribeTo(sNavObject.attOutMsg)
scSim.AddModelToTask(simTaskName, attError)
8

Set initial orbital conditions for all spacecraft

The debris objects start in a 2:1 Hill-frame ellipse and lead-follower configuration relative to the servicer.
from Basilisk.utilities import orbitalMotion

oe = orbitalMotion.ClassicElements()
oe.a = 10000000.0  # [m]   - 10,000 km semi-major axis
oe.e = 0.01
oe.i = 33.3 * macros.D2R
oe.Omega = 48.2 * macros.D2R
oe.omega = 347.8 * macros.D2R
oe.f = 85.3 * macros.D2R
rN, vN = orbitalMotion.elem2rv(mu, oe)

scObject.hub.r_CN_NInit = rN
scObject.hub.v_CN_NInit = vN
scObject.hub.sigma_BNInit = [[0.1], [0.2], [-0.3]]
scObject.hub.omega_BN_BInit = [[0.001], [-0.01], [0.03]]  # [rad/s]

# Debris objects start in slightly different orbits (Hill-frame offsets)
# See the full scenario file for debris IC computation
9

Initialize and execute

scSim.InitializeSimulation()
scSim.ConfigureStopTime(simulationTime)
scSim.ExecuteSimulation()
The simulation produces attitude error, RW motor torque, rate error, and RW speed plots for the servicer.

Module execution order

For correct dynamics, modules must execute in this order within each time step:
RW effectors  (priority 4-5)

Spacecraft dynamics  (default priority)

Navigation sensors

FSW algorithms (guidance → error → control → allocation)
Pass a higher integer priority to AddModelToTask() to schedule a module earlier in the step.

Vizard support

The scenario includes vizSupport.enableUnityVisualization() with all three spacecraft. Vizard renders each body using its ModelTag as the display name. Camera and light instruments can be attached to any spacecraft via vizSupport helper calls before InitializeSimulation().

Build docs developers (and LLMs) love