クラウドエンジニアのノート

情報技術系全般,自分用メモを公開してます。

サンプション・アーキテクチャをC++で実装

サンプション・アーキテクチャとは

ロボットの制御などに用いられ、それぞれのモジュールの階層構造を管理するアーキテクチャです。
ある目標に向かって行動するために最も重要なモジュールが下層にあり、上位層になればなるほどより抽象的なモジュールが構成される形が一般的ですね。

自動運転で例えると、道順通り運転する最上位層もモジュールがあって、下層モジュールには、渋滞してたら回避するモジュールや、車が突っ込んで来たら回避するモジュール…というような構成です。

実際に私の研究のラジコンカーにも実装されております。

日本語wikipediaより英語のほうが詳しい説明があります。

ja.wikipedia.org en.wikipedia.org

また、今回の実装は以下のサイトにあるjavaのソースをc++に翻訳しました。 www.ibm.com

実装クラス

大きくわけて3つあります。 1. Behavior(基底クラス) 2. BehaviorBasedAgent(実行クラス) 3. main

注意点 - これから説明するソースにはCarというクラスがありますが、車の情報を管理するクラスだと思ってください(アクチュエータとかのアクセスもここから) - すべてヘッダオンリークラスです - 元々ソースにあったコメントは英語、処理の代わりとして日本語のコメント入れてます

ファイル構成

.
├── main.cpp
├── Car.h
├── BehaviorBasedAgent.h
├── subsumption
│   ├── Behavior.h
│   ├── Drive.h
│   └── Stop.h
  • Drive.h コース通り運転するモジュール
  • Stop.h 停止処理があるモジュール

として説明していきます。

Behavior 基底クラス

Behavior.h

#pragma once
#include "../Car.h"

// Must add "override" modifier !
class Behavior {
   protected:
    Car& car;

   public:
    // This constructor is supposed to be called from child class
    Behavior(Car& car_) : car(car_){};

    // virtual function
    virtual bool isActive() {
        throw std::runtime_error("Called base class @ Behavior.h isActive()");
        return false;
    }
    virtual void act() { throw std::runtime_error("Called base class @ Behavior.h act()"); }
};

実装はかんたんでisActive()にmoduleがactiveになる条件を記述して、act()に行動を記述します。

Behavior実装例(Stopクラス)

Stop.h

#pragma once
#include "../Car.h"
#include "Behavior.h"

class Stop : public Behavior {
   public:
    Stop(Car& car_) : Behavior(car_){}

    bool isActive() override {
        // ストップする条件を満たしたら(信号、コースアウト…)
        return true;
    }
    void act() override {
         // Carクラスを使って停止処理
    }
};

BehaviorBasedAgent クラス

BehaviorBasedAgent.h

#pragma once
#include "subsumption/Behavior.h"
#include "subsumption/Stop.h"

// - RelationMat
//   BehaviorRelationMat[i][j] : 行動優先度がi<jならば[i][j]=false
using BehaviorRelationMat = std::vector<std::vector<bool>>;
using Behaviors = std::vector<std::unique_ptr<Behavior>>;

class BehaviorBasedAgent {
   private:
    Behaviors& behaviors;
    BehaviorRelationMat suppresses;
    int current_behavior_index;

   public:
    BehaviorBasedAgent(Behaviors& behaviors_, BehaviorRelationMat suppresses_)
        : behaviors(behaviors_), suppresses(suppresses_), current_behavior_index(0) {}

    void step() {
        std::vector<bool> is_active(behaviors.size());

        for (size_t i = 0; i < behaviors.size(); i++) {
            is_active[i] = behaviors[i]->isActive();
        }
        bool ran_behavior = false;
        size_t counter = 0;
        while (!ran_behavior) {
            bool run_current_behavior = is_active[current_behavior_index];
            if (run_current_behavior) {
                for (size_t i = 0; i < suppresses[0].size(); i++) {
                    if (is_active[i] && suppresses[i][current_behavior_index]) {
                        run_current_behavior = false;

                        break;
                    }
                }
            }

            if (run_current_behavior) {
                if (current_behavior_index < static_cast<int>(behaviors.size())) {
                    // do action
                    behaviors[current_behavior_index]->act();
                }

                ran_behavior = true;
            }
            if (behaviors.size() > 0) {
                current_behavior_index = (current_behavior_index + 1) % behaviors.size();
                counter++;
                if(counter == behaviors.size()) break;
            }
        }
    }
};

main.cpp(インスタンスを持つ)

#include "BehaviorBasedAgent.h"

int main(int argc, const char** argv) {
    //他の処理
    ...

    // This instance deals with modules that act as actuators
    Behaviors actuator_module;
    actuator_module.push_back(std::unique_ptr<Behavior>(new Stop(car)));
    actuator_module.push_back(std::unique_ptr<Behavior>(new Drive(car)));
    BehaviorBasedAgent actuator_agent(actuator_module, {{false, true}, {false, false}});

    // control loop
    while(1){
        subsumption.step();
    }

    ...
    //他の処理
    ...
    return EXIT_SUCCESS;
}
  • BehaviorBasedAgentに他のファイルをincludeしてるので、mainではこのファイルだけで大丈夫です
  • あとは制御処理のループでstep()を呼んで上げれば勝手に動作します -actuator_agentを初期化するときに、関係の行列を一緒に定義します

ibmのサイトに詳しい説明があるのですが、それぞれモジュールの関係性を、行動優先度がi<jならば[i][j]=falseと定義します。
なのでこの行列の対角成分は自身との比較なのでfalseですね。
もしすべてのモジュールの関係性が、下層モジュール < 上層モジュルならば、下falseの三角行列になります。