-
Notifications
You must be signed in to change notification settings - Fork 1
ai_scheduler
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.
-
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.
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.
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
.
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 calledgo_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 haveactionref
,orderref
oractionlist
elements.
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.
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>
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.
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>
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 thename
,type
,preset
andoptional
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 beactionref
,orderref
oractionlist
nodes. Note that this node is internally interpreted as an ActionList: you can specify theexec
,order
andrepeat
attributes and ai/scheduler will execute the subtasks with the preferences you just set.
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.
The call of an action works the same as the order reference, replacing orderref
by actionref
.
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.
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>
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. |
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. |
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 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
andrepeat
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>
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>
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
ordrivers_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
).
- For a topic or service, provide the main message class (e.g.
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)
}
...
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.
Just how does all that beautiful stuff work ?
Wiki UTCoupe 2018, Université de Technologie de Compiègne, France.
Any questions or comments ? Wanna join our team ? Contact us at [email protected]!