-
Notifications
You must be signed in to change notification settings - Fork 9
Controller development guide
A controller is composed of two fundamental elements that instruct the simulator on how to use it: firstly, it needs a configuration file, which defines its class and any parameters required for it to properly function; secondly, it requires the class itself, in which the code regulating its behavior resides.
This section will provide a simple example on how to define a new controller, as to facilitate the implementation of new, potentially complex behaviors for the simulator to apply.
First, we will need to create a controller configuration file. These files ideally reside under the conf/
directory in the simulator's directory structure.
Configuration is defined as a standard JSON file. The object defined therein must always provide a class, expressed as <source file>.<class name>
. It may also incorporate any parameters required to regulate the controller's behavior, such as configurations for path planning algorithms, heuristics, hyperparameters for model generation, or any element the model may require to perform its functions. These parameters will be automatically parsed and passed onto the corresponding controller.
Take, for example, the contents of conf/controller-naive.json
. Note that only the class
parameter is required.
{
"class": "naive_controller.Naive_Controller"
}
As for the code defining a controller, it must follow a small set of simple rules for it to be functional. Firstly, it needs to define a controller class, which inherits its behavior from the base Controller
class, residing under the controller
module. For ease of development, it is also recommended to import the upd_sensor_angles
wrapper, which will help interface with the robotic part of the simulator without needing to account for most of the hardware details involved in its design.
Having created our class, it will need to fulfill the following conditions:
- Take a
config
parameter as part of its__init__
method. This parameter will contain all information required. Furthermore, this method must callsuper.__init__
, passing in the selfsameconfig
parameter as an argument. Afterwards, required information specific to the current controller can be extracted simply by accessing theconfig
parameter itself as a dictionary, with the key represented as a string corresponding to the name of the configuration parameters. These keys match the ones previously defined in the configuration JSON file both in spelling and exact capitalization. Failing to comply with these two requirements when accessing the configuration dictionary will most likely lead to a KeyError exception, and the simulator being unable to start until the problem is fixed. - Have a
control
method which takes in the readings of the robot's sensors (represented by thedst
parameter in the examples provided here), and returns the angular and linear velocity, in that exact order. It is highly recommended that theupd_sensor_angles
be applied to this control method to simplify its implementation in any controllers that may require access to robot state information.
An example of one such file follows:
from controller import Controller, upd_sensor_angles
class Naive_Controller(Controller):
"""
Class implementing an extremely simple naive controller.
Not really dependable, meant to serve as a simple example
only.
"""
def __init__(self, config):
"""
Constructor for the Naive_Controller class.
Outputs:
- A configured Naive_Controller object.
"""
super().__init__("NAIVE", config)
@upd_sensor_angles
def control(self, dst):
"""
Driver function to centralize and standardize the controller. Can be modified by child classes,
provided that the result value always is a tuple of the form (angular velocity, acceleration)
"""
return 3, 0
Take note of the structure of the control
method:
def control(self, dst):
"""
Driver function to centralize and standardize the controller. Can be modified by child classes,
provided that the result value always is a tuple of the form (angular velocity, acceleration)
"""
return 3, 0
In this case, the naive controller we are implementing will simply return an angular velocity of 3, and prompt the robot to spin around on its central axis. Any sort of complex behavior will require computing the desired angular and linear velocities, and returning them at the end of the method. Any number of methods or functions can be called for this process, provided they are within scope.