Repository navigation
Home
| Rules & Info | Competitor Starter Code (Here) | Forum |
|---|
Link to Specs and Environmental Model Details section
- Open Space Teams PRO through SpaceCRAFT.exe, and log in.
- Open Sim from the main menu and select the User made sim checkbox on the left.
- Select
Repos/STU4-Sim/STU4_500Rocks.simconf, then select Confirm.
Start with:
-
Systems/Controller.py- the Python file that will be run from the sim config in STU4-Sim. -
Systems/BasicController.py- example of main autonomous-controller template. -
Systems/UserAPI.py- example of a smaller API-oriented example for reading state and sending commands.
The recommended workflow is to edit the user-code section of Systems/Controller.py, below the two large boilerplate markers. Keep the simulator connection boilerplate unchanged.
The common objects are initialized in BasicController.py:
this = st.GetThisSystem()
lander = this.GetParam(st.VarType.entityRef, "Lander")
phobos = this.GetParam(st.VarType.entityRef, "Phobos")
target = this.GetParam(st.VarType.entityRef, "Target")Useful state queries include:
state = lander.getLocVelAcc()
location = state.loc_WRT_ExprIn(phobos.GetBodyFixedFrame())
velocity = state.vel_WRT_ExprIn(phobos.GetBodyFixedFrame())
rotation = lander.getRotation()
angular_velocity = lander.getAngVelocity()
mass = lander.getMass()
inertia = lander.getInertiaTensor(lander.GetBodyFixedFrame())
damage = lander.GetParam(st.VarType.double, ["Stats", "Damage"])The target's position can be read in the same frame:
target_state = target.getLocVelAcc()
target_location = target_state.loc_WRT_ExprIn(phobos.GetBodyFixedFrame())Always make frame conversions explicit. The simulation uses Phobos-centered frames for mission geometry and the lander's body-fixed frame for actuator commands.
Use the helper already provided by BasicController.py or UserAPI.py:
SetCommand(linear_acceleration, angular_acceleration)Both arguments are NumPy 3-vectors:
-
linear_acceleration: m/s^2, expressed in the lander's body frame. -
angular_acceleration: rad/s^2, expressed in the lander's body frame.
Example:
linear_acceleration = np.array([0.0, 0.0, 1.0])
angular_acceleration = np.zeros(3)
SetCommand(linear_acceleration, angular_acceleration)Thruster acceleration is limited by each thruster's maximum thrust force. Excessive acceleration commands are capped to within the vehicle's available acceleration.
Systems/Radar.py publishes noise-aberrated debris observations to the lander. The nearest observations are available through the helper:
debris = GetNearestDebrisLocations(4)Each returned item is a doubleM3x3 array:
debris_center = item[0, :] # m, Phobos-fixed-frame coordinates
closest_range = item[1, 0] # m, range to closest part of debris, with sphere shape assumption
range_rate = item[1, 1] # m/s, positive is increasing range
radar_diameter = item[1, 2] # mThe array is sorted nearest to furthest distance from vehicle to debris center.
BasicController.py exposes TopCamera and BottomCamera. To point a camera, use:
PanTiltCamera(topCamera, yaw_degrees, pitch_degrees)The helper clamps the angles to the camera's supported range. Camera image capture is available through the SpaceTeams camera API and is useful for debugging what the lander can currently see or for building a simple vision-based target detector.
A typical capture flow looks like this:
cameraParams = st.CaptureImageProperties()
cameraParams.nonphys_EV = 12.0
cameraParams.FOV = 60.0
cameraParams.ResolutionX = 512
cameraParams.ResolutionY = 512
image = st.camera.CaptureImageBlocking(topCamera, cameraParams)st.camera.CaptureImageBlocking will wait until the image has captured and has been returned from the Unreal client program. This may take hundreds of milliseconds during each call.
It is intended for competitors to only change python code in Controller.py, and add additional scripts if necessary. Using any library, framework, dataset, or other tool is allowed, as long as you are legally allowed to submit the full source code (or, for example, pip package names) to us as part of your submission. We must be able to run your solution code on our own computers to validate your solution. Retrieving debris information through the sim, instead of through the limitations of the radar and the camera, is not allowed. Directly affecting entity motion through functions other than the thruster commands is not allowed. Changing entity physics, scoring, System activation, thruster data, or other sim config / entity config data is not allowed. If you have questions about this, please ask on the Forum.
This example points acceleration toward the target without using radar or obstacle detection:
lander_state = lander.getLocVelAcc()
lander_location = lander_state.loc_WRT_ExprIn(phobosFrame)
lander_velocity = lander_state.vel_WRT_ExprIn(phobosFrame)
target_state = target.getLocVelAcc()
target_location = target_state.loc_WRT_ExprIn(phobosFrame)
to_target = target_location - lander_location
distance = np.linalg.norm(to_target)
if distance > 1.0:
direction = to_target / distance
desired_velocity = 10.0 * direction
acceleration = desired_velocity - lander_velocity
acceleration = st.frames.FramedVector(acceleration, phobosFrame).ExprIn(landerFrame)
SetCommand(acceleration, np.zeros(3))
else:
SetCommand(np.zeros(3), np.zeros(3))This is only a starting point. A competitive controller should account for noisy radar observations, relative velocity, acceleration limits, attitude, debris geometry, fuel usage, and the scoring tradeoff between speed and damage.
An orthographic, wireframe view of the shuttle is shown below:

Each image is centered on the shuttle's center of mass, and each grid square is 1
The shuttle position, velocity, orientation, and angular velocity can all be obtained directly from the Space Teams PRO simulation. This data can be treated as the "truth" and there is no need to filter it. If it helps, you can also treat the data as if it came from a very high-quality inertial measurement unit (IMU) with imperceptible bias and noise. Example code for obtaining these states is provided.
The interplanetary spacecraft does not move with respect to Phobos, and hovers roughly a kilometer above the surface. Its exact coordinates can be obtained in a similar manner to the shuttle states, and example code for this is provided as well.
Every object within the debris field starts the simulation with a random initial position, velocity, orientation, and angular velocity. While these states cannot be obtained directly at any point during the simulation, the shuttle's radar can obtain estimates for some of these states (see below).
The competition environment is characterized by the concurrent gravitational pulls of Phobos and Mars. The motion of every object in the simulation—excluding the interplanetary spacecraft—is affected by the combined gravitational field of the two bodies. Gravity from the Sun, Deimos (Mars' other moon), and other planets/moons/asteroids is not included. Additionally, solar radiation pressure (SRP) is not modeled.
All object states are integrated in the Phobos-fixed reference frame. Since Phobos has a relatively slow rotation period, and since the simulation run time should not exceed several minutes, this reference frame can be treated as inertial. Some states are also provided in the shuttle body-fixed reference frame (e.g., radar outputs). See below for more details.
The shuttle's radar accumulates measurements of position, range rate, and diameter for the N nearest pieces of debris. The data is provided in a
The third row of the matrix is not used. Each term in the top two rows is defined as follows:
| Component | Description | Estimate Standard Deviation |
|---|---|---|
| The object's center of mass with respect to the shuttle, expressed in the Phobos-fixed reference frame | 2 meters | |
| The distance from the shuttle to the closest point on the object | 2 meters | |
| The range rate of the object with respect to the shuttle (i.e., the object's speed along the direction away from the shuttle) | 0.02 meters per second | |
| The average diameter of the object | 5% of the object's true diameter |
The shuttle has two cameras that can be used to obtain images of the surrounding environment in real time. The camera specifications are listed below:
| Camera | Position (meters, shuttle body-fixed frame) | Yaw Limits (degrees, shuttle body-fixed frame) | Pitch Limits (degrees, shuttle body-fixed frame) |
|---|---|---|---|
| Top Camera | |||
| Bottom Camera |
To avoid confusion, the pitch and yaw of each camera is defined as follows:
Your run is successful when the crew vehicle reaches the designated safe area near the interplanetary ship. Reaching the safe area stops the clock and ends your run.
Collisions with debris do not disqualify you, but they cause damage that lowers your score. A successful run is one that gets the crew to safety as quickly, cleanly, and efficiently as possible.
There is no pass/fail cutoff in this competition. Every run receives a score, and a poor run simply receives a poor score.
However, a run will end early if the crew vehicle accumulates the maximum allowable damage. When this happens, the mission is over and the run is scored based on distance from the interplanetary ship in place of time. All runs that don't arrive at the interplanetary ship are in a lower score "category" than all runs that do arrive. The exact scoring function may be adjusted by the competition organizers.
Fuel is not limited, and running low or using a lot of fuel will not end your run. It will only reduce your score.
Your score is based on three factors: how fast you reach the safe area, how much damage the vehicle takes, and how much fuel you use. Each is compared against a nominal (reference) run.
| Component | Formula | Notes |
|---|---|---|
| Time Score | Nominal Time ÷ Your Time | Faster than nominal gives a value above 1.0; slower gives a value below 1.0. |
| Damage Penalty | 2 × (Your Damage ÷ Max Damage) | No damage gives 0. Reaching max damage gives 2.0 and ends the run. |
| Fuel Penalty | Your Fuel Usage ÷ Nominal Fuel Usage | Less fuel than nominal gives a penalty below 1.0; more gives a penalty above 1.0. |
Raw Score = Time Score − Damage Penalty − Fuel Penalty
A raw score of 0 means you matched the nominal run. Positive is better than nominal; negative is worse.
The raw score is converted to a final score by mapping −0.5 → 0 and +0.5 → 100:
Final Score = (Raw Score + 0.5) × 100
A nominal run scores 50. The final score is not capped, so exceptional runs can exceed 100 and very poor runs can fall below 0 (for example, a raw score of −1.0 becomes a final score of −50).
You reach the safe area in 80% of the nominal time (Time Score = 1.25), take damage equal to 5% of the maximum (Penalty = 0.10), and use 90% of the nominal fuel (Penalty = 0.90).
Raw Score = 1.25 − 0.10 − 0.90 = 0.25 → Final Score = 75