STU Lunar SAR API Docs - SimDynamX/SpaceTeamsPRO GitHub Wiki
Up to date for Platform 0.27.5 and STU Lunar SAR 1.3.2
Written by Neil McHenry and Connor Jakubik
Prerequisites:
| Rules & Info | Setup & Learning | STU API Docs (Here) | Q&A Discussions Category | Starter Code | Starter Code Releases |
|---|
Changelog:
2024/11/19Many changes for Task 3, including new API functions and revised gaussian speed vs slope curves.2024/11/09Updates for Platform 0.27.5.2024/11/08Documentation for GetStateOfCharge and GetChargingStationXY Entity Telemetry functions.2024/11/06Small updates for Task 2.2024/11/04Added Slope vs. Power and Slope vs. Speed Curves.2024/10/23Newly created.
Link back to STU 2024 Lunar SAR Rules & Info: https://github.com/SimDynamX/SpaceTeamsPro/discussions/33
Link to Starter Code repository: https://github.com/SimDynamX/STU_Lunar_SAR
Subsection Links:
- Development Environment Setup
- Modifying Files for Tasks 1 & 2
- Handling Errors and Crashes
- API Documentation
- Mission Manager Functions
- Task Graph
How to set up your development environment
- Do the setup steps from here: STU Lunar SAR Competitor Setup & Learning
There are several ways to do python development; here is one way that is the typical way we at SimDynamX set it up.
- Install VS Code and open it
- Do
Open FoldertoC:\Users\YOUR WINDOWS USERNAME\AppData\Local\SimDynamX\STPro\Assets - Open one of the .py files in
Repos/STU_Lunar_SAR_YourTeamName - If prompted, install the recommended python extensions, and wait for those installs to complete
- Set your Python interpreter to the one that was installed during setup
- While having a python file open, click the python version number in the bottom right of the screen
- At the top of the screen, click
Enter interpreter path... - Click
Find... - Navigate to
AppData\Local\SimDynamX\STPro\Versions\(the highest version number in there)\py\Scriptsand selectpython.exe, thenSelect Interpreter - Should now be switched to the correct python interpreter
- And you should get syntax highlighting for the
spaceteamspython module.
- While having a python file open, click the python version number in the bottom right of the screen
Which files to modify for Task 1
The files in the starter code repository that are in the top folder (not API or sims subfolders) are available to be edited by competitors. There are some exceptions:
- There are a few things you will need to edit in the
sims/STU2_SAR_Task1.jsonsim config file:- All paths that reference
STU_Competition2, like the Source file for the MissionManager system will need to have theSTU_Competition2changed out with the repo/folder name that you are using as the name for your copy of the starter code repo.
- All paths that reference
- Don't edit
CompetitionBackend.py - You can in general change the
Sourceon any of these python systems in order to change out which .py file in your repo they are using:MissionManagerEntityBehavior_(any)
Where you should start is making small modifications to the starter code in ExampleMM_TaskgraphSimple.py, and seeing what happens when running the sim. Make good use of st.OnScreenLogMessage() and st.OnScreenAlert() for easy-to-see printing.
Which files to modify for Task 2
Same as Task 1. There are more EntityBehavior_ Systems to modify the Source on than before.
Which files to modify for Final Task
Same as Task 2.
How to run the Task sims in SpaceCRAFT (the graphics portion of SpaceTeamsPRO)
- Do the path rename change mentioned above (after having done all the setup steps)
- Open
SpaceCRAFT.exe - Log in
- On the main menu,
Sim->User-Created Sims->Search for and clickRepos/STU_Lunar_Sar_YourTeamName/sims/STU2_SAR_Task1.json->Confirm
Handling errors and crashes
If the Compute_Server crashed on sim startup, the sim may seem to start normally but around 10 seconds later a dialog box with this message comes up:
UE Server: Failed to connect to server, reason: [10061] Failed to connect to locally-running Compute_Server for simulation; this could mean Compute_Server crashed.
Check the latest-modified file in the Compute_Server/Logs folder for more details.
Would you like to return to the main menu? "No" quits the application.
If the Compute_Server crashed during the sim, a dialog box with this message comes up:
Simulation stopped; reason: [10054] Existing Platform data connection to locally-running Compute_Server was forcibly closed; this could mean Compute_Server crashed.
Check the latest-modified file in the Compute_Server/Logs folder for more details.
Would you like to return to the main menu? "No" quits the application.
If something like this happens, find out what caused the sim to crash by finding the set of most-recently-edited .log files in /Compute_Server/Logs.
The shortest-name log (maybe Sim_STU2_SAR_Task1.log) will be the main Compute_Server log. Open that one.
Common errors:
Python System Instance MissionManager invalid Source filepath- Maybe you didn't rename the folder as explained above?
- Maybe you renamed the .py file but didn't update the sim config to match?
[FATAL]---[Main->PySys_MissionManager @NID:2]: ST Netcode: [10054] Existing connection was forcibly closed by the remote host. This is an unhandled disconnect, probably due to Python System MissionManager crashing / terminating. See Compute_Server/Logs/Sim_STU2_SAR_Task1_PySys_MissionManager.log for more details. Terminating program due to this unhandled disconnect.- Do as it says; the error is likely in that Python System's log file.
API Documentation
Coordinate Bounds
The crash site location is guaranteed to be within these bounds. They are given in both latitude/longitude pairs and local XY coordinates.
Latitude bounds: [-89.39°, -89.34°] = [89.39° S, 89.34° S]
Longitude bounds: [-139.0°, -134.5°] = [139.0° W, 134.5° W]
XY coordinates (meters):
(-317.154, 735.680) - - - - - - - - - - - - - - - (1199.211, 795.979)
| |
| |
| |
| y |
| ↑ |
| → x, North |
| |
| |
| |
| |
(-316.460, -718.026) - - - - - - - - - - - - - - - (1199.962, -776.878)
Available Mission Manager Functions
This section lists the commands available for use in the mission manager script. Each command is designed to control specific actions of an entity within the simulation environment.
Commanding (In General)
-
SendCommand(en, command_type, command)- NOTE: Call this directly when you are not using a taskgraph to dispatch this command.
- Sends a command to an entity.
- The payload can be used to send additional information about the command.
- Returns whether command sent successfully; will need to call this function again if the subject entity is not in comms.
- Use EnHasComms(en) to check if entity has comms.
- Parameters:
en: The entity to command (e.g.,LTV1).command_type: The command type (e.g.,MoveToCoord).command:Commandobject constructed with a validCommand_[command name] function (listed in the next section).
- Example Usage:
# Send a camera pan command azimuth = 150.0 #deg elevation = 10.0 #deg mm.SendCommand(LTV1, "CameraPan", Command_CameraPan(LTV1, azimuth, elevation, ""))
-
OnCommandComplete(en, command_type, reaction)- Registers a reaction (Python function with an
st.ParamMapargument) to a command completion.- A
lambdafunction can also be used as a reaction if additional inputs are needed.
- A
- Parameters:
en: The entity to register a reaction for (e.g.,LTV1).command_type: The command type (e.g.,MoveToCoord).reaction: Python function object (type the name only unless using a lambda).
- Example Usage (no lambda):
# Reaction to a stop command def Stop_Complete_LTV1(payload : st.ParamMap): # Example of a reaction (printing something) st.OnScreenLogMessage("LTV1: Stop command complete.", "MM Surface Movement", st.Severity.Info) # If this command is part of a taskgraph, also run the following to mark this task as completed. LTV1_TaskComplete(payload) # Registering the reaction mm.OnCommandComplete(LTV1, "Stop", Stop_Complete_LTV1) - Example Usage (with lambda):
# Reaction to a stop command def Stop_Complete(payload : st.ParamMap, en : st.Entity): # Example of a reaction (printing something) st.OnScreenLogMessage(f"{en.getName()}: Stop command complete.", "MM Surface Movement", st.Severity.Info) # If this command is part of a taskgraph, also run the following to mark this task as completed. General_TaskComplete(payload, en) # Registering the reaction mm.OnCommandComplete(LTV1, "Stop", lambda payload : Stop_Complete(payload, LTV1))
- Registers a reaction (Python function with an
-
OnCommandFail(en, command_type, reaction)- Registers a reaction (Python function with an
st.ParamMapargument) to a command failure.- A
lambdafunction can also be used as a reaction if additional inputs are needed.
- A
- Parameters:
en: The entity to register a reaction for (e.g.,LTV1).command_type: The command type (e.g.,MoveToCoord).reaction: Python function object (type the name only unless using a lambda).
- Example Usage (no lambda):
# Reaction to a stop command def Stop_Fail_LTV1(payload : st.ParamMap): # Example of a reaction (printing something) st.OnScreenLogMessage("LTV1: Stop command complete.", "MM Surface Movement", st.Severity.Info) # If this command is part of a taskgraph, also run the following to mark this task as completed. LTV1_TaskComplete(payload) # Registering the reaction mm.OnCommandFail(LTV1, "Stop", Stop_Fail_LTV1) - Example Usage (with lambda):
# Reaction to a stop command def Stop_Fail(payload : st.ParamMap, en : st.Entity): # Example of a reaction (printing something) st.OnScreenLogMessage(f"{en.getName()}: Stop command failed.", "MM Surface Movement", st.Severity.Error) # If this command is part of a taskgraph, also run the following to mark this task as completed. General_TaskFail(payload, en) # Registering the reaction mm.OnCommandFail(LTV1, "Stop", lambda payload : Stop_Fail(payload, LTV1))
- Registers a reaction (Python function with an
Checking for Communications Line of Sight
EnHasComms(en)- Checks if an entity has line of sight to any communications antenna.
- Parameters:
en: The entity to check (e.g.,LTV1).
- Example Usage:
# Check for LTV1 comms has_comms = mm.EnHasComms(LTV1) - An equivalent function called
HasCommsis provided inEntityTelemetry
Movement Commands
-
Command_MoveToCoord(en, xy, task_id)- Moves an entity to a specified XY coordinate.
- Parameters:
en: The entity to be moved (e.g.,LTV1).xy: AnXYobject representing the target coordinate.task_id: A unique string identifier for this move command.
- Example Usage:
# Create an XY coordinate object target_xy = XY(100.0, 50.0) # Create a MoveToCoord command for LTV1 with task ID "Move1" move_command = Command_MoveToCoord(LTV1, target_xy, "Move1")
-
Command_Stop(en, task_id)- Stops an entity that is currently moving. Has no effect if the entity is not moving; always completes (doesn't fail) if the entity has comms.
- Parameters:
en: The entity to be stopped (e.g.,LTV1).task_id: A unique string identifier for this stop command.
- Example Usage:
# Create a Stop command for LTV1 with task ID "Stop1" stop_command = Command_Stop(LTV1, "Stop1")
Orientation Commands
Command_RotateToAzimuth(en, azimuth, task_id)- Rotates an entity to a specified azimuth angle.
- Parameters:
en: The entity to be rotated.azimuth: The target azimuth angle in degrees.task_id: A unique string identifier for this rotate command.
- Example Usage:
# Create a RotateToAzimuth command for LTV2 to face 90 degrees with task ID "Rotate1" rotate_command = Command_RotateToAzimuth(LTV2, 90.0, "Rotate1")
Camera Commands
-
Command_CameraPan(en, azimuth_deg, elevation_deg, task_id)- Controls the pan of a camera attached to an entity.
- Parameters:
en: The entity whose camera will be panned.azimuth_deg: The target azimuth angle in degrees.elevation_deg: The target elevation angle in degrees.task_id: A unique string identifier for this camera pan command.
- Example Usage:
# Create a CameraPan command for LTV1 with task ID "CameraPan1" camera_command = Command_CameraPan(LTV1, 45.0, 30.0, "CameraPan1")
-
Command_CaptureImage(en, exposure, task_id)- Captures an image using a camera attached to an entity.
- Parameters:
en: The entity whose camera will capture the image.exposure: The exposure value for the image capture.task_id: A unique string identifier for this image capture command.
- Example Usage:
# Create a CaptureImage command for LTV2 with task ID "Capture1" capture_command = Command_CaptureImage(LTV2, 4.0, "Capture1")
Antenna Commands
-
Command_PickUpAntenna(en, task_id)- Commands an entity to pick up an antenna.
- Parameters:
en: The entity that will pick up the antenna.task_id: A unique string identifier for this command.
- Example Usage:
# Create a PickUpAntenna command for LTV1 with task ID "PickUp1" pick_up_command = Command_PickUpAntenna(LTV1, "PickUp1")
-
Command_PlaceDownAntenna(en, task_id)- Commands an entity to place down an antenna.
- Parameters:
en: The entity that will place down the antenna.task_id: A unique string identifier for this command.
- Example Usage:
# Create a PlaceDownAntenna command for LTV1 with task ID "PlaceDown1" place_down_command = Command_PlaceDownAntenna(LTV1, "PlaceDown1")
Image Processing
This section describes how to process images captured by an entity's camera.
The example code below shows the ProcessImage function, which converts the raw image data to an RGB image for display or further analysis.
# Camera image processing
img_rgb = None
image_changed = False
def ProcessImage(image : st.CapturedImage):
global img_rgb
global image_changed
resx = image.properties.ResolutionX
resy = image.properties.ResolutionY
img_r = np.array(image.PixelsR, dtype=np.uint8).reshape((resy, resx))
img_g = np.array(image.PixelsG, dtype=np.uint8).reshape((resy, resx))
img_b = np.array(image.PixelsB, dtype=np.uint8).reshape((resy, resx))
# Stack channels into a single 3D array (height x width x 3)
img_rgb = np.stack((img_r, img_g, img_b), axis=-1)
image_changed = True
# Example Usage:
# Assuming 'payload' is a st.ParamMap object received from a CameraCapture command completion
image = st.CapturedImage()
image.PixelsR = payload.GetParamArray(st.VarType.uint8, "PixelsR")
image.PixelsG = payload.GetParamArray(st.VarType.uint8, "PixelsG")
image.PixelsB = payload.GetParamArray(st.VarType.uint8, "PixelsB")
image.properties.ResolutionX = payload.GetParam(st.VarType.int32, "ResolutionX")
image.properties.ResolutionY = payload.GetParam(st.VarType.int32, "ResolutionY")
image.properties.EV = payload.GetParam(st.VarType.double, "Exposure")
image.properties.FOV = payload.GetParam(st.VarType.double, "FOV")
ProcessImage(image)
Entity Telemetry
The EntityTelemetry module provides functions to access telemetry data for entities in the simulation. This data includes position, orientation, sensor readings, and communication status.
Example Usage:
import API.EntityTelemetry as ET
# Get the current XY position of LTV1
current_xy, has_comms = ET.GetCurrentXY(LTV1)
# Get lidar obstacle data from LTV1
rel_vecs, radii, had_comms = ET.GetLidarObstacles(LTV1)
st.logger_info("Lidar info: " + str(rel_vecs) + ", " + str(radii) + ", " + str(had_comms))
Entity Telemetry Functions
-
LatLonAltToXY(lla)- Converts an
st.PlanetUtils.LatLonAltobject (consisting of latitude, longitude, and altitude) to local XY coordinates. - Returns: an
XYobject with propertiesXY.xandXY.y.
- Converts an
-
XYToLatLonAlt(xy)- Converts an
XYobject (consisting of local x and y location) to anst.PlanetUtils.LatLonAltobject (consisting of latitude, longitude, and altitude). - Returns: an
st.PlanetUtils.LatLonAltobject (lla) with member getter functionslla.lat(),lla.lon(), andlla.alt().
- Converts an
-
HasComms(en)- Checks if an entity has communication capabilities.
- Returns:
Trueif the entity has comms,Falseotherwise.
-
GetMovementState(en)- Retrieves the movement state of an entity.
- Returns: A tuple containing:
- The movement state (e.g., "Moving", "Stopped").
- A boolean indicating comms status.
- Docstring:
''' Get the movement state of the entity. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. Movement states: Loiter - The entity is stationary. LoiterUntil - (unused) The entity is stationary until a certain time. Ride - (unused) The entity is embarked on another entity. StopRide - (unused) The Entity is disembarking from another entity. Startup - (unused) The entity is starting up. Moving - The entity is moving to a destination. Reversing - The entity is moving in reverse to a destination. Cooldown - (unused) The entity is shutting down. '''
-
IsMoving(en)- Checks if an entity is currently in motion.
- Returns: A tuple containing:
- A boolean indicating if the entity is moving.
- A boolean indicating comms status.
- Docstring:
''' Get whether the entity is currently moving. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetCurrentXY(en)- Gets the current XY coordinates of an entity.
- Returns: A tuple containing:
- An
XYobject representing the coordinates. - A boolean indicating comms status.
- An
- Docstring:
''' Get the current XY coordinate of the entity. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetMoveToXY(en)- Gets the target XY coordinates to which an entity is moving.
- Returns: A tuple containing:
- An
XYobject representing the target coordinates. - A boolean indicating comms status.
- An
- Docstring:
''' Get the current move-to waypoint XY coordinate of the entity. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetAzimuth(en)- Gets the current azimuth angle of an entity.
- Returns: A tuple containing:
- The azimuth angle in degrees.
- A boolean indicating comms status.
- Docstring:
''' Get the current azimuth of the entity. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetLidarObstacles(en)- Retrieves lidar sensor data about obstacles around an entity.
- Returns: A tuple containing:
- A list of relative vectors to obstacles.
- A list of their radii.
- A boolean indicating comms status.
- Docstring:
''' Returns a list of relative vectors to obstacles and their radii. 3rd element of the returned tuple is whether the entity has comms and thus whether the returned values are valid. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetTargetScanStatus(en)- Checks if the target has been detected by the scanner.
- Returns: A tuple containing:
- A boolean indicating target detection.
- The target's location as an
XYobject. - A boolean indicating comms status.
- Docstring:
''' Returns whether a target has been found and the location of the found target. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetStateOfCharge(en)- Returns the 0-1 fraction of charge of the entity's battery.
- Returns: A tuple containing:
- The state of charge.
- A boolean indicating comms status.
- Docstring:
''' Returns the 0-1 fraction of charge of the entity's battery. Also returns whether the entity had comms. If the entity does not have comms, the function will return a default value. '''
-
GetChargingStationXY()- Returns the XY coordinate of the charging station.
- Returns: An
XYobject representing the charging station coordinates. - Docstring:
''' Returns the XY coordinate of the charging station. Doesn't require comms. '''
-
GetAntennaXY(index)- Returns the XY coordinate of the appropriate antenna.
- Returns: An
XYobject representing the antenna coordinates. - Docstring:
''' Returns the XY coordinate of the appropriate antenna. Index is 1->3. Doesn't require comms. '''
Slope vs. Power Curves
Each entity utilizes a variable amount of power based on the slope angle it sees. Please reference the plots below for each entity:
LTV
Scout
Sampling Rover
Truck Rover
Excavator Rover
Slope vs. Speed Curves
Each entity's speed is limited based on the slope angle it encounters. Please reference the plots below for each entity:
LTV
Scout
Sampling Rover
Truck Rover
Excavator Rover
Task Graph
The TaskGraph module provides a way to define and manage a sequence of tasks with dependencies. Each task represents an action to be performed by an entity, such as moving to a location or capturing an image. The task graph ensures that tasks are executed in the correct order and that dependencies between tasks are met.
Example Usage:
import TaskGraph as TG
# Create a task graph
task_graph = TG.TaskGraph()
# Define tasks
task1 = TG.Task("Task1", Command_MoveToCoord(LTV1, XY(10, 20), "Task1"))
task2 = TG.Task("Task2", Command_RotateToAzimuth(LTV1, 90, "Task2"))
# Add tasks to the graph with dependencies
task_graph.add_task(task1, []) # No dependencies for the first task
task_graph.add_task(task2, ["Task1"]) # task2 depends on task1
# ... (rest of the script)