Правильний спосіб використання архітектури підписки з роботом C


11

Останнім часом я багато читав про архітектуру підписки, і є кілька різних способів, як люди, як вважають, виступають.

Наприклад, деякі люди використовують глобальну змінну "прапор", щоб мати завдання взяти під контроль. Інші використовують endTimeSlice()та дозволяють арбітру реально вибирати. І я вважаю, що це правильно.

У мене є цей невеликий розділ коду RobotC, над яким я працюю для рядка, що слідує за роботом, але я не впевнений, що я це роблю правильно, оскільки в даний час метод треку завжди переймає метод пошуку. Правильний потік повинен бути таким, що знахідка повинна спрямовувати робота до лінії, використовуючи спіральний шлях для пошуку лінії. Після того, як лінія знайдена, трек повинен перейти.

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);
    }
}

Я просто використав тут деякі коментарі, а не власне код, щоб тримати його коротким. Мої, якщо заяви недостатньо хороші як умови, тому що, коли робот виходить з лінії, track()переймає це. Це пов'язано з іншим твердженням в межах треку? Якщо так, то як track()виконувати повороти, коли вона втрачає лінію, не переймаючи з корму на початку програми?


1
Цифри, які ви даєте StartTask, чи є вони пріоритетним завданням? 9 буде найвищим пріоритетом? У такому випадку findпріоритет не повинен мати track? Насправді, умова findта elseстан Росії trackоднакові. Отже, як людина, якщо б значення датчика було більшим за поріг, що б ви зробили? Піти по спіралі чи повернути, щоб відрегулювати лінію?
Шахбаз

Я знаю, що таке архітектура підзарядки, і я впевнений, що у ваших роботів повинні бути різні апаратні модулі, відповідальні за обчислювальні завдання низького рівня. І що код насправді повинен поглинати сигнал, а не просто слухати для умовного стану. Тож чи не слід вам передавати сигнал на інший апарат? Тепер моя проблема полягає в тому, що у мене є два Arduinos, і я хочу, щоб Arduino A передавав або гальмував сигнал від Arduino B. Хтось має уявлення, як це робиться? Оскільки я можу підключити Arduino A, щоб надіслати сигнал до Arduino B, але я не знаю, як подати або перешкодити сиг
Ambient Soda

Відповіді:


7

Завдяки архітектурі підписки вам слід ретельно розробити поведінку таким чином, що якщо ви призначите 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
    }
}

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


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 з пріоритетами, вони не є хорошим вибором для реалізації субсидії на моєму досвіді. З таких причин, як пріоритети не завжди виконуються, завдання не можна переривати (іноді), тому, коли трапляється подія з більш високим пріоритетом, вона не реагуватиме так, як ви очікували, роботC тільки нещодавно став повторним учасником, тому такі речі, як доступ до датчика з більш ніж 1 завдання може бути ризикованим (проблеми з I2C від часу), а в деяких випадках - немає (автоматично опитувані датчики).

Ви можете додати свою власну пріоритетну реалізацію до вищевказаного циклу, коли ви працюєте, але це дійсно не потрібно для запуску.

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

Інша річ, з якою я вас залишу, - це те, що передбачення, будучи охайним і підходящим для багатьох речей, не є гарним способом реалізації того, що краще робити традиційно. Дійсно, частина «ухилення» може бути хорошим кандидатом на отримання субсидії, але, чесно кажучи, ваше інше завдання слід назвати «GoOn AboutYourBusiness». Я говорю це тому, що ви, мабуть, не хочете переходити від пошуку до наступного з підрахунком. Обробляйте з тими традиційними петлями програмування. З одним датчиком, - чи відчуття світла темніше чи світліше, ніж у останньому циклі? якщо він темніший (припускаючи чорну лінію), продовжуйте повертати той самий напрямок, якщо світліше повернути в інший бік, якщо він залишився колишнім, ідіть прямо. Вам, мабуть, потрібно додати деякий 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
    }
}

Я згоден, що цю проблему легше вирішити простою петлею. Я не розумію, чому хтось би спротив це.
Шахбаз

Я не хочу залишати враження, що їх простіше вирішити простою петлею, а швидше враження, що це правильне використання підзарядки для використання простого циклу як одного із завдань. Хто б не знизив рівень, він має модні пункти, і не має розуміння субсидії. Ви не виявите, що не так багато людей, які роблять підписку на LEGO NXT (мається на увазі за допомогою robotC), тому не очікуйте, що код буде легко доступний для вставки.
Spiked3

Так, мені так само цікаво, чому ОП використовував завдання для чогось такого простого, як субсідування.
Rocketmagnet

Тому що це дуже-дуже-дуже-дуже поширена помилка початківця з роботомC - намагатися використовувати завдання для всього. Я б хотів, щоб вони перемістили його лише до передової області.
Spiked3
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.