Skip to content
This repository has been archived by the owner on Oct 25, 2023. It is now read-only.

ai_scheduler

Pierre Laclau edited this page Feb 18, 2018 · 13 revisions

Description

ai/scheduler is the AI's ❤️. It's role is to manage a tree of possible actions during a match, while dynamically adapting the current strategy when problems are encountered (e.g. navigation plan blocked by an enemy, door locked, servo or internal component not responding...). The tree is defined in an XML file; the scheduler then explores it by sending the corresponding requests to the inferior nodes, while adapting the following ones based on the received answers.

Therefore, the idea is to define a "possibilities tree" of a game in the XML files for a certain robot. It is possible to give dependencies between tasks, execution orders, simultaneous requests, etc. which is what makes the tree adaptive to errors.

Authors and versions

  • v0.1 (A17) : @MadeInPierre
    • Main parsing and decision structure
    • ActionLists supporting order and execution modes
    • Support order requests for services only
  • v0.2 (A17) : @Milesial
    • Supporting parameter entries in the XML file for the order requests (topic sending, services and actions).
  • v1.0 (fin A17) : @MadeInPierre
    • Supporting topic and (blocking for now) action request types
    • ActionLists now support while and for loops
    • Supporting team-specific tasks in the XML files
    • Strategy starts with an HMI arm event and ends with a game_status HALT event.

Installation

The node doesn't require any particular dependency, aside from rospy (ROS python library for communication) and the existence of the nodes where requests are sent to.

Configuration

Two main elements need to be configured in order to have a working strategy : a python dictionary located inside ai_scheduler/src/scheduler_communication.py, which gives the proper ROS classes to the scheduler corresponding to the given server destination names; as well as the files 1_strategies.xml, 2_actions.xml and 3_orders.xml located at memory_definitions/def/robots/<robot_name>/ai/*.xml.

Creating the robot's strategies, actions and orders (XML files)

The structure the scheduler follows for managing the actions tree relies on 4 main concepts:

  • Orders, defined in the file 3_orders.xml : they are the scheduler soul 🙏. They represent the real ROS messages (topics pub and sub, service calls, action requests) that will be sent to the inferior nodes.
  • Actions, defined in the file 2_actions.xml : they are a list of Orders and other Actions defined elsewhere in the file. They are a way of declaring a set of Orders that are independent from the strategies the robot could follow. For instance, an action called go_fire_balls can be used in many different strategies; it is better to define this associated set of actions once (in an action) in order to reference it once in all the concerned strategies (no copy&paste allowed, ever 🚫).
  • Action Lists, that can be included inside Actions or Strategies: they are the scheduler's intelligence 💪, or rather a list of actions and orders with many optional execution orders and modes. This is where we can define conditions, number of executions, dependencies and relationships with the ActionList's subtasks. This is the tool that lets the scheduler adapt itself and be "intelligent". Note that Actions are in reality nothing more than ActionLists that can reference themselves; you can also define execution modes and orders in Actions.
  • Strategies, defined in the file 1_strategies.xml : a strategy represents a specific tree of actions in a game. This file lets you create several alternate trees for a same robot: The user will then be able to choose a specific strategy to apply at the robot startup according to the team we are against with! Can also be useful to create small action trees for testing purposes without deleting the main game tree. A strategy can have actionref, orderref or actionlist elements.

Order definitions

Orders are defined in the file 3_orders.xml. The <order> XML node needs to have a ref attribute, in order for it to be referenced from the outside. It can take an optional duration attribute, which is a manual estimation of how long the order will take to execute (ai/scheduler can adapt its next requests according to how much time's left TODO not yet). This node must have a <message> child node, with a dest attribute, representing the service or action destination the message will be sent to. A message node holds multiple <param>, mirroring the structure of the ROS request message sent. Each <param> has a name, matching the name of the parameter in the ROS message, and a type, used to parse the parameter while building the request. A parameter can be optional, which means it doesn't have to be filled when the order is called (the default value will be put in by ROS). It can be preset : in that case, the parameter cannot be set when the order is called, the value is constant and cannot be changed. To finish, default values can be set in the order definition. Note that a param with a default value is therefore optional, and a param cannot be preset and optional.

Example

Let's assume we want to send a request to /asserv/goto, wich has the following ROS definition :

geometry_msgs/Pose2D position
float64 number
uint8 command
string message

Here is a valid example of the order definition, with all the elements seen above :

<order ref="goto" duration="1">
  <message dest="/asserv/goto">
    <param name="position" type="pose2d" />                   <!-- regular parameter, required -->
    <param name="number" type="float">42.8</param>            <!-- parameter with a default value -->
    <param name="command" type="int" preset="true">5</param>  <!-- preset parameter -->
    <param name="message" type="string" optional="true"/>     <!-- optional parameter -->
  </message>
</order>

Order references

The orders can be referenced from the actions or strategies definition files. To reference an order, a node <orderref> has to have its ref attribute matching the ref attribute of the referenced order. As childs of the node, the parameters (non-preset) are set, with the tag of the child matching the name of the parameter.

Example

Let's continue with our defined order from above. Suppose we want to reference it, here is a valid way to do it :

<orderref ref="goto">
  <position>
    <x>55.2</x>
    <y>57.1</y>
    <theta>3.14159</theta>
  </position>
  <message>hello world!</message> <!-- this node is optional -->
</orderref>

Action definitions

The actions are defined in the file 2_actions.xml. The <action> node has to have a ref attribute, in order for it to be referenced from the outside (other actions or strategies). This node has three children:

  • The <conditions> node is TODO yet to be implemented.
  • The <params> node holds all the parameters used when calling this action. They are defined identically to the parameters in the message of an order, seen above, with the name, type, preset and optional attributes. For them to be useful, the parameters given to an action have to be passed to the orders or the actions that are called from this action. This will be discussed in the next part.
  • The <actions> node is the main definition element. It defines the list of subtasks, which can be actionref, orderref or actionlist nodes. Note that this node is internally interpreted as an ActionList: you can specify the exec, order and repeat attributes and ai/scheduler will execute the subtasks with the preferences you just set.

Action parameters binding

An action has a bunch of children (orderref and/or actionref). The parameters of the parent action can be passed to these children for them to use them. To do that, we use the bind attributes on the children's parameters like so :

<action ref="goto_spawn">
  <params>
    <param name="speed"/>
  </params>
  <actions exec="all" order="linear" repeat="2">
    <orderref ref="wheels_goto">
      <target_pos>
        <x>6.5</x>
        <y>7.5</y>
        <theta>7</theta>
      </target_pos>
      <speed bind="speed"/>
    </orderref>
  </actions>
</action>

Here, the order wheels_goto requires two parameters. The target_pos one is given directly in the orderref. However, no value is passed for the speed one. Instead, it is binded to the parameter named speed of the parent action. The speed parameter of this action is defined in the params node. When the goto_spawn action will be referenced, the speed parameter given to it will be passed to the order reference of wheels_goto. Note that the names for the parent parameter and the binded parameter do not have to match.

Action references

The call of an action works the same as the order reference, replacing orderref by actionref.

Adding new parameter types

In order to parse correctly all wanted type, one has to add a parser class, child of the Param class, for each type of parameter. Check out src/scheduler/tasks/params.py for more details.

ActionList definitions

Actions and Strategies can include another XML node type that lets us define the dependencies, repeat loops and execution modes and orders between actionref or orderref elements. They do not directly trigger any ROS request, but are here only to the the AI's "adaptiveness".

Declaring an actionlist is very simple: you simply define the XML node with the optional attributes exec, order and repeat (see the explanation below) and then directly put your actionref and/or orderref references inside the node. Here are a few valid examples:

<actionlist exec="+" repeat="while">
    <orderref .../>
    <actionref .../>
    <actionlist order="simultaneous">
        ...
    </actionlist>
</actionlist>
The Execution Mode attribute

All subtasks references declared inside the list will be executing on a certain execution mode. When no exec attribute is defined, the default value "all" is taken.

The execution mode defines the condition on which the list will be considered as successfully executed based on the subtasks' execution results. You may sometimes want to create a set of subtasks where the entire group would be considered successful if at least one subtask ran successfully, for example. Here are all the available options:

Mode What it does
all All subtasks must have ran successfully for the list to be considered successful.
one Exactly one subtask among all subtasks must have ran successfully for the list to be considered successful. If a task runs successfully, all other subtasks are blocked and will never run.
+ At least one subtask must have ran successfully for the list to be considered successful. The scheduler will try all subtasks before giving the final list status.
The Execution Order attribute

You can also set the order in which the subtasks will be executed. When no order attribute is defined, the default value "linear" is taken.

Here are your options:

Order What it does
linear scheduler will follow the XML definition order, going from top to bottom one by one.
random scheduler will take a random subtask to execute at each step.
simultaneous TODO not supported yet All subtasks will be triggered at once.
fastest scheduler will execute the substasks that take less time to execute first, using the duration attribute that can be set in an Order definition. This is only based on a manual estimation.
mostreward scheduler will take the subtask that would give the most amount of reward (game score) at each new execution.
The Loop attribute

Finally, you can also make a list repeat several times.

Loop type What it does
<any number > 0> For loop : the list will be repeated by the given amount of times.
while The list will be repeated as long as the previous execution was successful.

Strategies

Strategies are very simple to create. The strategy node takes a name attribute that will be displayed on the HMI selection screen at startup. The node must include two children notes:

  • An actions node, which is an ActionList (exec, order and repeat attributes allowed) containing the tasks that will be executed when the strategy is started.
  • An actions_onfinish node, with a ActionList of subtasks that will be executed when all actions are finished or the strategy execution is stopped prematurely (game_status HALT, timer ended...). TODO never executes this list yet, not implemented

The root strategies node must have a teams node among the strategy nodes, which sets the names of teams the robot can play in. See the next section for using teams with ai/scheduler.

Here is an example of a valid 1_strategies.xml file:

<strategies>
    <teams>
        <team name="green"/>
        <team name="orange"/>
    </teams>
    <strategy name="best_strat_ever">
        <actions name="In-Game Actions">
	    <actionref ref="game_start"/>

	    <actionlist name="Main actions">
	        <actionref ref="activate_button"/>
	        <actionref ref="push_grape_1"/>
	    </actionlist>
        </actions>
        <actions_onfinish name="After-Game Actions">
        </actions_onfinish>
    </strategy>
    <strategy name="test_asserv" .../>
</strategies>

Using teams

You may sometimes want certain tasks to execute if the robot is in a certain team only. Simply create a team node inside any Strategy, ActionList or Action with a name attribute pointing to the team the subtasks will execute with.

<actionlist>
    <actionref .../>
    <team name="green">
        <actionref .../>
        <orderref .../>
    </team>
</actionlist>

Giving the scheduler the ability to speak

All server destinations written in the Orders' XML definitions must be be added in the python dictionary in ai_scheduler/src/scheduler_communication.py. This lets ai/scheduler send the Order requests to the ROS nodes, and know what each destination represent (a topic where subscribers are waiting, a topic where messages are being published, a service server or an action server), as the XML files can't provide the python classes that describe the ROS messages to be sent. When a new destination is created, you just have to add a dictionary entry according to the following rules:

  • The server name is the dictionary key. This is the dest attribute you declared while declaring the asociated order.
  • The value is a 2-value (3 in the case of action) tuple, with the server type in first position. The scheduler supports the following types of requests:
Server type Description
RequestTypes.PUB_MSG the key is seen as a topic name. When the corresponding Order is executed, ai/scheduler publishes a message on the topic with the provided message parameters givent in the XML file.
RequestTypes.SUB_MSG the key is seen as a topic name. The scheduler waits for a message to be published on that topic by an external node before considering the Order executed successfully.
RequestTypes.SERVICE ai/scheduler sends a service request the the destination name, with the message parameters included in the XML definitions. The response message must have a ai_scheduler/TaskResult or bool success in it in order for the scheduler to know if the execution was successfully executed by the messge server.
RequestTypes.ACTION ai/scheduler sends a blocking (TODO for now) action request to the associated destination, with the XML parameters. a bool success or ai_scheduler/TaskResult result must exist in the response message.
  • You must then provide the Python class corresponding to the topic/service/action destination message. Simply add import my_package.msg (or .srv) in the file header zone, and then add in the tuple:
    • For a topic or service, provide the main message class (e.g. drivers_ard_hmi.msg.ROSEvent or drivers_ard_asserv.srv.SetPos)
    • For an action, provide the main action class in the second tuple position (e.g. movement_actuators.msg.dispatchAction) and the Goal class after that (e.g. movement_actuators.msg.dispatchGoal).
Example

The RequestTypes class in scheduler_communication.py must then have the following structure:

import ai_timer.srv
import ai_scheduler.msg

class RequestTypes(object):
    PUB_MSG = 0 # Publish a message on the specified topic.
    SUB_MSG = 1 # Wait for a message to be published at the specified topic name.
    SERVICE = 2 # Sends a service request to the specified destination.
    ACTION  = 3 # Sends a blocking action request to the specified destination.

    SERVERS = None

    @staticmethod
    def init():
        RequestTypes.SERVERS = {
            "/ai/scheduler/score": (RequestTypes.PUB_MSG, ai_scheduler.msg.AIScore),
            "/ai/timer/start":     (RequestTypes.SERVICE, ai_timer.srv.StartTimer)
        }
    ...

Running the package

Now let's start the node and watch a strategy being explored : simply start the node by executing:

rosrun ai_scheduler scheduler_node.py

The scheduler will initialize itself by loading the strategies XML file and looking for all the different strategies that have been declared. It then goes in a standby mode, where it regularly publishes the available strategy names to the topic /feedback/ard_hmi/set_strategies and /feedback/ard_hmi/set_teams, waiting for an HMI answer with the selected strategy and team.

You can now start a strategy with the HMI selection screen and buttons. However, if you do not have the HMI connected on your setup, simply simulate an HMI selection event py publishing:

rostopic pub /feedback/ard_hmi/hmi_event drivers_ard_hmi/HMIEvent "event: 1
chosen_strategy_id: 0
chosen_team_id: 0" --once

The node will load the 3 XML files (1_strategies.xml, 2_actions.xml and 3_orders.xml) with the specified team. The strategy will be displayed in the console with a nice color scheme, and the strategy will automatically be followed until all orders are called or a HALT has been set in ai/game_status.

Communication

How it Works

Just how does all that beautiful stuff work ?