在机器人C中使用归类架构的正确方法


11

最近,我一直在大量阅读有关“ Subsumption Architecture”的文章,人们似乎在倡导几种不同的方式。

例如,某些人使用全局“标志”变量来控制任务。其他人使用,endTimeSlice()并允许仲裁员真正选择。我认为这是正确的。

我有一小部分的RobotC代码,我正在为跟随机器人的一行工作,但是不确定我是否做对了,因为当前track方法将始终接管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)2012年

我知道什么是归类架构,并且我确定您的机器人应该具有不同的硬件模块来负责低级计算任务。并且代码实际上应该吸收信号,而不是仅监听条件状态。那么,您不应该将信号传递到另一块硬件中吗?现在我的问题是我有两个Arduino,我希望Arduino A包含或禁止Arduino B发出的信号。有谁知道如何做到这一点?因为我可以连接Arduino A来向Arduino B发送信号,但是我不确定如何包含或禁止信号
Ambient Soda

Answers:


7

使用Subsumption Architecture,您应该以一种方式精心设计行为,如果您分配任务T优先级n,那么T如果所有优先级高于优先级的任务n都被忽略,那么机器人应该采取的措施。

让我们订购示例任务,然后找出实现它的方法。你的任务是evadefindtrack

通常,您希望机器人跟踪一条线。但是,如果无法检测到该行,则应尝试找到它。最重要的是,它应该避开障碍。这给我们以下排序:

  • 最高优先级: evade
  • 然后: find
  • 然后: track

如前所述,原因find具有更高的优先级track,因为您track只会在evadefind不必要时才会这样做。如果放在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_leftls_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

简短的回答;不,您真的需要做很多不同的事情。

长期不完整的答案;让我给您一些适合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/- 是的,当前电影中就是我(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 2012年

我不想留下一个简单的循环就更容易解决的印象,而是给人一种印象,即使用简单的循环作为任务之一是正确的包容。凡是降级的人都有修改点,并且不了解包容性。您不会发现没有很多人在LEGO NXT上进行归类(通过使用robotC进行暗示),因此不要期望可以轻易粘贴代码。
Spiked3 2012年

是的,我想知道为什么OP将任务用于诸如归类之类的简单任务。
Rocketmagnet 2012年

因为这是robotC非常非常非常非常常见的初学者错误-尝试并使用所有任务。我希望他们将其移至高级领域。
Spiked3 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.