This repository contains a ROS 2 based multi-agent bidding system for dynamic task reassignment. The project implements a distributed bidding agent architecture where heterogeneous drones can bid on tasks (including combo bids for task sequences) and coordinate task allocation through a decentralized auction mechanism.
The system consists of:
- Bidder Agent: Core ROS 2 node that manages bidding logic, task processing, fault handling, precondition enforcement, and communication
- Bid Calculator: Computes bid values for single tasks, task sequence permutations, and insertion-based marginal-cost bids. Supports precondition-aware slot constraints when inserting new tasks into existing schedules
- Assignment Solver: Exhaustive backtracking solver that finds the optimal set of non-overlapping bids maximizing task coverage (primary) and minimizing makespan (secondary), with precondition constraint validation
- Bid Storage Manager: In-memory storage (using
std::unordered_map) for bid entries, task details, and assignment tracking - Path Planner: Polymorphic path planning with support for different drone types (Multirotor, Fixed-Wing) via
PathPlannerFactory. Integrates environment data (mission area, no-fly zones) - Simulator: Time-stepped movement simulation with trapezoidal velocity profiles and remaining operation time countdown
- Drone Status: Tracks drone state, position, speed, remaining operation time, capabilities, current task assignments, and scheduled position (effective start position for bid cost estimation)
- Config Loader: YAML-based configuration system for heterogeneous drone fleets
- Publisher / Subscriber: ROS 2 pub/sub layer handling bids, tasks, drone status, simulation ticks, fault injection, initial assignments, environment data, and simulation restarts
- Custom ROS 2 Messages: Task, Bid, DroneStatus, Environment, FaultInjection, RestartSim, SimTick, InitAssignment, RequestAssignment
The system supports heterogeneous drone swarms with different capabilities. Drone configurations are defined in config/drone_config.yaml:
drone_configs:
- config_id: "multirotor_big"
type: "multi_rotor"
max_operation_time_sec: 5400.0
min_remaining_operation_time_after_bid_sec: 1800.0
critical_remaining_operation_time_sec: 600.0
movement_speed_mps: 10.0
acceleration_mps2: 2.0
capabilities: ["gps", "camera", "thermal"]
- config_id: "multirotor_small"
type: "multi_rotor"
max_operation_time_sec: 5400.0
min_remaining_operation_time_after_bid_sec: 1800.0
critical_remaining_operation_time_sec: 600.0
movement_speed_mps: 10.0
acceleration_mps2: 3.0
capabilities: ["gps", "camera"]
- config_id: "fixed_wing"
type: "fixed_wing"
max_operation_time_sec: 9000.0
min_remaining_operation_time_after_bid_sec: 1800.0
critical_remaining_operation_time_sec: 600.0
movement_speed_mps: 15.0
acceleration_mps2: 1.0
capabilities: ["gps", "camera"]- multi_rotor: Can hover, fly direct paths, suitable for precision tasks
- fixed_wing: Higher speed and efficiency, but cannot hover (turning radius constraints, Dubins-like path estimation)
- vtol: Hybrid capabilities (currently uses multirotor path planner)
| Parameter | Description |
|---|---|
config_id |
Unique identifier for this drone configuration |
type |
Drone type: multi_rotor, fixed_wing, or vtol |
max_operation_time_sec |
Maximum operation time budget in seconds |
min_remaining_operation_time_after_bid_sec |
Minimum operation time that must remain after a bid is accepted (reserve buffer) |
critical_remaining_operation_time_sec |
Operation time threshold below which the drone is considered critically low |
movement_speed_mps |
Cruise speed in meters per second |
acceleration_mps2 |
Acceleration in meters per second squared |
capabilities |
List of capabilities: gps, camera, thermal |
- Operating System: Linux (Ubuntu 22.04 recommended)
- ROS 2: Humble Hawksbill or compatible version
- CMake: Version 3.16 or higher
- C++ Compiler: C++17 compatible (GCC 9+ or Clang 10+)
# Core ROS 2 packages
sudo apt install ros-humble-rclcpp
sudo apt install ros-humble-std-msgs
sudo apt install ros-humble-geometry-msgs
sudo apt install ros-humble-unique-identifier-msgs
sudo apt install ros-humble-rosidl-default-generators# fmt library for string formatting
sudo apt install libfmt-dev
# Boost libraries (filesystem, program_options, system, uuid)
sudo apt install libboost-all-dev
# yaml-cpp for configuration loading
sudo apt install libyaml-cpp-dev# Ensure Python 3 and pip are installed
sudo apt install python3 python3-pip
# ROS 2 Python client library (usually installed with ROS 2)
sudo apt install ros-humble-rclpygit config blame.ignoreRevsFile .git-blame-ignore-revs# Install system dependencies
sudo apt update
sudo apt install -y \
cmake \
build-essential \
libfmt-dev \
libboost-all-dev \
libyaml-cpp-dev
# Install ROS 2 dependencies (assuming ROS 2 Humble is already installed)
sudo apt install -y \
ros-humble-rclcpp \
ros-humble-std-msgs \
ros-humble-geometry-msgs \
ros-humble-unique-identifier-msgs \
ros-humble-rosidl-default-generators \
ros-humble-rclpysource /opt/ros/humble/setup.bash# From project root directory
mkdir -p build
cd build
cmake ..
makeThe simulation clock must be running for drones to process ticks and move. Start it before launching agents:
# Default: 10 Hz tick rate, 1x real-time speed
python3 tools/sim_clock.py
# Custom tick rate and speed
python3 tools/sim_clock.py --rate 20 --speed 2.0
# Non-interactive mode (for scripts/background use)
python3 tools/sim_clock.py --no-interactiveInteractive controls (while running):
| Key | Action |
|---|---|
p |
Pause / Resume simulation |
s |
Step one tick (while paused) |
+ |
Increase speed by 0.5× |
- |
Decrease speed by 0.5× |
r |
Reset simulation time to 0 |
q |
Quit |
# Source ROS 2 environment
source /opt/ros/humble/setup.bash
# Run from build directory with default configuration (multirotor_small)
cd build
./drone_agent
# Run with a specific drone configuration
./drone_agent --ros-args -p drone_config_id:=multirotor_big -p config_file:=drone_config.yamlUse the provided launch script to start multiple agents with different configurations:
# Make the script executable (first time only)
chmod +x launch/launch.sh
# Start all configured drones (runs in background)
./launch/launch.sh
# Check status of running agents
./launch/launch.sh status
# Stop all running agents
./launch/launch.sh stopThe launch script reads drone configurations from config/drone_config.yaml and spawns one agent per configuration. Edit the DRONE_CONFIGS array in the script to change which drones are launched. Log files are written to /tmp/drone_agent_pids/.
| Parameter | Type | Default | Description |
|---|---|---|---|
drone_config_id |
string | "multirotor_small" |
ID of drone config to load from YAML |
config_file |
string | "drone_config.yaml" |
Name of the configuration file |
Python scripts are provided in the tools/ directory for testing the system.
# Publish a single task with defaults
python3 tools/publish_task.py
# Publish with custom values
python3 tools/publish_task.py --type go_to --goal-x 50 --goal-y 100 --goal-z 15
# Publish multiple tasks (goal coordinates are offset per task)
python3 tools/publish_task.py --count 5
# Custom capabilities and state
python3 tools/publish_task.py --capabilities camera gps --state pending# Publish a single random bid
python3 tools/publish_bid.py
# Publish with specific values
python3 tools/publish_bid.py --value 100.0
# Publish multiple bids
python3 tools/publish_bid.py --count 5
# Publish a combo bid for multiple tasks (sequence)
python3 tools/publish_bid.py --task-ids "UUID1" "UUID2"# Start the simulation clock (required for drone movement)
python3 tools/sim_clock.py
# Custom settings
python3 tools/sim_clock.py --rate 20 --speed 2.0unique_identifier_msgs/UUID task_id
string task_type
geometry_msgs/Vector3 goal_location
string[] capability_requirements
string current_state
unique_identifier_msgs/UUID[] precondition_task_ids
unique_identifier_msgs/UUID route_predecessor_task_id
# Supports single-task and combo (multi-task sequence) bids
unique_identifier_msgs/UUID agent_id
unique_identifier_msgs/UUID[] task_ids
float64[] seconds_to_complete # Estimated completion time for each sub-combo
float64 bid_value
int32 total_bids_from_agent # Used for early assignment trigger
unique_identifier_msgs/UUID drone_id
geometry_msgs/Vector3 current_location
float32 average_speed
float32 current_speed
string drone_type
string current_state
string[] capabilities
unique_identifier_msgs/UUID current_task_id
unique_identifier_msgs/UUID[] task_backlog
int32 battery_level
float64 sim_time # Total elapsed simulation time in seconds
float64 delta_time # Time since last tick in seconds
uint64 tick_count # Monotonic tick counter
float64 time_scale # Simulation speed multiplier (1.0 = real-time)
unique_identifier_msgs/UUID drone_id
Task[] tasks
bool make_assignment
int32 num_tasks
uint64 rng_key
bool use_capabilities
bool use_dependencies
string environment_type # Mission Area, No-Fly-Zone (Polygon), No-Fly-Zone (Circle)
geometry_msgs/Vector3[] vectors # Mission Area: 2 vectors (min/max), Polygon: N vertices, Circle: 1 center
float64 radius # For No-Fly-Zone (Circle)
unique_identifier_msgs/UUID drone_id
bool fault
bool restart_simulation
| Topic | Message Type | Direction | Description |
|---|---|---|---|
/tasks |
thesis_project/msg/Task |
Pub & Sub | Task announcements and state updates (pending, unplanned, assigned, in_progress, completed, failed) |
/bids |
thesis_project/msg/Bid |
Pub & Sub | Agent bids on tasks (single-task and combo bids) |
/drone_status |
thesis_project/msg/DroneStatus |
Pub & Sub | Drone status broadcast (1 Hz); subscribed to track active drones for early assignment trigger |
/sim_tick |
thesis_project/msg/SimTick |
Sub | Simulation clock ticks driving movement and task processing |
/init_assignment |
thesis_project/msg/InitAssignment |
Sub | Initial task assignments at simulation start |
/environment |
thesis_project/msg/Environment |
Sub | Mission area and no-fly zone definitions |
/fault_injection |
thesis_project/msg/FaultInjection |
Sub | Inject drone failures for resilience testing |
/restart_sim |
thesis_project/msg/RestartSim |
Sub | Reinitialize all agents and clear state |
├── CMakeLists.txt # Main CMake configuration
├── package.xml # ROS 2 package manifest
├── cmake_settings.cmake # Build type and compile_commands.json settings
├── depends.cmake # Dependency configuration (Boost, ROS 2, fmt, yaml-cpp)
├── sources.cmake # Source file definitions
├── compile_commands.json # Compilation database (symlink to build/)
├── .clang-format # Code formatting configuration
├── .git-blame-ignore-revs # Git blame ignore list for formatting commits
├── .gitignore # Git ignore rules
├── config/ # Configuration files
│ └── drone_config.yaml # Drone fleet definitions
├── launch/ # Launch scripts
│ └── launch.sh # Shell script to launch/stop/status drone swarm
├── msg/ # Custom ROS 2 message definitions
│ ├── Task.msg
│ ├── Bid.msg
│ ├── DroneStatus.msg
│ ├── Environment.msg
│ ├── FaultInjection.msg
│ ├── InitAssignment.msg
│ ├── RequestAssignment.msg
│ ├── RestartSim.msg
│ └── SimTick.msg
├── src/ # C++ source files
│ ├── main.cpp # Entry point, ROS 2 parameter handling, config file discovery
│ ├── bidder_agent.cpp/hpp # Core agent node (task processing, fault handling, precondition enforcement, assignment)
│ ├── assignment_solver.cpp/hpp # Exhaustive backtracking solver for task-to-drone assignment with precondition validation
│ ├── bid_calculator.cpp/hpp # Bid computation for single tasks, task permutations, and insertion-based marginal-cost bids
│ ├── bid_storage_manager.cpp/hpp # In-memory bid/task storage and assignment tracking
│ ├── drone_status.cpp/hpp # Drone state, position, battery, capabilities, scheduled position management
│ ├── config_loader.cpp/hpp # YAML drone configuration loader
│ ├── path_planner.cpp/hpp # Polymorphic path planners (Multirotor, FixedWing) + factory
│ ├── publisher.cpp/hpp # ROS 2 publishers (drone status, bids, task state changes)
│ ├── subscriber.cpp/hpp # ROS 2 subscribers (bids, tasks, drone status, sim ticks, environment, faults, init assignments, restarts)
│ ├── simulator.cpp/hpp # Movement simulation with trapezoidal velocity profiles + factory
│ ├── logger.hpp # Thread-safe colored logging with file output support
│ └── utils.hpp # Enums (TaskState, DroneState, DroneType, Capability, TaskType), structs (Point, Task, Bid, SimTick, MissionArea, NoFlyZonePoly, NoFlyZoneCirc), helper functions
├── tools/ # Python testing tools
│ ├── publish_task.py # Publish demo tasks to /tasks
│ ├── publish_bid.py # Publish demo bids to /bids
│ ├── sim_clock.py # Interactive simulation clock publisher
│ ├── cpp_template/ # C++ template files
│ └── network_monitor_app/ # Network monitoring application
└── build/ # Build output directory
The system implements a decentralized auction-based task allocation:
- Task Reception: When tasks arrive on
/tasks, each agent calculates bids for all permutations of the task batch (single tasks and sequences up to a configurable max combo size). If the drone already has assigned tasks, insertion-based bidding is used to compute marginal costs of adding new tasks into the existing schedule - Bid Calculation: The
BidCalculatoruses the drone'sPathPlannerto estimate completion time for each task sequence from the drone's effective start position (last scheduled task's destination, or current GPS position if idle). Drones missing required capabilities produce infinite-cost bids. Precondition constraints are respected when generating permutations and determining valid insertion slots - Bid Exchange: Bids are published on
/bids(including atotal_bids_from_agentcount) and all agents store received bids in their localBidStorageManager - Assignment Solving: An early trigger fires when all active drones (tracked via
/drone_statussubscriptions) have reported their bid counts and all expected bids have been received. A 20-second timeout acts as a safety fallback. TheAssignmentSolveruses exhaustive backtracking search that maximizes task coverage (primary) and minimizes makespan (secondary), with pruning optimizations and precondition constraint validation - Task Execution: Assigned tasks are executed sequentially, respecting precondition ordering (tasks whose preconditions are not yet completed are deferred). The
Simulatormoves the drone toward each goal location using trapezoidal velocity profiles
The system uses polymorphic path planners to support different drone types:
- PathPlanner (base class): Euclidean distance-based time and energy estimation
- MultirotorPathPlanner: Direct-path planning for multirotors (can hover, fly straight lines)
-
FixedWingPathPlanner: Accounts for turning radius constraints using Dubins-like path estimation (
$R = v^2 / (g \cdot \tan(\theta))$ with 45° max bank angle)
The appropriate planner is automatically selected based on drone type via PathPlannerFactory.
The system uses a centralized simulation clock (sim_clock.py) that publishes SimTick messages. On each tick:
- The
Simulatorupdates drone position using a trapezoidal velocity profile (acceleration → cruise → deceleration) - Remaining operation time is decremented by incoming simulation tick duration
- Task completion is detected when the drone reaches the goal location
Note: Drone-type-specific simulators (
MultirotorSimulator,FixedWingSimulator) andSimulatorFactoryexist in the codebase but are not currently used at runtime; all agents use the baseSimulatorclass.
- Fault Injection: A
/fault_injectionmessage marks a specific drone asFAILED. All other agents then:- Identify the failed drone's uncompleted tasks
- Find tasks on any drone (including themselves) whose preconditions depend on the failed drone's uncompleted tasks
- Remove affected dependent tasks from their own assigned list if present
- Combine the failed drone's tasks and all dependent tasks into a single replan batch
- If the drone already has an existing schedule, use insertion-based bidding (marginal cost) instead of full rebidding
- Simulation Restart: A
/restart_simmessage reinitializes all agents, clearing task and bid state
Tasks can declare precondition dependencies via the precondition_task_ids field in the Task message. This enables ordered task chains (e.g., task B requires task A to be completed first):
- Bid Generation: The
BidCalculatoronly generates permutations that respect precondition ordering - Assignment Solving: The
AssignmentSolvervalidates that all precondition tasks are assigned (to any drone) before accepting a solution that includes a dependent task - Task Execution: The
BidderAgentchecksarePreconditionsMet()before starting each task; blocked tasks are deferred until their preconditions are globally completed - Fault Recovery: When a drone fails, tasks on other drones whose preconditions depended on the failed drone's work are also replanned
Tasks can declare a strict route predecessor via route_predecessor_task_id.
If set for task B, then task A=route_predecessor_task_id and B must be:
- assigned to the same drone,
- executed in direct order A -> B,
- with no task between them.
Enforcement points:
- Bid Generation: route-successor tasks are only generated directly after their predecessor
- Insertion Bidding: insertions cannot split an existing route predecessor/successor pair
- Assignment Solving: solutions violating same-drone or direct adjacency are rejected
- Task Execution: if predecessor
Ahas just completed, successorBis forced as the next task
When a drone already has assigned tasks and new tasks arrive (e.g., during fault recovery), the system uses insertion-based bidding rather than full rebidding:
BidCalculator::calculateBidsWithInsertion()finds the optimal insertion points for new tasks into the existing scheduleBidCalculator::findBestInsertion()uses a greedy cheapest-insertion heuristic with precondition-aware slot constraints- Already in-progress tasks are locked and cannot be reordered
- The bid value represents the marginal cost of adding the new tasks
The system supports environment definitions received via the /environment topic:
- Mission Area (
MissionAreastruct): Bounding box defined by min/max corners constraining drone movement - No-Fly Zones - Polygon (
NoFlyZonePolystruct): Polygonal restricted areas defined by vertex lists - No-Fly Zones - Circle (
NoFlyZoneCircstruct): Circular restricted areas defined by center point and radius
Environment data flows from the subscriber through the BidderAgent to the BidCalculator and PathPlanner for future path cost integration.
- Add a new entry to
config/drone_config.yaml - Add the
config_idto theDRONE_CONFIGSarray inlaunch/launch.sh - Rebuild is not required for config changes
This README was updated on June 9th, 2026.