Robot CでSubsumption Architectureを使用する正しい方法


11

私は最近、Subsumption Architectureについてたくさん読んでいますが、人々が主張しているように見える方法はいくつかあります。

たとえば、一部の人々はグローバルな「フラグ」変数を使用して、タスクが制御を取得します。他のものはendTimeSlice()を使用し、アービターが本当に選択できるようにします。そして、これは正しいと思います。

行追跡ロボットのために取り組んでいるこのRobotCコードの小さなセクションがありますが、現在トラックメソッドは常にfindメソッドを引き継ぐので、正しく実行しているかどうかはわかりません。正しい流れは、findがラインを見つけるためにらせん状のパスを使用してロボットをラインに導くことです。ラインが見つかったら、トラックが引き継ぎます。

task evade(){
    if(SensorValue(forwardSonarSensor) > threshold){
            //box the obstruction
    }
}

task find(){
    if(SensorValue(lightSensor) > threshold){
            //spiral the robot
    }
}

task track(){

    if(SensorValue(lightSensor) < threshold){
            //go straight
    }else{
                //execute turns to follow the line
    }
}

task main(){
    while(true){
        StartTask(evade,9);
        StartTask(track,8);
        StartTask(find,7);
        wait1Msec(250);
    }
}

ここでは、簡潔にするために、実際のコードではなくコメントをいくつか使用しています。私のifステートメントは、ロボットがオフラインになるとtrack()引き継ぐため、条件として十分ではありません。これはトラック内のelseステートメントによるものですか?もしそうならtrack()、プログラムの開始時に飼料から引き継ぐことなくラインを失うときにターンを実行するにはどうすればよいですか?


1
あなたが与える数字StartTaskは、タスクの優先順位ですか?9が最優先されますか?その場合、findよりも優先すべきではありませんtrackか?実際、の状態findとのelse状態はtrack同じです。それで、人間として、センサー値がしきい値よりも大きい場合、どうしますか?スパイラルに行くか、ラインを調整するために回転しますか?
Shahbaz

私は包摂アーキテクチャが何であるかを知っており、あなたのロボットには低レベルの計算タスクを担当するさまざまなハードウェアモジュールが必要です。そして、コードは実際には、条件付き状態をリッスンするだけでなく、信号を吸収する必要があります。では、信号を別のハードウェアに渡すべきではありませんか?私の問題は、2つのArduinoがあり、Arduino AにArduino Bからの信号を取り込んだり抑制したりすることです。これがどのように行われるかについて誰かが何か考えを持っていますか?Arduino Aを配線してArduino Bに信号を送信することができますが、sigを取り込んだり禁止したりする方法がわかりません
Ambient Soda

回答:


7

サブサンプションアーキテクチャでは、タスクのT優先度を割り当てる場合にnT優先度よりも高い優先度のすべてのタスクnが無視された場合にロボットが実行するように、動作を注意深く設計する必要があります。

タスクの例を注文して、それを実装する方法を見つけましょう。あなたのタスクはevadefindtrack

一般的に、ロボットにラインを追跡させたいでしょう。ただし、ラインを検出できなかった場合は、ラインを見つけようとします。とりわけ、それは障害を回避する必要があります。これにより、次の順序になります。

  • 最優先: evade
  • 次に: find
  • 次に: track

その理由は、findより高い優先順位があるtrack、私は上記のように、あなたが希望、それがあるtrack場合にのみ、evadeかつfind不要ですが。findtrackに置くと、ライン上にいなくても障害物がなければ追跡を開始します。

次に、実装を見てみましょう。

task find(){
    if(SensorValue(lightSensor) > threshold){
            //spiral the robot
    }
}

task track(){

    if(SensorValue(lightSensor) < threshold){
            //go straight
    }else{
                //execute turns to follow the line
    }
}

findより高い優先度を与えたことを忘れないでください。したがって、ロボットがを感知できない場合はlightSensor、ラインを見つけるためにスパイラルになります。一度実行すると、track起動します。ご覧のように、決して発生しないelse状態ですtrack

これが機能している間、ロボットは非常にぎこちなく動きます。ロボットの現在のビルドを考えると、実際にできることは多くありません。


私はすでにあなたの質問に答えましたが、ここにあなたのライントラッキングの簡単な改善があります:

1つの光センサーの代わりに、2つを使用します。ls_leftおよびls_right。(少なくとも)2つのセンサーを使用して、完全に軌道から外れているか、または軌道から出ようとしているのかを理解できます。2番目のケースでは、簡単に適切な方向に向きを変えて軌道に戻ることができます。

あなたのfind仕事は似ています:

task find(){
    if (SensorValue(ls_left) > threshold
        && Sensorvalue(ls_right) > threshold){
            //spiral the robot
    }
}

つまり、まったく何も感じない場合にのみスパイラルになります

あなたのtrack仕事は、今より効率的になります。

task track(){

    if (SensorValue(ls_left) < threshold
        && SensorValue(ls_right) < threshold){
            //go straight
    } else if (SensorValue(ls_left) < threshold
        && SensorValue(ls_right) > threshold){
            //turn left
    } else if (SensorValue(ls_left) > threshold
        && SensorValue(ls_right) < threshold){
            //turn right
    } else {
            // shouldn't happen, but go on spiral anyway
    }
}

明らかに、光センサーのマトリックスを使用すると、トラックからどれだけひどく外れているか(つまり、どの角度で)を判断し、トラックに戻る方法(つまり、角速度で)をより適切に決定できます。


4

短い答え; 実際には、まったく異なることをする必要はありません。

長い不完全な答え。robotCに適した擬似コードをいくつか紹介します。これにより、より良い方法に進むことができます。まず、タスクを使用しないでください-これはrobotCタスクの目的ではありません。それらはおそらく動作するように作られているかもしれませんし、そうでないかもしれません(試してみるためにもかなりの数の変更が必要です)。

// global variables
int distance;
int light;

main() {
   while (true) {
   distance = read_distance;
   light = read_light;
   if (task1_wantsToRun())
     task1_run();
   if (task2_wantsToRun())
     task2_run();   
   }
}

ここにはいくつかの事柄があります。優先度は無関係になります。robotCに優先度のあるタスクがあるように見えるので、私の経験では、包摂の実装には適していません。優先度が常に守られるとは限らない、タスクを中断できない(時々)などの理由により、優先度の高いイベントが発生した場合、期待どおりに反応しない、robotCが最近再入可能になったため、センサーへのアクセスなど2つ以上のタスクからは危険な場合があり(I2Cタイミングの問題)、場合によっては危険ではありません(自動的にポーリングされるセンサー)。

物事が機能するように、独自の優先度実装を上記のループに追加できますが、実際には開始に必要ありません。

あなたのコメント「// box the obstruction」は弾道の振る舞いを説明しています。これらは、マルチタスクを使用して実装するのが少し難しいです。私が使用した単純なループは、それをはるかに簡単にし、初心者/学習にとってより良いものにします。

私があなたに残しているもう1つのことは、包摂は多くのことに対してきちんとしていて適切である一方で、伝統的に行われている方法を実装するための良い方法ではないということです。実際、「回避」の部分は包摂の良い候補かもしれませんが、正直なところ、他のタスクは「GoOnAboutYourBusiness」と呼ばれるべきです。これは、おそらく検索から包摂を伴うフォローに変更したくないためです。従来のプログラミングループでそれらを処理します。単一のセンサーを使用した場合-感知された光は、最後のループよりも暗いですか、それとも明るいですか?暗くなった場合(黒い線を想定)、同じ方向に回転し続けます。明るくなった場合は、反対方向に回転します。同じままの場合は、直進します。スムーズにするために、左右に曲がるのではなく、PIDを追加してステアリングカーブを使用する必要があります。

そして、はい、複数のセンサーが役立ちます。http://www.mindsensors.com/- ええ、それは現在映画の中で私です(11/10/2012)

更新:実際のコード

私はしばらくしてこれを試しますが、コンパイルして上で書いたものを示しています:

#pragma config(Sensor, S1,     S_LIGHT,        sensorLightActive)
#pragma config(Sensor, S2,     S_DISTANCE,     sensorSONAR)
#pragma config(Motor,  motorB,          LEFT,          tmotorNXT, PIDControl, encoder)
#pragma config(Motor,  motorC,          RIGHT,         tmotorNXT, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

int distance_value, light_value;

bool evade_wantsToRun()
{
    return distance_value < 30;
}

void evade_task()
{
    // full stop
    motor[LEFT] = 0;        
    // evade the object ballistically (ie in full control)  
    // turn left, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = -20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn right, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn right, drive
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    nSyncedTurnRatio = 100;
    Sleep(1000);
    // turn left, resume
    nSyncedTurnRatio = 0;
    motor[LEFT] = 20;
    Sleep(500);
    motor[LEFT] = 0;
}

///////////////////////////////

void TurnBySteer(int d)
{
    // normalize -100 100 to 0 200
    nSyncedTurnRatio = d + 100; 
}
///////////////////////////////

typedef enum programPhase { starting, searching, following, finished };
programPhase phase = starting;

// these 'tasks' are called from a loop, thus do not need to loop themselves

void initialize()
{
    nSyncedTurnRatio = 50;
    nSyncedMotors = synchBC;
    motor[LEFT] = 30;       // start a spiral drive
    phase = searching;
}

void search()
{
    if (light_value < 24)
    {
        nSyncedTurnRatio = 100;
        phase = following;
    }
}

int lastLight = -1;
int currentSteer = 0;
void follow()
{
    // if it is solid white we have lost the line and must stop
    // if lightSensors detects dark, we are on line
    // if it got lighter, we are going more off line
    // if it got darker we are headed in a good direction, slow down turn in anticipation
    // +++PID will be even smoother
    if (light_value > 64)
    {
        motor[LEFT] = 0;
        phase = finished;
        return;
    }
    if (light_value < 24)
        currentSteer = 0;
    else if (light_value > lastLight)
        currentSteer += sgn(currentSteer) * 1;
    else    // implied (light_value < lastLight)
        currentSteer -= sgn(currentSteer) * 1;      

    TurnBySteer(currentSteer);
}

bool regularProcessing_wantsToRun()
{
    return phase != finished;
}

void regularProcessing_task()
{
    switch (phase)
    {
    case starting:
        initialize();
        break;
    case searching:
        search();
        break;
    case following:
        follow();
    }
}

task main()
{
    // subsumption tasks in priority oder
    while (true)
    {
        // read sensors once per loop
        distance_value = SensorValue[S_DISTANCE];
        light_value = SensorValue[S_LIGHT];
        if (evade_wantsToRun())
            evade_task();
        if (regularProcessing_wantsToRun())
            regularProcessing_task();
        else
            StopAllTasks();
        EndTimeSlice();     // give others a chance, but make it as short as possible
    }
}

この問題は単純なループで簡単に解決できることに同意します。なぜ誰がこれを反対票を投じるのか、私にはわかりません。
Shahbaz

単純なループで解決する方が簡単だという印象を残したくないのではなく、単純なループをタスクの1つとして使用することが包摂の正しい使用であるという印象を残したくありません。ダウングレードした人には誰でもmodポイントがあり、包摂についての理解がありません。LEGO NXT(robotCを使用することで暗示される)で包摂を行う人はそれほど多くないので、コードを簡単に貼り付けることができるとは期待しないでください。
Spiked3

はい、私はなぜOPが包摂のような単純なもののためにタスクを使用していたのか疑問に思っています。
Rocketmagnet

それは、robotCでの非常に非常に非常に非常に一般的な初心者の間違いであるためです。彼らがそれを高度な専用エリアに移動してくれることを願っています。
Spiked3
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.