Правильный способ использования Subsuration Architecture с Robot C

11

В последнее время я много читал о Subsuration Architecture, и есть несколько разных способов, которыми люди, похоже, выступают.

Например, некоторые люди используют глобальную переменную «flag», чтобы взять задачу под контроль. Другие используют endTimeSlice()и позволяют арбитру действительно выбирать. И я думаю, что это правильно.

У меня есть небольшой раздел кода RobotC, над которым я работаю для робота, следующего за линией, но я не уверен, что все делаю правильно, так как в настоящее время метод track всегда принимает метод 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одинаковы. Итак, как человек, если бы значение датчика превышало пороговое значение, что бы вы сделали? Идти по спирали или повернуть, чтобы отрегулировать линию?
Шахбаз
Я знаю, что такое архитектура подчинения, и я уверен, что ваши роботы должны иметь разные аппаратные модули, отвечающие за вычислительные задачи низкого уровня. И что код должен фактически поглощать сигнал, а не просто слушать условное состояние. Так не следует ли передавать сигнал в другое устройство? Теперь моя проблема в том, что у меня есть два Arduinos, и я хочу, чтобы Arduino A включал или подавлял сигнал от Arduino B. У кого-нибудь есть идеи, как это сделать? Как я могу подключить Arduino A, чтобы послать сигнал Arduino B, но я не уверен, как включить или запретить sig
Ambient Soda

Ответы:

7

С Subsume Architecture вы должны тщательно проектировать свое поведение таким образом, чтобы, если вы назначаете Tприоритет задачи n, то это Tбыло то, что должен делать робот, если все задачи с более высоким приоритетом nигнорируются.

Давайте закажем ваши примеры задач, а затем выясним, как их реализовать. Ваши задачи evade, findи track.

В общем, вы бы хотели, чтобы робот отслеживал линию. Однако, если он не может обнаружить линию, он должен попытаться найти ее. Прежде всего, он должен избегать препятствий. Это дает нам следующий порядок:

  • Наивысший приоритет: evade
  • Затем: find
  • Затем: track

Причина findимеет более высокий приоритет, чем track, как я уже говорил выше, вы бы trackтолько, если evadeи findне нужны. Если вы поставите findниже track, это означает, что вы начнете отслеживать, если нет никаких препятствий, даже если вы не на линии.

Теперь давайте посмотрим на вашу реализацию:

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никогда не происходит.

Пока это работает, робот будет двигаться очень неловко. На самом деле вы ничего не можете с этим поделать, учитывая текущую сборку вашего робота.


Хотя я уже ответил на ваш вопрос, но вот простое улучшение отслеживания вашей линии:

Вместо одного датчика освещенности используйте два; ls_leftи ls_right. Используя (по крайней мере) два датчика, вы можете понять, находитесь ли вы полностью вне дорожки или собираетесь покинуть дорожку. Во втором случае вы можете легко повернуть в нужном направлении и вернуться на правильный путь.

Ваша 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
    }
}

Очевидно, что с матрицей датчиков освещенности вы можете лучше судить о том, насколько плохо вы выходите из колеи (то есть с каким углом), и лучше решить, как вернуться на трассу (то есть с какой угловой скоростью).

Shahbaz
источник
4

краткий ответ; нет, тебе действительно нужно делать что-то немного по-другому.

длинный неполный ответ; Позвольте мне дать вам некоторый код psuedo, подходящий для 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 только недавно стал повторно входящим, поэтому такие вещи, как доступ к датчику от более чем одной задачи может быть рискованно (проблемы синхронизации I2C), а в некоторых случаях это не так (датчики автоматически опрашиваются).

Вы можете добавить свою собственную реализацию приоритетов в вышеупомянутый цикл, как только все заработает, но это действительно не нужно для запуска.

Ваш комментарий "// помечаю препятствие" описывает баллистическое поведение. Это немного сложно реализовать с помощью многозадачности. Простой цикл, который я использовал, делает его намного проще и лучше для начинающих / обучения.

Еще одна вещь, с которой я вас оставлю, - это то, что потребление, будучи аккуратным и подходящим для многих вещей, не является хорошим способом реализовать то, что лучше сделать традиционно. В самом деле, часть «уклониться» может быть хорошим кандидатом для включения в категорию, но, честно говоря, ваша другая задача должна называться «GoOnAboutYourBusiness». Я говорю это потому, что вы, вероятно, не хотите переходить от поиска к следующему с подпоправкой. Обработайте их с помощью традиционных циклов программирования. С одним датчиком, - воспринимается ли свет темнее или светлее, чем это было в прошлом цикле? если он стал темнее (предполагается черная линия), продолжайте поворачивать в том же направлении, если он стал светлее, поверните в другую сторону, если он остался прежним, идите прямо. Вам, вероятно, нужно добавить PID и использовать кривую поворота вместо того, чтобы просто поворачивать влево и вправо, чтобы быть более плавным.

И да, несколько датчиков помогают. http://www.mindsensors.com/ - да, это я в фильме в настоящее время (10/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
    }
}
Spiked3
источник
Я согласен, что эту проблему легче решить с помощью простого цикла. Я не понимаю, почему кто-то может понизить это.
Шахбаз
Я не хочу оставлять впечатление, что его проще решить с помощью простого цикла, скорее, создается впечатление, что правильное использование подстановки - это использование простого цикла в качестве одной из задач. Кто бы ни понизил это, у него есть модовые пункты, и нет понимания погружения. Вы не обнаружите, что на LEGO NXT не так уж много людей, выполняющих подсчеты (подразумевается с помощью robotC), поэтому не ожидайте, что код будет легко доступен для вставки.
Spiked3
Да, мне так же интересно, почему ОП использовал задачи для чего-то столь же простого, как погружение.
Ракетный магнит
Потому что это очень-очень-очень-очень распространенная ошибка новичка с robotC - пытаться использовать задачи для всего. Я хотел бы, чтобы они переместили это в продвинутую только область.
Spiked3