SpaceTry is an infrastructure to train space rovers autonomy. This repository has a course-grade Mars mission demo pack for Space ROS + Behavior Trees + Autonomy Evaluation Scenario Generation Skill for LLM Agents.
Contents:
- Project Layout β Repository structure and ROS2 packages
- Dependencies & Integrations β Integration with Space-ROS, Curiosity Rover, and BehaviorTree.CPP
- Quickstart β Quick validation: build Docker image and run smoke tests
- Step-by-Step: Run SpaceTry π₯ β Complete walkthrough from container startup to rover control
- Mission configuration β Mission objectives, waypoints, and validation
- Test Scenarios β Framework for autonomy evaluation and self-adaptation testing
spacetry/
βββ AGENTS.md - LLM agents instructions and rules
βββ README.md - project overview and usage
βββ REF_SCENARIO.md - autonomy behavior description for the reference scenario
βββ deps/ - repository configuration for dependency management
βββ docker/
βββ scripts/ - helper scripts to build, execution, and validation
βββ skills/
β βββ spacetry-autonomy-scenario-driver/ - autonomy testing scenario LLM agent skill
βββ src/ - ROS 2 packages & Gazebo assets
β βββ spacetry_battery/ - Battery manager node
β βββ spacetry_bringup/ - Rover launch configurations
β βββ spacetry_bt/ - Behavior tree runner node (C++)
β βββ spacetry_mission/ - Mission description and configuration files
β βββ spacetry_models/ - Gazebo models (target rocks, obstacles, outpost)
β βββ spacetry_monitors/ - Safety properties monitoring node
β βββ spacetry_perception/ - Perception nodes
β βββ spacetry_world/ - Gazebo world configurations
βββ docs/ - Project structure and implementation details
βββ logs/ - Scenario execution and test run logs
The full project implementation and structure details can be found in IMPLEMENTATION.md.
SpaceTry π₯ is designed to run without installing ROS locally. Everything runs inside the SpaceTry π₯ Docker image on linux.
It integrates with i) The Curiosity Rover from Space-ROS Demos and ii) BehaviorTree.CPP which trees can be used in Groot2.
NASA Curiosity Rover and World from Space-ROS
The Curiosity rover model comes from the Space ROS Demos repository and is imported via vcs using deps/spacetry.repos. The import brings three ROS 2 packages:
| Package | Role |
|---|---|
curiosity_description |
URDF/xacro model of the rover (meshes, joints, sensors, ros2_control config) |
curiosity_gazebo |
Gazebo models (terrain, paths), plugins, and the odom_tf_publisher helper node |
curiosity_rover_demo |
Demo nodes for arm, mast, wheel control and the mars_rover.launch.py integration launch |
At launch, spacetry_bringup processes the xacro (curiosity_mars_rover.xacro) into a URDF XML and spawns the rover into the mars_outpost world via ros_gz_sim create. After spawn, the launch file loads a controller chain through ros2_control:
GazeboSystemhardware interface β activejoint_state_broadcasterβ active- In parallel:
arm_joint_trajectory_controller,mast_joint_trajectory_controller,wheel_velocity_controller,steer_position_controller,wheel_tree_position_controller
BehaviorTree.CPP and Groot2 Integration
SpaceTry π₯ uses BehaviorTree.CPP (v4.6+) for mission logic and supports visual editing and monitoring via Groot2.
Core Integration:
The spacetry_bt_runner executable loads a BehaviorTree from an XML file and executes it with configurable tick rate (default 10 Hz). The runner:
- Loads XML tree from
tree_fileparameter - Registers custom ROS 2 action and condition nodes for rover control
- Ticks the tree at configurable rate (via
tick_hzparameter) - Publishes ongoing execution status to stdout logs
- Supports max runtime limit via
max_runtime_sparameter - Reads waypoints and mission parameters from ROS 2 parameter server
Custom BT Nodes:
The runner provides mission-specific nodes registered with the BehaviorTree factory:
| Node | Type | Inputs | Purpose |
|---|---|---|---|
SetGoal |
SyncAction | waypoint (string) |
Load a waypoint into the blackboard as current goal |
NavigateWithAvoidance |
StatefulAction | goal, v_lin, min_lin, v_ang, dist_tol, obstacle_threshold_m |
Navigate towards goal while avoiding LiDAR obstacles |
AlignToGoal |
StatefulAction | goal, v_lin, min_lin, v_ang, yaw_tol_deg |
Align rover orientation to goal heading |
StopAndObserve |
StatefulAction | seconds |
Stop movement and pause (for sampling/observation) |
LogMessage |
SyncAction | message (string) |
Log a status message to console |
Groot2 Workflow:
Trees are defined in standard BehaviorTree.CPP XML format (BTCPP_format="4"):
- Edit/visualize trees in Groot2 (available at https://www.behaviortree.dev/groot/)
- Export to XML and place in
/src/spacetry_bt/trees/ - Run via
spacetry_bt_runnerwith--ros-args -p tree_file:=<path-to-xml> - Monitor execution in real-time via stdout logs and ROS 2 topics
Example Groot2 Export:
Groot2 can monitor and edit trees during runtime if the BT is launched with the JSON logger enabled. For offline editing, export the tree as XML, which is automatically discovered by colcon's install step.
Integration Launch
The single launch file (spacetry_curiosity_outpost.launch.py) in the package spacetry_bringup orchestrates the full mission stack:
- Launches the
mars_outpostworld (GUI or headless viaheadlessarg) - Publishes
robot_descriptionviarobot_state_publisher - Spawns the Curiosity rover at the pose defined by the
spawn_waypointarg (default:dock_pad_01fromwaypoints.yaml, falls back to origin if missing) - Starts ROSβGazebo bridges (
/clock,/scan,/odometry,/image_raw) - Loads the ros2_control controller chain (joint state broadcaster β arm/mast/wheel/steer/suspension controllers)
- Launches demo nodes for arm/mast/wheel services
- Starts
battery_manager(initial SOC configurable viabatteryarg) - Starts
obstacle_direction_node(LiDAR obstacle classification)
Please follow the instruction here to install Docker.
Build and run the smoke test (builds the docker image and the workspace, loads the world headless, validates configs).
From the repo root:
./scripts/build.sh && docker run --rm --platform linux/amd64 spacetry:dev /ws/scripts/smoke_test.shYou should see:
OK: mars_outpost loaded headlessOK: mission config validated
1. Build the SpaceTry π₯ Docker image
From the repo root:
./scripts/build.sh2. Start the container and enter bash
From the repo root:
./scripts/run.shEnter the running container:
docker exec -it docker-spacetry-1 bash 3. Build the workspace
Inside the container, run:
source /opt/ros/spaceros/setup.bash && source /etc/profile && colcon build --merge-install --event-handlers console_direct+4. Launch SpaceTry Rover in Simulation with Autonomous Mission π₯
SpaceTry π₯ bringup launches the mars_outpost world, spawns the Curiosity rover (with proper simulation clock synchronization), starts ROSβGazebo bridges, loads the ros2_control controller chain, and automatically starts the behavior tree mission runner.
Option A: Run from inside the running container
From inside the container (see step 2.), simply launch the rover:
source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.pyThe launch will:
- Start Gazebo with mars_outpost world
- Publish ROSβGazebo bridges (including
/clockfor simulation time) - Spawn Curiosity rover (with 2-second delay to ensure clock is available)
- Load ros2_control controller chain
- Automatically start the behavior tree runner to execute the mission
To use a different BT tree file:
source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py tree_file:=$(ros2 pkg prefix --share spacetry_bt)/trees/my_custom_tree.xmlOption B: Run from a new terminal window
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py battery:=0.75'To specify a custom BT tree:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py battery:=0.75 tree_file:=$(ros2 pkg prefix --share spacetry_bt)/trees/my_custom_tree.xml'Option C: Run in Headless Mode (no Gazebo GUI)
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /etc/profile && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py battery:=0.5 headless:=1'To specify a custom BT tree:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /etc/profile && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py battery:=0.5 headless:=1 tree_file:=$(ros2 pkg prefix --share spacetry_bt)/trees/my_custom_tree.xml'Option D: Launch without the Behavior Tree
To launch the rover without the BT runner (for manual testing or debugging):
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py enable_bt_runner:=false'This starts the full simulation stack without the autonomous mission runner. Useful for:
- Manual rover control via command-line or external controllers
- Testing individual components (perception, battery manager, etc.)
- Debugging without BT execution overhead
Option E: Run the Behavior Tree separately on a new terminal
First, launch the rover without the BT runner:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 launch spacetry_bringup spacetry_curiosity_outpost.launch.py enable_bt_runner:=false'Then in another terminal, run the behavior tree runner:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && source /ws/install/setup.bash && ros2 run spacetry_bt spacetry_bt_runner --ros-args -p tree_file:=$(ros2 pkg prefix --share spacetry_bt)/trees/base_bt.xml --params-file /ws/src/spacetry_bt/bt_params.yaml'This allows you to start the BT independently, giving you fine-grained control over when the mission execution begins.
Option F: Verify the rover and BT are running
Check if Curiosity nodes are active:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && ros2 node list | grep curiosity'Verify the behavior tree runner is executing:
docker exec -it docker-spacetry-1 bash -lc 'source /opt/ros/spaceros/setup.bash && ros2 node list | grep spacetry_bt_runner'You should see:
- Gazebo window with mars_outpost scene and rover spawned near
dock_pad_01 - Console output from rover nodes and BT runner showing mission execution (if BT enabled)
- Use
battery:=0.5to set initial battery state (example: 50%) - Use
enable_bt_runner:=falseto launch without the behavior tree - Use
tree_file:=$(ros2 pkg prefix --share spacetry_bt)/trees/my_tree.xmlto specify a custom BT tree - The BT executes the mission from base_bt.xml automatically (when enabled)
5. Stop SpaceTry π₯
After closing Gazebo GUI, exit all the containers bash with:
exitAnd then from the repo root:
docker compose -f docker/docker-compose.yaml downMission configuration files live in:
src/spacetry_mission/config/objects.yamlβ mission objects (IDs must match model instance names in the world)src/spacetry_mission/config/waypoints.yamlβ named navigation waypoints (frame: world)
The validator checks that IDs in objects.yaml exist in the installed world SDF. Run (from the repo root):
docker run --rm --platform linux/amd64 -v "$(pwd)":/ws -w /ws spacetry:dev bash -lc '
set -e
source /opt/ros/spaceros/setup.bash
colcon build
source install/setup.bash
export SPACETRY_WORLD_SDF="$(ros2 pkg prefix spacetry_world)/share/spacetry_world/worlds/mars_outpost.sdf"
export SPACETRY_MISSION_CONFIG_DIR="$(ros2 pkg prefix spacetry_mission)/share/spacetry_mission/config"
python3 /ws/scripts/validate_mission_config.py
'
More details on the mission goals, configurations, and launch can be found in MISSION.md.
SpaceTry π₯ includes an LLM-Agent Skill for generating and running test scenarios that evaluate the rover's autonomous capabilities and self-adaptation to faults or changing/unforeseen conditions.
Test scenarios inject uncertainty into the rover's mission execution to evaluate:
- Perceptual Adaptation β Can the rover adapt when sensors degrade or fail?
- Behavioral Flexibility β Does the behavior tree transition to contingencies appropriately?
- Resource-Aware Autonomy β Can the rover adapt goals to energy constraints?
- Obstacle Intelligence β Can the rover discover and avoid dynamic obstacles?
- Mission Resilience β Can the rover recover from failures or replanning challenges?
- Safety Under Autonomy β Does self-adaptation maintain safety constraints?
For a deeper understanding of the test scenario process and how to create custom scenarios, see the AGENTS.md section on Autonomy Test Scenario Generation and Evaluation Workflow and the Scenario Generation Prompt Template.
The workflow consists of:
| Step | Task | Description | Workflow |
|---|---|---|---|
| 1 | Scenario Prompt Template | Specify the autonomy under text and mission context and objectives using the prompt template. | User Input |
| 2 | Scenario Driver Generation | Use the LLM agent custom Skill to generate and parametrize the evaluation scenario from the prompt. | Authoring |
| 3 | Scenario Driver Parametrization | Configure and fine-tune scenario parameters before execution. | Authoring |
| 4 | Scenario Driver Execution | Execute the generated scenario in the simulation environment. | Authoring |
| 5 | Autonomy Evaluation Report | Analyze the output report from the agent to assess the rover's self-adaptation capabilities in the uncertainty test scenario. | User Input |
A quick reference guide with examples is available at SCENARIO_PROMPT_QUICK_REF.md.