Files
Ramon Roche 63cec47786 feat(simulation): add ROS 2 swarm controller package
ROS 2 package for coordinating PX4 SIH multi-drone swarm missions via
XRCE-DDS. Includes:

- swarm_controller.py: orchestrates N drones through 6 phases
  (EV lock, arm/takeoff, grid hold, circle transition, circle hold,
  kamikaze dive) with DDS cross-talk filtering for multi-instance
  XRCE-DDS setups
- vision_provider.py: publishes noisy VehicleOdometry from ground
  truth for EKF2 external vision fusion (placeholder for non-GPS)
- swarm_launch.py: ROS 2 launch file for N vision providers + controller
- run_swarm.sh: Docker entrypoint that launches PX4 instances,
  XRCE-DDS agent, and ROS 2 nodes
- Dockerfile + build_docker.sh: containerized build with ROS 2 Jazzy,
  px4_msgs from PX4 source, and the swarm package

Validated with 4 drones: all phases complete, 4/4 impacts, ULogs
generated per instance (~16 MB each).

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-03-16 07:44:43 -07:00

81 lines
2.4 KiB
Python

"""
Launch file for the PX4 swarm coordination stack.
Starts one vision_provider node per drone and a single swarm_controller node.
Usage:
ros2 launch swarm_ros2 swarm_launch.py num_drones:=16 speed_factor:=1.0
"""
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
# Declare launch arguments.
num_drones_arg = DeclareLaunchArgument(
'num_drones',
default_value='16',
description='Number of drones in the swarm',
)
speed_factor_arg = DeclareLaunchArgument(
'speed_factor',
default_value='1.0',
description='Simulation speed factor',
)
formation_config_arg = DeclareLaunchArgument(
'formation_config',
default_value='',
description='Path to YAML formation configuration file',
)
num_drones = LaunchConfiguration('num_drones')
speed_factor = LaunchConfiguration('speed_factor')
# We need a concrete integer to iterate, so we support a fixed max and
# conditionally launch. ROS 2 launch does not natively support dynamic
# loops over LaunchConfiguration integers, so we generate nodes for a
# reasonable upper bound and rely on the controller's num_drones param
# to limit actual usage. For simplicity, generate exactly 16 nodes
# (the default). For other counts, users can call this launch file
# programmatically or adjust the constant below.
MAX_DRONES = 16
vision_nodes = []
for i in range(MAX_DRONES):
ns = f'px4_{i}'
vision_nodes.append(
Node(
package='swarm_ros2',
executable='vision_provider',
name=f'vision_provider_{i}',
parameters=[{
'namespace': ns,
'instance_id': i,
}],
output='screen',
)
)
# Single swarm controller.
controller_node = Node(
package='swarm_ros2',
executable='swarm_controller',
name='swarm_controller',
parameters=[{
'num_drones': num_drones,
'speed_factor': speed_factor,
}],
output='screen',
)
return LaunchDescription([
num_drones_arg,
speed_factor_arg,
formation_config_arg,
*vision_nodes,
controller_node,
])