суббота, 15 сентября 2018 г.

NXT for Speed

Похоже LEGO Mindstorms NXT уже всеми забыт и в мире пластиковой робототехники безраздельно правит EV3. Однако мы не спешим списывать его со счетов и в нашем сегодняшнем проекте он еще даст жару!


NXT for Speed - гоночный аркадный автомат с использованием руля и педалей. Основная цель проекта - изучение основных принципов работы с динамической векторной графикой, закрепление навыков программирования многопоточных приложений на С.


В нашем проекте мы реализовали:
  • динамический расчет конфигурации трассы
  • физическую модель, учитывающую силу нажатия на педаль газа, сопротивление покрышек, сопротивление воздуха. Автомобиль способен двигаться накатом, тормозить с учетом силы нажатия на педаль тормоза.
  • вид от третьего лица с изменением угла зрения на автомобиль
  • "плавающий" горизонт с задним фоном (горы)
  • дорожные знаки на обочинах дороги 
  • управление с использованием руля (контроль угла поворота) и педалей (газ-тормоз)
  • реализован автовозврат педалей - они жесткие и возвращаются в исходную позицию, если их отпустить
  • обратная связь от препятствий - при съезде на обочины руль трясет
  • обратная связь от скорости движения - чем выше скорость тем сложнее резко повернуть руль, на стоящей машине руль поворачивается легко
  • цифровой спидометр в углу экрана
  • подсчет очков

Инструкцию по сборке в формате LEGO Digital Designer можно скачать по ссылке.


Программа написана на языке NXC и выглядит следующим образом:

Код:
int ohki=0;
int trig=1;
int r=23;
int ry=0;
int rx=0;
int mx=0;
int my;
float rz=2;
int l=0;
int c=0;
long t;
float m=500;
int f=4;
float x1=25,x2=75,y1=0,y2=0;
int rt=0;
int o=-10000000000,p=100000000;
int u=0;
float k=0.90;
int tmp =0;
float turn[48];
int ch=0;
float a=50;
int b=0;
int e=0;
int Sign(int x, int y,int v,int c)
{
  if(c==0){
    LineOut(x,y,x,y+v/2,DRAW_OPT_NORMAL);
    RectOut(x-v/4,y+v/2,v/2,v/2,DRAW_OPT_NORMAL);
  }
  else{
    LineOut(x,y,x,y+v/2,DRAW_OPT_CLEAR);
    RectOut(x-v/4,y+v/2,v/2,v/2,DRAW_OPT_CLEAR);
  }
}

int lines(int x, int y,int color)
{
  mx=x;

  if(color==0){
    RectOut(x-6,y+4,11,3,DRAW_OPT_NORMAL);
    RectOut(x-5,y+2,2,2,DRAW_OPT_NORMAL);
    RectOut(x+2,y+2,2,2,DRAW_OPT_NORMAL);
    LineOut(x+5,y+7,x+3,y+10,DRAW_OPT_NORMAL);
    LineOut(x-6,y+7,x-4,y+10,DRAW_OPT_NORMAL);
    LineOut(x-4,y+10,x+3,y+10,DRAW_OPT_NORMAL);
    if(CurrentTick()-t>500){
      TextOut(0,0,"    ");

      e=(500-m)/5;
      NumOut(0,0,e);
      t=CurrentTick();
    }
    if(MotorRotationCount(OUT_A)  > 15){
      LineOut(x+5,y+6,x+9,y+9,DRAW_OPT_NORMAL);
      LineOut(x+3,y+10,x+7,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-2,y+12,DRAW_OPT_NORMAL);
      LineOut(x,y+12,x+7,y+12,DRAW_OPT_NORMAL);
      LineOut(x+7,y+12,x+10,y+5,DRAW_OPT_NORMAL);
      LineOut(x+6,y+3,x+10,y+5,DRAW_OPT_NORMAL);
      LineOut(x+10,y+4,x+10,y+3,DRAW_OPT_NORMAL);
      LineOut(x+9,y+3,x+10,y+3,DRAW_OPT_NORMAL);

      if(x+10>=p+10){
        TextOut(35,20,"Over");
        rt=1;
      }
    }
    else if(MotorRotationCount(OUT_A)  <-15){

      LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-7,y+12,DRAW_OPT_NORMAL);
      LineOut(x+1,y+12,x-8,y+12,DRAW_OPT_NORMAL);
      LineOut(x-6,y+7,x-9,y+10,DRAW_OPT_NORMAL);
      LineOut(x-8,y+11,x-11,y+7,DRAW_OPT_NORMAL);
      LineOut(x-11,y+7,x-7,y+4,DRAW_OPT_NORMAL);
      LineOut(x-11,y+7,x-11,y+4,DRAW_OPT_NORMAL);

      LineOut(x-11,y+4,x-9,y+4,DRAW_OPT_NORMAL);
      if(x-11<=o-10){
        TextOut(35,20,"Over");
        rt=1;
      }
    }
    else{
      LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-4,y+10,x-2,y+12,DRAW_OPT_NORMAL);
      LineOut(x-2,y+12,x+1,y+12,DRAW_OPT_NORMAL);
      LineOut(x-5,y+11,x-5,y+11,DRAW_OPT_NORMAL);
      LineOut(x+4,y+11,x+4,y+11,DRAW_OPT_NORMAL);
      //if(x+10<=p || x-6>=o){
      //  TextOut(0,0,"Over");
      //  rt=1;
      //}
    }
    Wait(10);
  }
  else{
    RectOut(x-6,y+4,11,3,DRAW_OPT_CLEAR);
    RectOut(x-5,y+2,2,2,DRAW_OPT_CLEAR);
    RectOut(x+2,y+2,2,2,DRAW_OPT_CLEAR);
    LineOut(x+5,y+7,x+3,y+10,DRAW_OPT_CLEAR);
    LineOut(x-6,y+7,x-4,y+10,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x+3,y+10,DRAW_OPT_CLEAR);

    LineOut(x+5,y+6,x+9,y+9,DRAW_OPT_CLEAR);
    LineOut(x+3,y+10,x+7,y+12,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x,y+12,DRAW_OPT_CLEAR);
    LineOut(x,y+12,x+7,y+12,DRAW_OPT_CLEAR);
    LineOut(x+7,y+12,x+10,y+5,DRAW_OPT_CLEAR);
    LineOut(x+6,y+3,x+10,y+5,DRAW_OPT_CLEAR);
    LineOut(x+10,y+4,x+10,y+3,DRAW_OPT_CLEAR);
    LineOut(x+9,y+3,x+10,y+3,DRAW_OPT_CLEAR);
    LineOut(x+3,y+10,x+1,y+12,DRAW_OPT_CLEAR);
    LineOut(x-4,y+10,x-7,y+12,DRAW_OPT_CLEAR);
    LineOut(x+1,y+12,x-8,y+12,DRAW_OPT_CLEAR);
    LineOut(x-6,y+7,x-9,y+10,DRAW_OPT_CLEAR);
    LineOut(x-8,y+11,x-11,y+7,DRAW_OPT_CLEAR);
    LineOut(x-11,y+7,x-7,y+4,DRAW_OPT_CLEAR);
    LineOut(x-11,y+7,x-11,y+4,DRAW_OPT_CLEAR);
    LineOut(x-11,y+4,x-9,y+4,DRAW_OPT_CLEAR);
  }
  return 0;
}

task mot()
{
  Wait(3000);
  float P=0.7;
  //P=0.3*e;
  float Kp=e/100;
  float P2=0.7;
  float P3=0.7;
  float ERR1=0;
  float ERR2=0;
  float ERR3=0;
  float u1=0;
  float u2=0;
  float u3=0;
  while(1){
    if(rt==1){
      break;
    }

    float Kp=e/100;
    ERR1=0-MotorRotationCount(OUT_A);
    u1=P*ERR1*Kp;
    ERR2=0-MotorRotationCount(OUT_B);
    u2=P2*ERR2;
    ERR3=0-MotorRotationCount(OUT_C);
    u3=P3*ERR3;
    if(mx-11<=o+3){
      u1=Random(70)-35;
    }
    if(mx+10>=p-3){
      u1=Random(70)-35;
    }

    if(u1>100)u1=100;
    if(u1<-100)u1=-100;
    if(u1<0){
      OnRev(OUT_A,-1*u1);
    }
    else{
      OnFwd(OUT_A,u1);
    }
    if(u2>100)u2=100;
    if(u2<-100)u2=-100;
    if(u2<0){
      OnRev(OUT_B,-1*u2);
    }
    else{
      OnFwd(OUT_B,u2);
    }
    if(u3>100)u3=100;
    if(u3<-100)u3=-100;
    if(u3<0){
      OnRev(OUT_C,-1*u3);
    }
    else{
      OnFwd(OUT_C,u2);
    }
    Wait(100);
  }
}

task road()
{
  for(int i =0;i<47;i++){
    turn[i]=0;
  }
  while(true){

    ch++;
    f--;
    if(f==0){
      f=4;
    }

    l+=1;

    for(int i =0;i<23;i+=1){

      LineOut(25+turn[i]+i*0.9,i*2,25+turn[i+1]+(i+1)*0.9,i*2+3,DRAW_OPT_NORMAL);
      LineOut(75+turn[i]-i*0.9,i*2,75+turn[i+1]-(i+1)*0.9,i*2+3,DRAW_OPT_NORMAL);
      if(i==r){
        if(trig==1){
          rx=25+turn[i]+i*0.9-5;
          ry=i*2;
        }
        else{
          rx=75+turn[i]-i*0.9+5;
          ry=i*2;
        }
      }
      if(i%4==f  && i>5){
        LineOut(50+turn[i],i*2,50+turn[i+1],i*2+3,DRAW_OPT_NORMAL );
      }
    }
    Sign(rx,ry,rz,0);
    m+=5;
    m+=m*0.2;
    if(m>500){
      m=500;
    }
    if(m<0){
      m=0;
    }
    if(abs(MotorRotationCount(OUT_B))> 5){
      if(m<=500){
        m-=1.7*abs(0-abs(MotorRotationCount(OUT_B))*2);

      }
      if(m>500){
        m=500;
      }
      if(m<0){
        m=0;
      }
    }
    if(abs(MotorRotationCount(OUT_C))> 5){
      if(m>=0){
        m+=1.7*abs(0-abs(MotorRotationCount(OUT_C)));

      }
      if(m>500){
        m=500;
      }
      if(m<0){
        m=0;
      }
    }


    Wait(m);

    for(int i =0;i<23;i+=1){
      LineOut(25+turn[i]+i*0.9,i*2,25+turn[i+1]+(i+1)*0.9,i*2+3,DRAW_OPT_CLEAR);
      LineOut(75+turn[i]-i*0.9,i*2,75+turn[i+1]-(i+1)*0.9,i*2+3,DRAW_OPT_CLEAR);

      if(i%4==f && i>5)
      {
        LineOut(50+turn[i],i*2,50+turn[i+1],i*2+3,DRAW_OPT_CLEAR);
      }
      if(i==3){
        o=25+turn[i];
        p=75+turn[i];
      }
    }
    Sign(rx,ry,rz,1);
    rz+=0.5;
    r--;

    if(rz>=13){
      rz=2;
      r=23;
      trig=trig*-1;
    }
    for(int i =0;i<47;i++){
      turn[i]=turn[i+1];
    }
    if(ch==23){
      ohki+=1;
      ch=0;
      u = Random(100);
      turn[47]=u-50;
      for(int i =23;i<47;i++){
        turn[i]=turn[i-1]*k+turn[47]*(1-k);
      }
    }

    if(rt==1){
      break;
    }
    if(l==1){
      LineOut(70,0,70,35,DRAW_OPT_NORMAL);
      LineOut(70,35,30,35,DRAW_OPT_NORMAL);
      LineOut(70,17,30,17,DRAW_OPT_NORMAL);
      LineOut(70,0,30,0,DRAW_OPT_NORMAL);
      Wait(1000);
      LineOut(70,0,70,35,DRAW_OPT_CLEAR);
      LineOut(70,35,30,35,DRAW_OPT_CLEAR);
      LineOut(70,17,30,17,DRAW_OPT_CLEAR);
      LineOut(70,0,30,0,DRAW_OPT_CLEAR);
      LineOut(30,0,70,35,DRAW_OPT_NORMAL);
      LineOut(30,35,70,35,DRAW_OPT_NORMAL);
      LineOut(70,0,30,0,DRAW_OPT_NORMAL);

      Wait(1000);
      LineOut(30,0,70,35,DRAW_OPT_CLEAR);
      LineOut(30,35,70,35,DRAW_OPT_CLEAR);
      LineOut(70,0,30,0,DRAW_OPT_CLEAR);
      LineOut(30,17,70,35,DRAW_OPT_NORMAL);
      LineOut(70,0,70,35,DRAW_OPT_NORMAL);

      Wait(1000);
      LineOut(30,17,70,35,DRAW_OPT_CLEAR);
      LineOut(70,0,70,35,DRAW_OPT_CLEAR);
    }
  }
}

task main()
{
  t=CurrentTick();
  SetSensorTouch(IN_4);
  start road;
  start mot;

  ClearScreen();
  LineOut(0,48,100,48,DRAW_OPT_NORMAL);

  lines(a,b,0);

  while(true){
    NumOut(80,0,ohki);
    c=30-((a-50)/2);
    LineOut(c-50,49,c-30,53,DRAW_OPT_NORMAL);
    LineOut(c-10,49,c-30,53,DRAW_OPT_NORMAL);
    LineOut(c-10,49,c,55,DRAW_OPT_NORMAL);
    LineOut(c+10,49,c,55,DRAW_OPT_NORMAL);
    LineOut(c+5,51,c+15,55,DRAW_OPT_NORMAL);
    LineOut(c+25,51,c+15,55,DRAW_OPT_NORMAL);
    LineOut(c+20,51,c+35,55,DRAW_OPT_NORMAL);
    LineOut(c+40,49,c+35,55,DRAW_OPT_NORMAL);
    LineOut(c+40,51,c+55,55,DRAW_OPT_NORMAL);
    LineOut(c+60,49,c+55,55,DRAW_OPT_NORMAL);
    LineOut(c+60,51,c+85,55,DRAW_OPT_NORMAL);
    LineOut(c+100,49,c+85,55,DRAW_OPT_NORMAL);
    lines(a,b,1);
    if(abs(MotorRotationCount(OUT_A))>=15){
      a+=(MotorRotationCount(OUT_A))/100.0;
    }
    LineOut(c-10,49,c,55,DRAW_OPT_CLEAR);
    LineOut(c+10,49,c,55,DRAW_OPT_CLEAR);
    LineOut(c+5,51,c+15,55,DRAW_OPT_CLEAR);
    LineOut(c+25,51,c+15,55,DRAW_OPT_CLEAR);
    LineOut(c+20,51,c+35,55,DRAW_OPT_CLEAR);
    LineOut(c+40,49,c+35,55,DRAW_OPT_CLEAR);
    LineOut(c+40,51,c+55,55,DRAW_OPT_CLEAR);
    LineOut(c+60,49,c+55,55,DRAW_OPT_CLEAR);
    LineOut(c+60,51,c+85,55,DRAW_OPT_CLEAR);
    LineOut(c+100,49,c+85,55,DRAW_OPT_CLEAR);
    LineOut(c-50,49,c-30,53,DRAW_OPT_CLEAR);
    LineOut(c-10,49,c-30,53,DRAW_OPT_CLEAR);
    lines(a,b,0);
    if(rt==1){
      break;
    }
  }
  Wait(1500);
}



Самое популярное