ROS2-BDI
A planning based Multi-Agent BDI architecture for ROS2
Skills
Tip!
Check ros2_bdi_tests for demo examples regarding the implementation, dependencies needed for the package(s) in which sensor/action nodes are implemented.
Sensors
Setting up a sensor node for a ROS2-BDI agent is very simple. It mainly requires to create a cpp class inheriting from the Sensor abstract class, as provided within the framework, implementing the constructor and the performSensing() methods. Anything else is optional, not-required and strictly related to advanced logic specific to the sensor you might need. Keep in mind that the Sensor class is nonetheless than an extension of the base rclcpp::Node class providing additional features to easily and correctly manage the sensed properties forwarding them to update the belief set of the agent. This entails that any usual ROS2 node additional feature (e.g. topic subscription, calling/handling services....) you want to setup will be available, but it's suggested to strictly limit them in relation to the specific logic of the sensor.
Sensor constructor
Sensor(const std::string& sensor_name,
const ros2_bdi_interfaces::msg::Belief& proto_belief,
const bool match_param_by_param = true,
const bool enable_perform_sensing = true)
Parameter | Required | Description |
---|---|---|
sensor_name |
Name for the sensor; must be unique among the nodes in the ROS2-BDI agent namespace | |
proto_belief |
Belief prototype for the sensor | |
match_param_by_param |
Defines whether in a function/fluent sensing all params should match or not (default behaviour is set to true
to match the rule defined in the table below, but you can override it so that fluent/function case is basically equivalent to the one of the predicate-type prototype. |
|
enable_perform_sensing |
Default behaviour is to have a periodic sensing callback, but one can disable it to invoke the sense() call exclusively in other (subscription) callbacks |
IMPORTANT NOTE: the behaviour of the sensor is strictly bounded to the prototype belief it'll work with (setup in the constructor and not updatable later on at run-time). This is done mainly because we imagine sensor nodes to be very ”task-specific” in terms of detection, meaning that we could not have a single sensor detecting the distance from environmental objects on a given dimension in a three-dimensional space and at the same time recognizing what kind of objects they are. At a higher level, this is supposed to be interpreted as tasks for different sensors, even if it could be the case that both are accessing the same raw data stream.
The belief prototype a sensor node is tightly bounded to has different implications depending on the belief type. They are all summarised in the following table.
Belief type | Sensing behaviour | Example |
---|---|---|
INSTANCE pddl_type=1 |
The sensor node is bounded to a specific instance class (e.g. agent, waypoint) and could detect just the presence of instances of such type, i.e. triggering their addition/deletion to/from the belief set. |
Sensor bounded to trigger add/del of instances of type waypoint
|
PREDICATE pddl_type=2 |
The sensor node is bounded to a specific predicate definition (e.g. ”(in ?r - robot ?wp - waypoint)”), triggering either additions (or deletions) to (or from) the belief set of predicates of such type. | Sensor bounded to trigger add/del of ”in” predicates, as per definition given in the PDDL domain defined for the agent |
FLUENT pddl_type=3 |
The sensor node is bounded to a specific function instance (e.g. ”(battery charge ?cleaner 90)”), triggering first the addition (if not already there), then the update of its value by always publishing to the topic /agent_id/add_belief for further explanation). This does not imply that deletion is forbidden, but the most common usage scenarios shouldn’t foresee it. |
Sensor bounded to trigger update of ”battery_charge ” for ”cleaner ” agent
|
Sensing methods
void performSensing()
Assuming the default behaviour (i.e. enable_perform_sensing
set to true
), any Sensor node must implement the virtual method void performSensing()
specified in the
provided abstract class from which it's inheriting. The call will be regularly called respecting the value of the corresponding init.
parameters and it's supposed to withold the logic responsible for the sensing operations. If and when these require to update the belief set in
some way, the sense(belief, op)
API call is provided as described below and can be invoked within the performSensing()
implementation.
void
sense(const ros2_bdi_interfaces::msg::Belief& belief, const UpdOperation& op)
Parameter | Description |
---|---|
belief |
Belief to be added/updated/deleted to/from the belief set of the ROS2-BDI agent. MUST be compliant to the proto_belief
specified in the constructor.
|
op |
Specifying whether the belief must be added/updated/deleted, respectively the ammissible values are the following:
{ADD, UPD, DEL}
|
Sensor init. ROS2 parameters
Parameter | Type | Description | Default |
---|---|---|---|
init_sleep |
int | Initial sleep period expressed in seconds for the sensor node, waiting for the first trigger of the performSensing call |
0 |
sensing_freq |
float | Frequency for calling performSensing , expressed in Hz |
8.0 |
Actions
Setting up an action executor node for a ROS2-BDI agent can be seen as a little trickier, but hopefully the guide below will guide you through all the needed steps and available features.
Action constructor
BDIActionExecutor(const std::string& action_name,
const int& working_freq,
const bool agent_id_as_specialized_arg = true)
Parameter | Required | Description |
---|---|---|
action_name |
Name for the action; should match the one within the pddl domain definition | |
working_freq |
Frequency at which the doWork method is called (expressed in Hz) | |
agent_id_as_specialized_arg |
Set to true if the action node should contain within its ROS2 parameters, the agent id, entailing
that only actions that contains within their arguments the agent_id will trigger and progress the enforcement of the action via the logic defined
in this Action Executor node |
Action Executor progress/status methods
float advanceWork()
Any BDIActionExecutor node must implement the virtual method float advanceWork()
specified in the
provided abstract class from which it's inheriting. The call will be regularly called respecting the value of the corresponding init.
parameters passed in the constructor ( i.e. working_freq
) and it's supposed to withold the logic responsible for the action advancement operations, returning a float value
in the [0-1] range which expresses how much progress has been made in that step. The latter starts from 0.0
when the action execution is triggered and advance toward completion (i.e. 1.0) through the cumulative returned value of the
advanceWork()
call. Reaching 1.0 will always be considered a successful execution. Immediate success or failure can be triggered too, regardless of the current progress value and/or the one for the "step" just executed.
Within the method implementation, any of the API methods (status/communication) provided below can be called.
std::vector<std::string> getArguments()
returns arguments of the actions
float getProgress()
returns current progress state of the action in the [0-1] range
void execSuccess()
Communicates action execution has successfully completed; no additional log provided
void execSuccess(const std::string& success_log)
Parameter | Description |
---|---|
success_log |
additional log for the successfully execution |
Communicates action execution has successfully completed; additional log provided
void execFailed()
Communicates action execution has aborted; no additional log provided
void execFailed(const std::string& err_log)
Parameter | Description |
---|---|
error_log |
additional log for the failed execution |
Communicates action execution has aborted; additional log provided
Action Executor communication methods
BDICommunications::CheckBeliefResult
sendCheckBeliefRequest(const std::string& agent_ref, const ros2_bdi_interfaces::msg::Belief& belief)
Parameter | Description |
---|---|
agent_ref |
agent_id of the ROS2-BDI agent that is going to be queried with the request |
belief |
belief sent into the check request |
returns whether update request has been accepted and belief has been found
BDICommunications::UpdBeliefResult
sendUpdBeliefRequest(const std::string& agent_ref, const ros2_bdi_interfaces::msg::Belief& belief, const BDICommunications::UpdOperation& op)
Parameter | Description |
---|---|
agent_ref |
agent_id of the ROS2-BDI agent that is going to be queried with the request |
belief |
belief sent into the update request |
op |
specify whether the belief should be added or deleted; acceptable values among {ADD, DEL} |
returns whether update request has been accepted and performed
BDICommunications::CheckDesireResult
sendCheckDesireRequest(const std::string& agent_ref, const ros2_bdi_interfaces::msg::Desire& desire)
Parameter | Description |
---|---|
agent_ref |
agent_id of the ROS2-BDI agent that is going to be queried with the request |
desire |
desire sent into the check request |
returns whether update request has been accepted and desire has been found
BDICommunications::UpdDesireResult
sendUpdDesireRequest(const std::string& agent_ref, const ros2_bdi_interfaces::msg::Desire& desire, const BDICommunications::UpdOperation& op, const bool& monitor_fulfill)
Parameter | Description |
---|---|
agent_ref |
agent_id of the ROS2-BDI agent that is going to be queried with the request |
desire |
desire sent into the update request |
op |
specify whether the desire should be added or deleted; acceptable values among {ADD, DEL} |
monitor_fulfill |
specify whether the action should monitor the fulfillment of the desire (in case it is added through the update) |
returns whether update request has been accepted and performed
bool
isMonitoredDesireFulfilled(const std::string& agent_ref, const ros2_bdi_interfaces::msg::Desire& desire)
Parameter | Description |
---|---|
agent_ref |
agent_id of the ROS2-BDI agent that has been already queried with the corresponding ADD desire request |
desire |
desire sent into the update request |
returns true if the desire has been fulfilled, false in any other case
Action init. ROS2 parameters
Parameter | Type | Description | Default |
---|---|---|---|
- | - | - | - |