Maneira correta de usar a arquitetura de subsunção com o robô C

11

Ultimamente, tenho lido muito sobre a Subsumption Architecture e existem algumas maneiras diferentes pelas quais as pessoas parecem advogar.

Por exemplo, algumas pessoas usam uma variável "flag" global para que uma tarefa assuma o controle. Outros usam endTimeSlice()e permitem que o árbitro realmente escolha. E acho que isso está correto.

Eu tenho essa pequena seção do código do RobotC na qual estou trabalhando para uma linha após o robô, mas não tenho certeza se estou fazendo certo, pois atualmente o método track sempre substitui o método find. O fluxo correto deve ser aquele que localizar deve guiar o robô até a linha usando um caminho em espiral para encontrar a linha. Uma vez que a linha é encontrada, a trilha deve assumir.

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

Acabei de usar alguns comentários aqui em vez do código real para mantê-lo breve. As minhas declarações if não são boas o suficiente como condições, porque quando o robô está fora de linha, track()assume o controle. Isso se deve à declaração else na trilha? Se sim, como track()executar curvas quando perde a linha sem retomar a forragem no início do programa?

James MV
fonte
1
Os números que você cede StartTask, são a prioridade da tarefa? 9 será a maior prioridade? Nesse caso, não deveria findter mais prioridade do que track? De fato, a condição de finde a elsecondição de tracksão as mesmas. Então, como humano, se o valor do sensor for maior que o limite, o que você faria? Vá em espiral ou gire para ajustar a linha?
Shahbaz
Sei o que é arquitetura de subsunção e tenho certeza de que seus robôs devem ter diferentes módulos de hardware, assumindo a responsabilidade por tarefas computacionais de baixo nível. E que o código realmente deve absorver o sinal, em vez de apenas ouvir um estado condicional. Então você não deveria passar o sinal para outra peça de hardware? Agora, o meu problema é que tenho dois Arduinos e quero que o Arduino A subsuma ou iniba um sinal do Arduino B. Alguém tem alguma idéia de como isso é feito? Como posso ligar Arduino A para enviar um sinal para Arduino B mas não estou certo como subsumir ou inibir o sig
Ambient Soda

Respostas:

7

Com a Subsumption Architecture, você deve projetar cuidadosamente seus comportamentos de forma que, se atribuir Tprioridade à tarefa n, Tdeve ser o que o robô deve fazer se todas as tarefas com prioridade mais alta do que nignoradas.

Vamos ordenar suas tarefas de exemplo e depois descobrir uma maneira de implementá-las. Suas tarefas são evade, finde track.

Em geral, você deseja que o robô rastreie uma linha. No entanto, se não conseguir detectar a linha, tente encontrá-la. Acima de tudo, deve evitar obstáculos. Isso nos dá a seguinte ordem:

  • Prioridade máxima: evade
  • Então: find
  • Então: track

O motivo findtem uma prioridade mais alta do que tracké que, como mencionei acima, você faria trackapenas se evadee findfor desnecessário. Se você colocar findabaixo track, isso significa que você começará a rastrear se não houver obstáculos, mesmo se você não estiver na linha.

Agora vamos dar uma olhada na sua implementação:

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

task track(){

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

Lembre-se de que demos finduma prioridade mais alta. Portanto, se o robô não puder detectar o lightSensor, ele entrará em espiral tentando encontrar a linha. trackQuando isso acontece, entra em ação. Como você pode ver, a elsecondição de tracknunca acontece.

Enquanto isso funciona, o robô se move muito desajeitadamente. Na verdade, não há muito que você possa fazer, dada a atual versão do seu robô.


Embora eu já tenha respondido sua pergunta, mas aqui está uma simples melhoria no seu rastreamento de linha:

Em vez de um sensor de luz, use dois; ls_lefte ls_right. Usando (pelo menos) dois sensores, você pode entender se está totalmente fora da pista ou prestes a sair da pista. No segundo caso, você pode facilmente mudar para a direção correta e voltar à pista.

Sua findtarefa é semelhante:

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

Ou seja, você entra em espiral apenas se não sentir nada

Sua tracktarefa agora se torna mais eficiente:

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

Obviamente, com uma matriz de sensores de luz, você pode julgar melhor o quanto está saindo da pista (por exemplo, com qual ângulo) e decidir melhor como voltar à pista (por exemplo, com qual velocidade angular).

Shahbaz
fonte
4

resposta curta; não, você realmente precisa fazer as coisas de maneira um pouco diferente.

resposta longa e incompleta; Deixe-me fornecer um código psuedo apropriado para robotC, que coloca você em um caminho melhor. Primeiro, não use tarefas - NÃO é para isso que servem as tarefas robotC. Eles podem ser feitos para funcionar, talvez, talvez não (e você precisa de algumas alterações para tentar).

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

há algumas coisas aqui; prioridade se torna irrelevante. Por mais agradável que pareça ter tarefas no robotC com prioridades, elas não são uma boa opção para implementação de subsunção na minha experiência. Por razões como, as prioridades nem sempre são respeitadas, as tarefas não podem ser interrompidas (às vezes); portanto, quando ocorrer um evento de prioridade mais alta, ele não reagirá como o esperado, o robotC só recentemente se tornou reentrante, portanto, coisas como acessar um sensor de mais de uma tarefa pode ser arriscada (problemas de tempo de I2C) e, em alguns casos, não é (sensores pesquisados ​​automaticamente).

Você pode adicionar sua própria implementação de prioridade ao loop acima à medida que as coisas funcionam, mas isso realmente não é necessário para iniciar.

O seu comentário "// box the obstruction" descreve um comportamento balístico. Isso é um pouco complicado de implementar usando o multitarefa. O loop simples que usei torna muito mais fácil e melhor para iniciantes / aprendizado.

A outra coisa com que vou deixar você é que a subsunção, apesar de organizada e apropriada para muitas coisas, não é uma boa maneira de implementar o que é melhor feito tradicionalmente. De fato, a parte 'evade' pode ser um bom candidato à subsunção, mas honestamente sua outra tarefa deve ser chamada 'GoOnAboutYourBusiness'. Digo isso porque você provavelmente não deseja mudar de pesquisar para seguir com subsunção. Lidar com aqueles com loops de programação tradicionais. Com um único sensor, - a luz é mais escura ou mais clara do que era no último loop? se ficar mais escuro (assumindo a linha preta), continue girando na mesma direção; se ficar mais claro, vire para o outro lado; se permaneceu o mesmo, siga em frente. Você provavelmente precisará adicionar um PID e usar uma curva de direção em vez de virar à esquerda e à direita para ficar mais suave.

E sim, vários sensores ajudam. http://www.mindsensors.com/ - sim, atualmente sou eu no filme (11/10/2012)

Atualização: código real

Vou tentar isso daqui a pouco, mas ele compila e ilustra o que escrevi acima:

#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
fonte
Concordo que esse problema é resolvido mais facilmente com um loop simples. Não entendo por que alguém rebaixaria isso.
Shahbaz #
Não quero deixar a impressão de que é mais fácil resolver com um loop simples, mas a impressão de que é o uso correto da subsunção usar um loop simples como uma das tarefas. Quem quer que tenha rebaixado, possui pontos de modificação e nenhum entendimento de subsunção. Você não encontrará muitas pessoas fazendo subsunção em um LEGO NXT (implícito no uso do robotC); portanto, não espere que o código esteja prontamente disponível para ser colado.
Spiked3
Sim, eu queria saber por que o OP estava usando tarefas para algo tão simples como subsunção.
Rocketmagnet
Porque é um erro de iniciante muito, muito, muito, muito comum com o robotC - tentar usar tarefas para tudo. Eu gostaria que eles o movessem para uma área única avançada.
Spiked3