A set of nodes and libraries for defining and executing long-term task-level behaviour.
The task executor is the central element of the strands_executive
framework. It receives tasks from clients, queues them for execution, then manages their execution and the required navigation between tasks. This package provides two executors: the simple fifo_task_executor
which simply executes tasks in the order it receives them (ignoring their timing constraints) and the scheduled_task_executor
which uses the scheduler node to create and maintain an execution schedule based on the time windows of the tasks. This package also provides the task_routine
Python module which facilitates the creation of daily task routines with repeating temporal structure.
For the task routine module see the description on the overview page, and the API documentation.
For the executive framework to function correctly, you must have the mongodb_store nodes running. These are used by the framework to store tasks with arbitrary arguments.
roslaunch mongodb_store mongodb_store.launch
or with path specifying, where should the db is stored:
roslaunch mongodb_store mongodb_store.launch db_path:=/...
Currently the task executive abstracts over navigation actions using the STRANDS topological navigation framework. Therefore you must have this framework running. For testing, or if you're not running the full topological navigation system, you can run a simple simulated topological system with the following command:
roslaunch topological_utils dummy_topological_navigation.launch
This produces a map with 9 nodes: ChargingPoint
in the centre, with v_-2
, v_-1
to v_2
running vertically and h_-2
to h_2
running horizontally, joining ChargingPoint
in the middle.
This node receives tasks via the services, schedules them for execution, then executes them in the order defined by the schedule.
When the executor is started, it will not start executing any tasks until the execution status is set to True
via a call to the /task_executor/set_execution_status
(strands_executive_msgs/GetExecutionStatus
) service. If execution status is set to False
then execution pauses, interrupting any currently executing task.
Tasks are added using the add_task
(single task) and add_tasks
(multiple tasks) services. All received tasks are added to a queue for scheduling which is monitored by the executor. When then queue contains tasks, the new tasks, plus the tasks already scheduled are sent to the scheduler node. If a task is successfully created, this replaces the previous schedule and execution continues. If scheduling fails then the newly added tasks are dropped by the executor and the previous schedule is reinstated. Adding new tasks does not interrupt the currently executing task.
If a task should be executed immediately, the demand_task
(strands_executive_msgs/DemandTask
) service can be used. This interrupts the currently executing task and replaces it with the demanded task. The schedule is then recreated to respect the execution constraints of the demanded task, and, if possible the interrupted task is included in this new schedule.
By default the execution of tasks is interruptible (via actionlib preempt). If you do not wish your task to be interrupted in these condition you can provide the IsTaskInterruptible.srv
service at the name <task name>_is_interruptible
, e.g. do_dishes_is_interruptible
from the example above. You can change the return value at runtime as this will be checked prior to interruption.
Here's an example from the node which provides the wait_action
.
class WaitServer:
def __init__(self):
self.server = actionlib.SimpleActionServer('wait_action', WaitAction, self.execute, False)
self.server.start()
# this is not necessary in this node, but included for testing purposes
rospy.Service('wait_action_is_interruptible', IsTaskInterruptible, self.is_interruptible)
def is_interruptible(self, req):
# rospy.loginfo('Yes, interrupt me, go ahead')
# return True
rospy.loginfo('No, I will never stop')
return False
When a task is executed it pass through two phases: navigation and action execution. If the task has a start_node_id
set then the executor uses topological navigation to move the robot to this start node. Before doing so it obtains an estimate of the travel time from the topological_navigation/travel_time_estimator
service. If the travel time greatly exceeds this estimate, the topological navigation action is preempted and the task execution is failed. If the topological_navigation action reports anything but success on completion then the task execution is failed. If it reports success then the task moves on to the action execution phase. This phases triggers the action server described by the task. If execution of the action server greatly exceeds the max_duration
of the task, it is preempted and execution is considered failed. The overall execution state machine is pictured below.
task_executor/add_tasks
(strands_executive_msgs/AddTasks)
Add a list of tasks to be scheduled for execution.
task_executor/add_task
(strands_executive_msgs/AddTask)
Add a single task to be scheduled for execution.
task_executor/demand_task
(strands_executive_msgs/DemandTask)
Triggers the immediate execution of a task, interrupting the currently executing task.
task_executor/set_execution_status
(strands_executive_msgs/SetExecutionStatus)
Sets the execution status of the executor. Set to false to pause execution. Starts at false so much be set to true on start-up.
task_executor/get_execution_status
(strands_executive_msgs/GetExecutionStatus)
Gets the current execution status of the executor.
task_executor/clear_schedule
(std_srvs/Empty
)
Clears all tasks scheduled for execution. Cancels any active task.
task_executor/cancel_task
(strands_executive_msgs/CancelTask)
Removes the task with the given id from the schedule. If this task is currently executing, execution is interrupted.
task_executor/get_active_task
(strands_executive_msgs/GetActiveTask)
Gets the task which is currently executing.
task_executor/events
strands_executive_msgs/TaskEvent)
Events that happen as the task executor passes through its state machine for each task.
current_schedule
strands_executive_msgs/ExecutionStatus)
The list of upcoming tasks and what is currently being executed.
A greatly simplified task executor that executes tasks in the order they are added, and only supports task addition and very little else when compared to the scheduled_task_executor
.
Prints a summary of the current_schedule
topic.