https://github.com/introlab/hbba_lite

Hybrid Behavior Based Architecture Lite Library

https://github.com/introlab/hbba_lite

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (11.7%) to scientific vocabulary
Last synced: 5 months ago · JSON representation

Repository

Hybrid Behavior Based Architecture Lite Library

Basic Info
  • Host: GitHub
  • Owner: introlab
  • License: gpl-3.0
  • Language: C++
  • Default Branch: ros2
  • Size: 200 KB
Statistics
  • Stars: 1
  • Watchers: 3
  • Forks: 0
  • Open Issues: 2
  • Releases: 0
Created almost 4 years ago · Last pushed about 1 year ago
Metadata Files
Readme License

README.md

hbba_lite

This is a lite version of HBBA.

For ROS1, please see the ros1 branch.

The main differences are :

  • Desires cannot have dependencies;
  • All Motivations modules are inside the same ROS node;
  • The filters can be integrated in ROS nodes;
  • Two kinds of filters exist: on/off and throttling;
  • It use Gecode instead of OR-Tools for the solver.

Dependencies

These dependencies need to be installed, in addition to ROS:

bash sudo apt install -y libgecode-dev

Diagram

The following diagram illustrates how HBBA Lite works.

HBBA Lite Diagram

Documentation

A hybrid robot architecture means that perceptual nodes can communicate with the behavior nodes and the planning modules.

Definitions

Filters

In order to disable a node, a HBBA filter blocks all messages. Also, a filter can reduce CPU usage by letting pass 1 message out of N. An on-off filter lets pass all messages or blocks all messages. A throttling filter lets pass 1 message out of N or blocks all messages.

Perceptual Nodes

The perceptual nodes process sensor data to extract useful information from it. Generally, the subscriber has an on-off filter for audio data or a throttling filter for image data.

Behavior Nodes

The behavior nodes publish messages to perform actions. For example, a behavior node can move the robot in order to follow a person. Generally, the action message publisher has an on-off filter to disable the behavior.

Resources

A resource represents something limited in the robot. For example, a resource can be declared for each actuator to prevent multiple behavior nodes to use the same actuator. Also, a resource can be declared to represent the available processing time in percent.

Desire

A desire represents something to be fulffilled by the robot. A desire has an intensity value that representes the level of priority of the desire. Desires with the highest intensity are priotized. Also, it may have parameters used by the strategy.

Strategy

A strategy represents a way to fulfill a desire. The strategy will accomplish the desire once the strategy is activated. At least one strategy is required for each desire type. If there are many strategies for a desire type, the strategy with the highest utility is prioritized. A strategy has a list of filters to enable and a list of resources required to be enabled. The resources can be used to prevent conflicts of actuators between strategies. If resources are used to manage the processing time in percent, the strategy resources can contain the estimated processing time in percent. So, HBBA will manage the processing time of the robot.

Desire Set

The desire set contains the current desires to fulfill. An observer pattern is implemented in the desire set to get notified of changes.

Motivations

The motivations modify the content of the desire set to make the robot do something useful. Generally, the motivations subscribe to perceptual nodes in order to add or remove desires with respect to the robot surrounding.

Solver

Once the solver gets the content of the desire set, it will find the best desires to fulfill with the best strategies.

The solver steps are: 1. For each type of desire in the set, find the most intense desire. 2. Find the desire-strategy combinations that maximize the sum of intensity-utility products that respect the resource constraints.

Creating Motivation Modules

See the demos for the T-Top robot.

Creating Desires

The following example shows how to declare a simple desire that does not have any parameter. ```cpp class Camera3dRecordingDesire : public Desire { public: explicit Camera3dRecordingDesire(uint16_t intensity = 1) : Desire(intensity) {}

~Camera3dRecordingDesire() override = default;

// The following macro overrides the required methods to declare a Desire.
DECLARE_DESIRE_METHODS(Camera3dRecordingDesire)

// The macro is expanded to :
// std::unique_ptr<Desire> clone() override { return std::make_unique<Camera3dRecordingDesire>(*this); }                            \
// DesireType type() override { return DesireType::get<Camera3dRecordingDesire>(); }

}; ```

The following example shows how to declare a desire that contains a parameter. ```cpp class TalkDesire : public Desire { // Declare the desire parameters here. std::string m_text;

public: // Add the desire parameters as arguments of the constructor. explicit TalkDesire::TalkDesire(string text, uint16t intensity = 1) : Desire(intensity), mtext(move(text)) {} ~TalkDesire() override = default;

// The following macro overrides the required methods to declare a Desire.
DECLARE_DESIRE_METHODS(TalkDesire)

// Add the getters of the desire parameters here.
const std::string& text() const { return m_text; }

}; ```

Creating Strategies

Strategies that only change the state of filters do not require creating a subclass of the strategy class. The following example shows how to create the strategy for the Camera3dRecordingDesire previously defined. cpp // The template argument of the class declares the desire type associated with the strategy. auto strategy = make_unique<Strategy<Camera3dRecordingDesire>>( utility, // The utility of the strategy unordered_map<string, uint16_t>{}, // Declare the resources used by the strategy unordered_map<string, FilterConfiguration>{ {"video_recorder_camera_3d/filter_state", FilterConfiguration::onOff()}}, // Declare the filters to enable and their type. move(filterPool)); // The instance of the class to change the filter state. Generally, the previous code is put inside a function to simplify its use in other projects. The association between the desire and the strategy is done with the template argument of the Strategy class.

The following example shows how to create a subclass in order to publish a message when the strategy is enabled. Also, the strategy removes the desire of the set once the desire is completed. ```cpp // The template argument of the Strategy class declares the desire type associated with the strategy. class TalkStrategy : public Strategy { std::sharedptr mdesireSet; std::sharedptrrclcpp::Node mnode; rclcpp::Publisher::SharedPtr mtalkPublisher; rclcpp::Subscription<behaviormsgs::msg::Done>::SharedPtr m_talkDoneSubscriber;

public: TalkStrategy( uint16t utility, std::sharedptr filterPool, std::sharedptr desireSet, std::sharedptrrclcpp::Node node) : Strategy( utility, {{"sound", 1}}, {{"talk/filterstate", FilterConfiguration::onOff()}}, move(filterPool)), mdesireSet(move(desireSet)), mnode(move(node)) { mtalkPublisher = node->createpublisher<behaviormsgs::msg::Text>( "talk/text", rclcpp::QoS(1).transientlocal()); mtalkDoneSubscriber = node->createsubscription<behaviormsgs::msg::Done>( "talk/done", 1, this { talkDoneSubscriberCallback(msg); }); }

DECLARE_NOT_COPYABLE(TalkStrategy);
DECLARE_NOT_MOVABLE(TalkStrategy);

StrategyType strategyType() override
{
    return StrategyType::get<TalkStrategy>();
}

protected: void onEnabling(const TalkDesire& desire) override { behaviormsgs::msg::Text msg; msg.text = desire.text(); msg.id = desire.id(); mtalkPublisher->publish(msg); }

private: void talkDoneSubscriberCallback(const behaviormsgs::msg::Done::SharedPtr msg) { if (msg->id == desireId()) { mdesireSet->removeDesire(msg->id); } } }; ```

Using the Desire Set

The following subsections show how to use the desire set. The DesireSet class is thread-safe, so it can be called by any thread. Once a change has been made to the desire set, the solver will be run to update the filter states. To prevent this behavior, a transaction can be created, so the solver will be run when the transaction is destroyed.

Add a Desire

cpp auto desire = make_unique<TalkDesire>("The text to say"); // Create the desire. desireSet->addDesire(std::move(desire)); // Add the desire to the set.

If you don't need to have access to the desire instance, there is a simplified syntax. cpp auto id = desireSet->addDesire<TalkDesire>("The text to say"); // Create the desire, add the desire to the set and return the id.

Remove a Desire

cpp desireSet->remove(id); // Remove the desire that has the provided id.

Remove All Desires

cpp desireSet->clear(); // Remove all desires

Remove All Desires of a Specific Type

cpp desireSet->removeAllDesiresOfType(DesireType::get<TalkDesire>()); // Remove all TalkDesire instances.

A simplied syntax exists. cpp desireSet->removeAllDesiresOfType<TalkDesire>(); // Remove all TalkDesire instances.

Check Whether the Set Contains a Desire

cpp desireSet->contains(id); // Return a boolean indicating whether the set contains a desire that has the provided id.

Check Whether the Set Contains a Desire of a Specific Type

cpp // Return a boolean indicating whether the set contains a desire of type TalkDesire. desireSet->containsAnyDesiresOfType(DesireType::get<TalkDesire>());

A simplied syntax exists. cpp // Return a boolean indicating whether the set contains a desire of type TalkDesire. desireSet->containsAnyDesiresOfType<TalkDesire>();

How to Create a Transaction

The following example shows how to use transactions.

```cpp { auto transaction = desireSet->beginTransaction();

// Change the desire set here.

} // When the transaction is destroyed, the changes will be applied. ```

How to Use the Observer Pattern

The following example shows how to create and add an observer to the desire set.

```cpp

include

include

class LogDesireSetObserver : public DesireSetObserver { public: void onDesireSetChanged(const std::vectorstd::unique_ptr<Desire>& desires) override { for (auto& desire : desires) { std::cout << desire->type().name() << " "; } std::cout << std::endl; } };

int main(int argc, char** argv) { LogDesireSetObserver observer; auto desireSet = make_shared(); // Create the desire set. desireSet->addObserver(&observer);

// All changes to the desire will be logged to the terminal.
return 0;

} ```

Initializing HBBA Lite

The following examples show how to initialize the HBBA Lite.

Normal Usage

```cpp constexpr bool WAITFORSERVICE = true;

auto desireSet = makeshared(); // Create the desire set. // Create the filter pool useful to change the filter states. // If WAITFORSERVICE is true, the pool will wait until the service become available. auto filterPool = makeshared(nodeHandle, WAITFORSERVICE);

vector> strategies; // Add the strategies related to the application into the vector.

auto solver = make_unique(); // Create the solver. HbbaLite hbba(desireSet, move(strategies), {{"sound", 1}}, // The resource available on the robot. move(solver)); // The constructor starts a thread for the solver.

// Add desires to the set. ```

Strategy State Logger Usage

To log the strategy state changed of the strategy, you can use the RosLogStrategyStateLogger class.

```cpp constexpr bool WAITFORSERVICE = true;

auto desireSet = makeshared(); // Create the desire set. // Create the filter pool useful to change the filter states. // If WAITFORSERVICE is true, the pool will wait until the service become available. auto filterPool = makeshared(nodeHandle, WAITFORSERVICE);

vector> strategies; // Add the strategies related to the application into the vector.

auto solver = makeunique(); // Create the solver. HbbaLite hbba(desireSet, move(strategies), {{"sound", 1}}, // The resource available on the robot. move(solver), // The constructor starts a thread for the solver. makeunique());

// Add desires to the set. ```

Filter State Logger Usage

To log the filter state changed of the strategy, you can use the RosLogFilterPoolDecorator class.

```cpp constexpr bool WAITFORSERVICE = true;

auto desireSet = makeshared(); // Create the desire set. // Create the filter pool useful to change the filter states. // If WAITFORSERVICE is true, the pool will wait until the service become available. auto rosFilterPool = makeunique(nodeHandle, WAITFORSERVICE); auto filterPool = make_shared(move(rosFilterPool));

vector> strategies; // Add the strategies related to the application into the vector.

auto solver = make_unique(); // Create the solver. HbbaLite hbba(desireSet, move(strategies), {{"sound", 1}}, // The resource available on the robot. move(solver)); // The constructor starts a thread for the solver.

// Add desires to the set. ```

Adding a Filter to C++ Nodes

Subscriber

```cpp

include

include

include

void callback(const stdmsgs::msg::Int8::ConstPtr& msg) { ROSINFO("Data received : %i", static_cast(msg->data)); }

int main(int argc, char** argv) { rclcpp::init(argc, argv); auto node = rclcpp::Node::makeshared("nodename");

// Replace this
auto sub = node->create_subscription<std_msgs::msg::Int8>("int_topic", 10, callback);

// with this to add an on/off filter
OnOffHbbaSubscriber<std_msgs::msg::Int8> sub(node, "int_topic", 10, &callback);

// with this to add a throttling filter
ThrottlingHbbaSubscriber<std_msgs::msg::Int8> sub(node, "int_topic", 10, &callback);
rclcpp::spin(node);

return 0;

} ```

Publisher

```cpp

include

include

include

int main(int argc, char** argv) { rclcpp::init(argc, argv); auto node = rclcpp::Node::makeshared("nodename");

// Replace this
auto pub = node->create_publisher<std_msgs::msg::Int8>("int_topic", 10);

// with this to add an on/off filter
OnOffHbbaPublisher<std_msgs::msg::Int8> pub(node, "int_topic", 10);

// with this to add a throttling filter
ThrottlingHbbaPublisher<std_msgs::msg::Int8> pub(node, "int_topic", 10);

return 0;

}

```

Adding a Filter to Python Nodes

Subscriber

```python import rospy from stdmsgs.msg import Int8 import hbbalite

def callback(data): rospy.loginfo('Data received : {}'.format(data.data))

def main(): rospy.initnode('nodename')

# Replace this
sub = rospy.Subscriber("int_topic", Int8, callback)

# with this to add an on/off filter
sub = hbba_lite.OnOffHbbaSubscriber('int_topic', Int8, callback)

# with this to add a throttling filter
sub = hbba_lite.ThrottlingHbbaSubscriber('int_topic', Int8, callback)

rospy.spin()

if name == 'main': main()

```

Publisher

```python import rospy from stdmsgs.msg import Int8 import hbbalite

def main(): rospy.initnode('nodename')

# Replace this
pub = rospy.Publisher('int_topic', Int8, queue_size=10)

# with this to add an on/off filter
pub = hbba_lite.OnOffHbbaPublisher('int_topic', Int8, queue_size=10)

# with this to add a throttling filter
pub = hbba_lite.ThrottlingHbbaPublisher('int_topic', Int8, queue_size=10)

if name == 'main': main()

```

Nodes

arbitration_node

This node applies priority arbitration to topics.

Parameters

  • topics (string): The topic names.
  • priorities (int): The priority for each topic (int, lower means higher priority).
  • timeout_s (double): The timeout in seconds for each topic.

Published Topics

  • out (Any): The output topic.

on_off_hbba_filter_node

This node applies an on/off filter on a topic.

Parameters

  • input_topic (string): The input topic name.
  • output_topic (string): The filtered topic name.
  • state_service (string): The service name to change the filter state.

Topics

  • Defined by the parameter input_topic (Any): The input topic.
  • Defined by the parameter output_topic (Any): The filtered topic.

Services

throttling_hbba_filter_node

This node applies a throttling filter on a topic.

Parameters

  • input_topic (string): The input topic name.
  • output_topic (string): The filtered topic name.
  • state_service (string): The service name to change the filter state.

Topics

  • Defined by the parameter input_topic (Any): The input topic.
  • Defined by the parameter output_topic (Any): The filtered topic.

Services

Owner

  • Name: IntRoLab
  • Login: introlab
  • Kind: organization
  • Location: Sherbrooke, Québec, Canada

IntRoLab - Intelligent / Interactive / Integrated / Interdisciplinary Robot Lab @ Université de Sherbrooke

GitHub Events

Total
  • Issues event: 1
  • Delete event: 1
  • Push event: 9
  • Pull request review event: 2
  • Pull request review comment event: 1
  • Pull request event: 4
  • Create event: 2
Last Year
  • Issues event: 1
  • Delete event: 1
  • Push event: 9
  • Pull request review event: 2
  • Pull request review comment event: 1
  • Pull request event: 4
  • Create event: 2

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 2
  • Total pull requests: 23
  • Average time to close issues: N/A
  • Average time to close pull requests: 1 day
  • Total issue authors: 2
  • Total pull request authors: 3
  • Average comments per issue: 0.0
  • Average comments per pull request: 0.0
  • Merged pull requests: 23
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 1
  • Pull requests: 3
  • Average time to close issues: N/A
  • Average time to close pull requests: 8 days
  • Issue authors: 1
  • Pull request authors: 2
  • Average comments per issue: 0.0
  • Average comments per pull request: 0.0
  • Merged pull requests: 3
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • mamaheux (1)
  • doumdi (1)
Pull Request Authors
  • mamaheux (18)
  • philippewarren (5)
  • doumdi (2)
Top Labels
Issue Labels
Pull Request Labels

Dependencies

.github/workflows/pull-request-audit.yaml actions
  • actions/checkout v2 composite
  • ros-tooling/setup-ros v0.2 composite
setup.py pypi