最近,我一直在大量阅读有关“ 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()会接管。这是由于track中的else语句引起的吗?如果是这样,那么在程序启动时,如何使track()松开生产线而又不从草料上接管,该如何执行转弯?

评论

您在StartTask中输入的数字是任务的优先级吗? 9是最高优先级吗?在那种情况下,难道不应该找到比跟踪更重要的优先权吗?实际上,查找的条件和跟踪的其他条件是相同的。因此,作为一个人,如果传感器值大于阈值,您会怎么做?继续旋转还是旋转来调整线?

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

#1 楼

使用Subsumption Architecture,您应该仔细设计行为,以使如果您为任务T分配优先级n,那么如果优先级高于T的所有任务都被忽略,则应由机器人执行n

让我们为示例任务排序,然后找出实现它的方法。您的任务是evadefindtrack

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


最高优先级:evade

然后:find

然后:track


find的优先级高于track的原因是,如上所述,只有在不需要trackevade的情况下,您才需要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便会启动。如您所见,elsetrack条件永远不会发生。

尽管这样做有效,但机器人会非常笨拙地运动。考虑到您的机器人的当前版本,实际上您无能为力。


尽管我已经回答了您的问题,但这是对行跟踪的一个简单改进:

而不是使用一个光传感器,而应使用两个; 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
    }
}


显然,有了光传感器矩阵,您可以更好地判断偏离轨道的严重程度(即,以什么角度),并更好地决定如何重新回到轨道(即以什么角度)速度)。

#2 楼

简短的回答;不,您确实确实需要做很多事情。
答案不完整;
让我给您一些适合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/-是的,这就是我当前在电影中(2012年11月10日)
更新:实际代码
我会在一段时间内尝试一下,但是编译并说明我上面写的内容:
#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
    }
}


评论


$ \ begingroup $
我同意通过一个简单的循环可以更轻松地解决此问题。我不明白为什么有人会对此表示反对。
$ \ endgroup $
– Shahbaz
2012年11月10日13:44

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



$ \ begingroup $
是的,我想知道为什么OP将任务用于诸如归类之类的简单任务。
$ \ endgroup $
– Rocketmagnet
2012年10月10日19:52

$ \ begingroup $
因为robotC是一个非常非常非常非常常见的初学者错误-尝试并使用所有任务。我希望他们将其移至高级领域。
$ \ endgroup $
– Spiked3
2012年11月13日在2:46