Skip to content

Commit

Permalink
Merge Release Version 2.1.1 with bug fixes and documentation improvem…
Browse files Browse the repository at this point in the history
…ents

Version 2.1.1
  • Loading branch information
nobodyczcz authored Nov 19, 2024
2 parents 5ea4fa1 + 56a930c commit 216a3c7
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 22 deletions.
9 changes: 9 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# Changelog
Version 2.1.1 - 2024-11-18
----------------------------
Fixed:
- Bug: Solution costs counted multiple times.
- Bug: When reassigning a task to an agent with a smaller id, `task->agent_assigned` will be reset to -1 instead of the smaller id agent.

Added:
- Docs explaining the anytime behaviour of the default planner.


Version 2.1.0 - 2024-11-15
----------------------------
Expand Down
11 changes: 7 additions & 4 deletions Prepare_Your_Submission.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ In `src/TaskScheduler.cpp`, you can find the default task scheduler, which calls
#### The default planner
In `src/MAPFPlanner.cpp`, you can find the default planner implementation, which calls the functions that are further defined in `default_planner/planner.cpp`. The default planner shares the same heuristic distance tables with the default scheduler. Its `initialize()` function prepares necessary data structures and a global heuristic table (if not initialized by the scheduler). Its `plan()` function computes collision-free actions for the current timestep.

The MAPF planner implemented in the default planner is a variant of Traffic Flow Optimised Guided PIBT, [Chen, Z., Harabor, D., Li, J., & Stuckey, P. J. (2024, March). Traffic flow optimisation for lifelong multi-agent path finding. In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 38, No. 18, pp. 20674-20682)](https://ojs.aaai.org/index.php/AAAI/article/view/30054/31856). The planner first optimises traffic flow assignments for each robot, then computes collision-free actions using [Priority Inheritance with Backtracking](https://www.sciencedirect.com/science/article/pii/S0004370222000923) following the optimised traffic flow. A more detailed technical report will be provided soon.

The MAPF planner implemented in the default planner is a variant of Traffic Flow Optimised Guided PIBT, [Chen, Z., Harabor, D., Li, J., & Stuckey, P. J. (2024, March). Traffic flow optimisation for lifelong multi-agent path finding. In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 38, No. 18, pp. 20674-20682)](https://ojs.aaai.org/index.php/AAAI/article/view/30054/31856). The planner first optimises traffic flow assignments for each robot, then computes collision-free actions using [Priority Inheritance with Backtracking](https://www.sciencedirect.com/science/article/pii/S0004370222000923) (PIBT) following the optimised traffic flow. A more detailed technical report will be provided soon.

> ⚠️ **NOTE** ⚠️, the default planner is an anytime algorithm, meaning the time it has to compute a solution directly impacts the quality of that solution.
When a small amount of time is allocated, the default planner behaves like vanilla PIBT. When more time is allocated, the default planner updates traffic cost and incrementally recomputes guide paths for agents to improve their arrival time.
If you are a Scheduling Track participant, remember that the sooner your `schedule_plan()` function returns, the more time is avaliable to the default planner.
Better plans and better schedules can both substantially influence the performance of your submission on the leaderboard.
How to allocate time between these two components is an important part of a successful strategy.

## What to implement for each track

Expand Down Expand Up @@ -122,8 +126,7 @@ If you compete in the combined track, you are allowed to modify the `Entry::comp

### Timing parameters for default planner and scheduler


At every timestep, we will ask your planner to compute the next valid action for each robot subject to a given `time_limit` in ms. The `time_limit` is given as an input parameter to the `compute()` function of `entry.cpp`, which is then passed to `TaskScheduler::plan()` and `MAPFPlanner::plan()`. Note that, for `TaskScheduler::plan()` and `MAPFPlanner::plan()` the start time of the current timestep begins at `env->plan_start_time`, indicating the scheduler and the planner should return actions before `env->plan_start_time` plus `time_limit` ms. This is a soft limit, which means if you do not return actions before the `time_limit` elapses, the simulator will continue, and all robots will wait in place until the next planning episode.
At every timestep, we will ask your planner to compute the next valid action for each robot subject to a given `time_limit` in ms. The `time_limit` is given as an input parameter to the `compute()` function of `Entry.cpp`, which is then passed to `TaskScheduler::plan()` and `MAPFPlanner::plan()`. Note that, for `TaskScheduler::plan()` and `MAPFPlanner::plan()` the start time of the current timestep begins at `env->plan_start_time`, indicating the scheduler and the planner should return actions before `env->plan_start_time` plus `time_limit` ms. This is a soft limit, which means if you do not return actions before the `time_limit` elapses, the simulator will continue, and all robots will wait in place until the next planning episode.

The default scheduler and default planner run in a sequential manner. The default scheduler uses `time_limit/2` as the timelimit to compute schedules.
The default planner uses the remaining time, after the scheduler returns, to compute collision-free actions.
Expand Down
8 changes: 4 additions & 4 deletions inc/TaskManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ class TaskManager{
vector<int> new_freeagents;
vector<int> new_tasks;

list<int> check_finished_tasks(vector<State> states, int timestep);
list<int> check_finished_tasks(vector<State>& states, int timestep);

int curr_timestep;


// reveal new task
void reveal_tasks(int timestep);
void update_tasks(vector<State> states, vector<int> assignment, int timestep);
void update_tasks(vector<State>& states, vector<int>& assignment, int timestep);

void sync_shared_env(SharedEnvironment* env);

void set_num_tasks_reveal(float num){num_tasks_reveal = num*num_of_agents;};
void set_logger(Logger* logger){this->logger = logger;}

bool validate_task_assgnment(vector<int> assignment); // validate the task assignment
bool set_task_assignment( vector<int> assignment); // set the task assignment; return true if task is valid
bool validate_task_assignment(vector<int>& assignment); // validate the task assignment
bool set_task_assignment( vector<int>& assignment); // set the task assignment; return true if task is valid

int get_number_errors() const {return schedule_errors.size();}

Expand Down
2 changes: 0 additions & 2 deletions src/CompetitionSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ void BaseSystem::simulate(int simulation_time)

for (int i = 0 ; i< timeout_timesteps; i ++){
simulator.move(all_wait_actions);
for (int k = 0; k < num_of_agents; k++)

for (int a = 0; a < num_of_agents; a++)
{
if (!env->goal_locations[a].empty())
Expand Down
78 changes: 67 additions & 11 deletions src/TaskManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@

using json = nlohmann::ordered_json;


bool TaskManager::validate_task_assgnment(vector<int> assignment)
/**
* This function validates the proposed schedule (assignment) from participants
*
* @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents.
*
*/
bool TaskManager::validate_task_assignment(vector<int>& assignment)
{
if (assignment.size() != num_of_agents)
{
schedule_errors.push_back(make_tuple("Invalid schedule size",-1,-1,-1,curr_timestep+1));
logger->log_warning("Scheduler Error: assignment size does not match number of agents",curr_timestep+1);
return false;
}

Expand Down Expand Up @@ -54,7 +61,16 @@ bool TaskManager::validate_task_assgnment(vector<int> assignment)
return true;
}

bool TaskManager::set_task_assignment(vector< int> assignment)

/**
* This function updates the current task assignments of agents.
* It first checks if the proposed assignment is valid,
* then updates the current assignment and updates the corresponding agent_assigned of each affected task.
*
* @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents.
*
*/
bool TaskManager::set_task_assignment(vector< int>& assignment)
{
for (int a = 0; a < assignment.size(); a++)
{
Expand All @@ -63,19 +79,26 @@ bool TaskManager::set_task_assignment(vector< int> assignment)
planner_schedule[a].push_back(make_pair(curr_timestep,assignment[a]));
}
}
if (! validate_task_assgnment(assignment))
if (! validate_task_assignment(assignment))
{
return false;
}

//reset all the agent_assigned to -1, so that any droped task->agent_assignment will be -1
for (int a = 0; a < assignment.size(); a++)
{
if (assignment[a] < 0 && current_assignment[a] < 0)
if (current_assignment[a] >= 0){
ongoing_tasks[current_assignment[a]]->agent_assigned = -1;
}
}

// then set the updated agent_assigned according to new assignments.
for (int a = 0; a < assignment.size(); a++)
{
if (assignment[a] < 0)
{
continue;
}
if (current_assignment[a] >= 0)
ongoing_tasks[current_assignment[a]]->agent_assigned = -1;
int t_id = assignment[a];
current_assignment[a] = t_id;
ongoing_tasks[t_id]->agent_assigned = a;
Expand All @@ -92,7 +115,14 @@ bool TaskManager::set_task_assignment(vector< int> assignment)
return true;
}

list<int> TaskManager::check_finished_tasks(vector<State> states, int timestep)
/**
* This function checks if any task is finished at the current timestep.
* If a task is finished, it updates the task's completion time and the agent's current assignment.
*
* @param states a vector of states of all agents, including the current location of each agent on the map.
* @param timestep the current timestep.
*/
list<int> TaskManager::check_finished_tasks(vector<State>& states, int timestep)
{
list<int> finished_tasks_this_timestep; // <agent_id, task_id, timestep>
new_freeagents.clear(); //prepare to push all new free agents to the shared environment
Expand Down Expand Up @@ -122,7 +152,12 @@ list<int> TaskManager::check_finished_tasks(vector<State> states, int timestep)
return finished_tasks_this_timestep;
}


/**
* This function synchronises the shared environment with the current task manager.
* It copies the current task pool, current task schedule, new free agents, and new tasks to the shared environment.
*
* @param env a pointer to the shared environment.
*/
void TaskManager::sync_shared_env(SharedEnvironment* env)
{
env->task_pool.clear();
Expand All @@ -135,6 +170,13 @@ void TaskManager::sync_shared_env(SharedEnvironment* env)
env->new_tasks = new_tasks;
}

/**
* This function reveals new tasks at the current timestep.
* It reveals a fixed number of tasks at each timestep,
* and adds them to the ongoing tasks, new_tasks, and all_tasks.
*
* @param timestep the current timestep.
*/
void TaskManager::reveal_tasks(int timestep)
{
new_tasks.clear(); //prepare to push all new revealed tasks to the shared environment
Expand All @@ -151,15 +193,29 @@ void TaskManager::reveal_tasks(int timestep)
}
}

void TaskManager::update_tasks(vector<State> states, vector<int> assignment, int timestep)
/**
* This function is reponsible for the task management process:
* 1. It updates the current assignments of agents with proposed schedule from participants.
* 2. It checks if any task is finished at the current timestep.
* 3. It reveals new tasks at the current timestep.
*
* @param states a vector of states of all agents, including the current location of each agent on the map.
* @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents.
* @param timestep the current timestep.
*/
void TaskManager::update_tasks(vector<State>& states, vector<int>& assignment, int timestep)
{
curr_timestep = timestep;
set_task_assignment(assignment);
check_finished_tasks(states,timestep);
reveal_tasks(timestep);
}


/**
* This function converts all tasks to a JSON object.
*
* @param map_cols the number of columns in the map.
*/
json TaskManager::to_json(int map_cols) const
{

Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.1.0
2.1.1

0 comments on commit 216a3c7

Please sign in to comment.