open-rmf / rmf_task

RMF library for managing task allocations
Apache License 2.0
22 stars 19 forks source link

Support tasks that have indefinite/permanent duration #42

Open mxgrey opened 2 years ago

mxgrey commented 2 years ago

There may be "tasks" that need to run indefinitely or permanently. For example, a patrolling task or a continuous delivery task (where we expect a new delivery to always be ready to go as soon as the previous delivery is finished).

In these cases, it's best to assign the continuous task to one robot, and then exclude the robot from being assigned any further tasks until the indefinite task is canceled or terminates some other way. (Note that we'll assume these indefinite tasks will be responsible for having battery recharging phases)

For the planner, we could indicate that a task is indefinite by changing Estimate::finish_state to return a std::optional<State> where a std::nullopt value means that it will run indefinitely (as opposed to Task::Model::estimate_finish where returning std::nullopt means that the task cannot be performed).

When a branch in the planner assigns an indefinite task to a robot, that branch cannot assign any more tasks to that robot.

arjo129 commented 2 years ago

~Hmm... What about charging? Or do we just ignore that for now.~ Sorry, I didn't read the description well enough.

mxgrey commented 2 years ago

Since it's difficult to spot and a very important point, I'll highlight it here:

Note that we'll assume these indefinite tasks will be responsible for having battery recharging phases

Yadunund commented 2 years ago

For the planner, we could indicate that a task is indefinite by changing Estimate::finish_state to return a std::optional where a std::nullopt value means that it will run indefinitely (as opposed to Task::Model::estimate_finish where returning std::nullopt means that the task cannot be performed).

One caveat with this approach is the need to rework the Candidates logic as it currently heavily relies on knowing the finish_time of the robot's state. I'm not sure if we can use an optional time field for the Candidates::Map (using Map = std::multimap<std::optional<rmf_traffic::Time>, Entry>;) and then update the logic everywhere to treat this as an indefinite request.

I was discussing with @arjo129 and felt a simpler approach could be to implement the Model for such requests such that it returns a finish_time with std::chrono::time_point<std::chrono::system_clock>::max(). This way the rest of the logic can remain the same. However, if we have two requests (one indefinite and the other definite) with the same earliest_start_times and only one robot, the problem of constraining the definite assignment before the indefinite remains. On potential easy fix for this would be add a check in the expand function to discard the expansion of nodes where an unassigned task is to be assigned to a robot with an indefinite request at the back of its assignment queue.

Another way to do this could be to assign any arbitrary finish time for such requests. And we have a large cost that gets multiplied with the overall cost of the node whenever a definite request is assigned after an indefinite one. A similar kind of action is performed in the BinaryPriorityCostCalculator implementation