A supervisor controls a robot. That means, that for a particular kind of robot, the supervisor can deduce the state of the robot (such as its pose) from the provided information (such as the reading of its wheel encoders), and set the inputs (such as wheel speeds) to make the robot attain a particular goal.
In order to achieve this, the supervisor should subdivide achieving the goal into several small tasks and use one or more controllers to accomplish them. The controllers are supposed to be general, reusable robot behaviors, and the task of the supervisor is to choose a suitable controller and supply the expected parameters to it, as deduced from the particular robot’s parameters.
The simplest supervisor only has one behavior for the robot. In the constructor, the supervisor should create the corresponding controller and assign it to the current variable. In order to load the controller, it is recommended to use the create_controller() method, that accepts a controller module string and the initial parameters for the controller.
The simulator will call the execute() method and supply the state of the robot, as returned by robot_info(), and the elapsed time. By default, the supervisor will use the process_state_info() method to interpret the information about the robot and then use the structure returned by get_controller_state() to execute the controller. The return value of the controller is passed to the simulator and subsequently to the robot’s inputs. As the controller output may not be in the right format for the robot, you can overwrite the execute() method and transform the output before returning it to the simulator (see supervisors.khepera3.K3Supervisor.execute() for an example)
In a more complicated supervisor, there will be more than one controller, and thus more than one state. The base supervisor class implements a finite state machine to handle this. This state machine then switches between states (controllers) as the state of the robot and its environment changes.
The state machine is defined as a set of controllers (states) and conditions, that would lead to a change of the current controller. To fully define one state, the add_controller() method of the supervisor should be called in the following way:
self.add_controller(c0, (condition1, c1), (condition2, c2), ...)
to add a state with a controller c0. The conditions are functions that take no parameters and evaluate to true or false. If a condition evaluates to true, the controller is switched e.g. to c1 for condition1.
Note
Since the condition functions are called without any arguments, all of the parameters you want to access in them, should be stored in the supervisor. A good place to do that is the process_state_info() method, that is guaranteed to be called before any conditions are checked. In very complicated cases, that might not be covered by this state machine, you are welcome to overwrite the execute() method and implement a more fine-grained behavior.
It is possible to let the user change some of the supervisor’s parameters when the simulation is running. The user can access the parameters by using the docking windows in the interface. Each window corresponds to one supervisor, and the content of the window is controlled completely by that supervisor.
To define the interface, you will have to implement the get_ui_description() method. The return value is a dictionary, where every key corresponds to a value label or a value group label, and the values are either further dictionaries, or numbers, or strings. Let’s have a look at the following example:
This dock was generated with the following code:
def get_ui_description(self,p = None):
"""Returns the UI description for the docker"""
if p is None:
p = self.parameters
return [('behavior',[
('goal', [
('x',p.behavior.goal.x),
('y',p.behavior.goal.y)]),
(('follow', 'Follow walls'), (p.behavior.follow,['yes','no'])),
]),
(('pid','PID close parameters','close'),[
(('kp','Proportional gain'), p.pid['close'].kp),
(('ki','Integral gain'), p.pid['close'].ki),
(('kd','Differential gain'), p.pid['close'].kd)
]),
(('pid','PID far parameters','far'), [
(('kp','Proportional gain'), p.pid['far'].kp),
(('ki','Integral gain'), p.pid['far'].ki),
(('kd','Differential gain'), p.pid['far'].kd)
])
]
As you see, every (key, value) tuple in the list that is returned from get_ui_description corresponds to an interface element. The type of the element depends on the type of value. ('x',3.0) will create a spin-box, ('color',('yellow',['red','yellow','green'])) - a set of radio-buttons red, yellow and green with yellow selected, and ('goal', [ ('x',1.0), ('y', 1.0)] ) a grouping box with two spin-boxes inside.
The key may be a string or a tuple with two or three values. The string or the first field of the tuple is the name of the field in the structure, like p.behavior above. It is also the name of an XML tag that corresponds to this element if the parameters are saved to a file. If the tuple has a second field, it is used as a label. In the case no label is supplied, the field name is capitalized and used as a label. A third field in the tuple is useful in the case you have two or more identical structures in your parameters. It is translated to the id attribute of the XML tag and to the key of the dictionary, that becomes a field of the parameters structure.
In the example above, the p argument of get_ui_description has a structure identical to the one that will be supplied to set_parameters() method if the user clicks Apply in the dock. Although this is not required, it can considerably simplify get_parameters(), from which such a structure has to be returned.
Note
The implementation of the Khepera3 supervisors use the same structure self.parameters to both store the parameters and to pass parameters to controllers. This behavior is neither compulsory nor ideal. In the general case, the controllers might require differently structured information.
The supervisor can also convey some information to the user by drawing on the view. This can be used e.g. to debug the controllers and the supervisor itself. The drawing happens in the draw() function, that takes a single Renderer parameter. Drawing happens after the execute() has been called, and so the supervisor can draw content directly related to the current state of the world.
The supervisor class oversees the control of a single robot. The supervisor does not move the robot directly. Instead, the supervisor selects a controller to do the work and uses the controller outputs to generate the robot inputs.
Parameters: |
|
---|
Any extension of pysimiam will require inheriting from this superclass. The important methods that have to be implemented to control a robot are estimate_pose(), process(), init_default_parameters() and get_ui_description().
The base class implements a state machine for switching between different controllers. See add_controller() for more information.
Type : | Pose |
---|
The initial pose of the robot, as supplied to the constructor. This parameter can be used in the user implementation
Type : | Pose |
---|
The estimated pose of the robot. This variable is updated automatically in the beginning of the calculation cycle using estimate_pose()
Type : | Struct |
---|
Current parameter structure of the supervisor. Updated in set_parameters()
Type : | Controller |
---|
The current controller to be executed in execute(). The subclass can set this value in process() or in the constructor. In case the state machine is used, the current controller will be switched automatically.
Type : | {Controller: [(condition()->bool,:class:~controller.Controller)]} |
---|
The transition table of the state machine. The keys of the dictionary are the state. The conditions are executed one after another until one returns True or the list is through. If one of the conditions evaluates to True, its corresponding controller is made current.
Type : | int |
---|
The color of the robot in the view (useful for drawing).
Add a transition table for a state with controller
The arguments are (function, controller) tuples. The functions cannot take any arguments. Each step, the functions are executed in the order they were supplied to this function. If a function evaluates to True, the current controller switches to the one specified with this function. The target controller is restarted using controller.Controller.restart().
The functions are guaranteed to be called after process(). Thus, robot should contain actual information about the robot.
Create and return a controller instance for a given controller class.
Parameters: |
|
---|
Draw anything in the view before anything else is drawn (except the grid)
Parameters: | renderer (Renderer) – A renderer to draw with |
---|
Draw anything in the view after everything else is drawn
Parameters: | renderer (Renderer) – A renderer to draw with |
---|
Updates the pose using odometry calculations.
Returns: | The estimated robot pose |
---|---|
Return type: | Pose |
The result of the evaluation of this function will be used to set self.pose_est
Must be implemented in subclasses.
Based on robot state and elapsed time, return the parameters for robot motion.
Parameters: |
|
---|---|
Returns: | An object (normally a tuple) that will be passed to the robot’s set_inputs() method. |
The default implementation proceeds as follows:
Check if the controller has to be switched
Get controller state from get_controller_state()
Execute currently selected controller with the parameters from previous step
Return unicycle model parameters as an output (velocity, omega)
Get the parameters that the current controller needs for operation
Returns: | A parameter structure in the format appropriate for the current controller. |
---|---|
Return type: | Struct |
The result of this function will be used to run the controller.
Must be implemented in subclasses
Get the parameter structure of the supervisor. A call to supervisor.set_parameters(supervisor.get_parameters()) should not change the supervisor’s state
Returns: | A supervisor-specific parameter structure. |
---|---|
Return type: | Struct |
Return a list describing the parameters available to the user.
Parameters: | params (Struct) – An instance of the paramaters structure as returned from get_parameters. If not specified, this method should use parameters |
---|---|
Returns: | A list describing the interface |
The structure returned by this function is used in the interface to show a window where the user can adjust the supervisor parameters. When the user confirms the changed parameters, this structure is used to create the structure that will be passed to set_parameters().
The format of the returned object is as follows:
Must be implemented in subclasses.
Populate parameters with default values
Must be implemented in subclasses.
Evaluate the information about the robot and set state variables.
Update this supervisor parameters. The params will have the same structure as specified by get_ui_description()
Parameters: | params (Struct) – An instance of the paramaters structure as can be returned from get_parameters(). |
---|