-
Notifications
You must be signed in to change notification settings - Fork 1
ai_scheduler
The scheduler is the AI's heart. 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 request to the inferior nodes, while adapting the following requests based on the received answers.
Therefore, the idea is to define a "possibilities tree" of a match in the XML files for a certain robot. It is possible to give dependencies between tasks, execution orders, simultaneous requests, etc.
-
v0.1 (A17) : @MadeInPierre
- General 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
- Strategy start with HMI arm event and end with 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 name; 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_ordres.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 or other Actions defined in the file. They are a way of declaring a set of orders that are independent from the possible strategies the robot could follow. For isntance, 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 in all the strategies that needed (no copy&paste allowed, ever 🚫). - Action Lists, that can be included inside Actions or Strategies: they are the scheduler's intelligence 💪, or a list of actions and orders with many optional execution orders and modes. This is where we can define conditions, number of executions, and dependencies or the descending tasks. This is 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 then also define execution modes 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: this is lets the user 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.
The orders are defined in the file 3_orders.xml
. The <order>
node has 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. This node must have a <message>
child node, with a dest
attributes, representing the service or action 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. 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
, with 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. This node has three children :
- The
<conditions>
node is 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
<action>
node doc is TODO
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">
<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 ai_params.py
for more details.
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 :
- The server type in first position. The scheduler supports the following types of requests:
-
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 aai_scheduler/TaskResult
orbool success
in it in order for the scheduler to know if the execution was successfully executed by the messge server. -
RequestTypes.ACTION
: a/scheduler sends a blocking (TODO for now) action request to the associated destination, with the XML parameters. abool success
orai_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 server type in first position. The scheduler supports the following types of requests:
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)
}
...
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]!