Mission code: Difference between revisions

From Rsewiki
Line 342: Line 342:
   return finished;
   return finished;
  }
  }
The first state 'case 0:' do nothing - just set the state to 11.
The 'case 11:' is
  case 11:
    if (fabsf(bridge->motor->getVelocity()) < 0.001 and bridge->imu->turnrate() < (2*180/M_PI))
    { // finished first drive and turnrate is zero'ish
      state = 12;
      usleep(35000);
      // start aruco analysis
      cam->arUcos->setNewFlagToFalse();
      cam->doArUcoAnalysis = true;
    }
    break;
This state checks if the robot is stationary by checking if the velocity is zero and the gyro do not detect any movement (less than 2 degrees per second).
If the robot is steady, then it waits a bit (35ms) to allow the camera to start another frame.
Then the flag to start ArUco analyses is set, and all previously detected markers have their 'new' flag cleared.
The 'case 12:' is
    case 12:
      if (not cam->doArUcoAnalysis)
      { // aruco processing finished
        if (cam->arUcos->getMarkerCount(true) > 0)
        { // found a marker - go to marker (any marker)
          state = 30;
          // tell the operator
          printf("# case=%d found marker\n", state);
          system("espeak \"found marker.\" -ven+f4 -s130 -a5 2>/dev/null &");
          bridge->send("oled 5 found marker");
        }
        else
        { // turn a bit (more)
          state = 20;
        }
      }
      break;
This state waits until the ArUco flag is cleared (is cleared, when the camera process is finished).
Then it checks the result, if the marker count is not zero, then the state is set to 30, if not then to 20 - to make a slight turn.
The 'case 20:' and 'case 21:' is
    case 20:
      { // turn a bit and then look for a marker again
        int line = 0;
        snprintf(lines[line++], MAX_LEN, "vel=0.25, tr=0.15: turn=10,time=10");
        snprintf(lines[line++], MAX_LEN, "vel=0,event=2:dist=1");
        sendAndActivateSnippet(lines, line);
        // make sure event 2 is cleared
        bridge->event->isEventSet(2);
        // tell the operator
        printf("# case=%d sent mission turn a bit\n", state);
        system("espeak \"turn.\" -ven+f4 -s130 -a5 2>/dev/null &");
        bridge->send("oled 5 code turn a bit");
        state = 21;
        break;
      }
    case 21: // wait until manoeuvre has finished
      if (bridge->event->isEventSet(2))
      {// repeat looking (until all 360 degrees are tested)
        if (featureCnt < 36)
          state = 11;
        else
          state = 999;
        featureCnt++;
      }
      break;
The case 20 sends a mission snippet to REGBOT with a turn of 10 degrees and a turning radius of 15cm. And ask REGBOT to emit an event 2 when finished and the velocity set to 0.
'Case 21' waits for event 2 and sets the state back to 11, initiating another image search. There is further a test to see if the robot has turned 36 times.
The 'case 30:' is
* extract the marker position,
* calculate manoeuver required to face the marker,
* translate the manoeuver to a REGBOT snippet.
hertil

Revision as of 14:46, 18 January 2020

Back to robobot.

Back to Robobot mission application.

Mission 1 code

This mission segment function is called very about 10ms until it returns true (finished). When the mission is in manual mode, then code is not called.


/**
* Run mission
* \param state is kept by caller, but is changed here
*              therefore defined as reference with the '&'.
*              State will be 0 at first call.
* \returns true, when finished. */
bool UMission::mission1(int & state)
{
 bool finished = false;
 // First commands to send to robobot in given mission
 // (robot sends event 1 after driving 1 meter)):
 switch (state)
 {
   case 0:
     // tell the operatior what to do
     printf("# press green to start.\n");
     system("espeak \"press green to start\" -ven+f4 -s130 -a5 2>/dev/null &"); 
     bridge->send("oled 5 press green to start");
     state++;
     break;
   case 1:
     if (bridge->joy->button[BUTTON_GREEN])
       state = 10;
     break;
   case 10: // first PART - wait for IR2 then go fwd and turn
     snprintf(lines[0], MAX_LEN, "vel=0 : ir2 < 0.3");
     // drive straight 0.6m - keep an acceleration limit of 1m/s2 (until changed)
     snprintf(lines[1], MAX_LEN, "vel=0.2,acc=1:dist=0.6");
     // stop and create an event when arrived at this line
     snprintf(lines[2], MAX_LEN, "event=1, vel=0");
     // add a line, so that the robot is occupied until next snippet has arrived
     snprintf(lines[3], MAX_LEN, ": dist=1");
     // send the 4 lines to the REGBOT
     sendAndActivateSnippet(lines, 4);
     // make sure event 1 is cleared
     bridge->event->isEventSet(1);
     // tell the operator
     printf("# case=%d sent mission snippet 1\n", state);
     system("espeak \"code snippet 1.\" -ven+f4 -s130 -a5 2>/dev/null &"); 
     bridge->send("oled 5 code snippet 1");
     //
     // play as we go
     play.setFile("../The_thing_goes_Bassim.mp3");
     play.setVolume(5); // % (0..100)
     play.start();
     // go to wait for finished
     state = 11;
     featureCnt = 0;
     break;
   case 11:
     // wait for event 1 (send when finished driving first part)
     if (bridge->event->isEventSet(1))
     { // finished first drive
       state = 999;
       play.stopPlaying();
     }
     break;
   case 999:
   default:
     printf("mission 1 ended \n");
     bridge->send("oled 5 \"mission 1 ended.\"");
     finished = true;
     break;
 }
 return finished;
}

On the first call, the 'state' variable is 0. The state is used in a case statement.


Green button pressed

On the first state, state 0, the operator is told what to do:

 switch (state)
 {
   case 0:
     // tell the operatior what to do
     printf("# press green to start.\n");
     system("espeak \"press green to start\" -ven+f4 -s130 -a5 2>/dev/null &"); 
     bridge->send("oled 5 press green to start");
     state++;
     break;
   case 1:
     if (bridge->joy->button[BUTTON_GREEN])
       state = 10;
     break;


The 'printf(...)' prints to the console.

Synthetic speach

The line ' system("espeak \"press green to start\" -ven+f4 -s130 -a5 2>/dev/null &"); ' will announce, using the speaker, what to do. Note that the text is in quotes '"' that are escaped by a backslash. The parameter '-ven+f4' is to use a female f4 voice. The '-s130' makes the speak a bit faster (130%). The '-a5' sets the volume to 5%. and the '2>/dev/null' pipes any error messages to the bit bucked (null device).

Google espeak for more details.

O-LED information

Further the line 'bridge->send("oled 5 press...");' will display a line (on line 5) on the small o-led display.

Next state

Once these messages are announced, then state is changed to 1.

On the next call to the function, there is a check to see if the green button is pressed, if it is, then state is changed to 10.

Drive snippet

When the state is t10 the following code is executed:

   case 10: // first PART - wait for IR2 then go fwd and turn
     snprintf(lines[0], MAX_LEN, "vel=0 : ir2 < 0.3");
     // drive straight 0.6m - keep an acceleration limit of 1m/s2 (until changed)
     snprintf(lines[1], MAX_LEN, "vel=0.2,acc=1:dist=0.6");
     // stop and create an event when arrived at this line
     snprintf(lines[2], MAX_LEN, "event=1, vel=0");
     // add a line, so that the robot is occupied until next snippet has arrived
     snprintf(lines[3], MAX_LEN, ": dist=1");
     // send the 4 lines to the REGBOT
     sendAndActivateSnippet(lines, 4);
     // make sure event 1 is cleared
     bridge->event->isEventSet(1);
     ...

There is an array of C-strings that is used to build the mission snippet.

The line 'snprintf(lines[0], MAX_LEN, "vel=0 : ir2 < 0.3");' prepares the command 'vel=0 : ir2 < 0.3', velocity zero and then wait until the IR2 sensor has a detection that is closer than 0.3m.

The next line is 'vel=0.2,acc=1:dist=0.6' sets velocity to 0.2m/s with a safe acceleration of 1m/s^2. This line then holds until 0.6m is driven.

The third line is 'event=1, vel=0' it will set the speed to 0 and generate an event, event 1. There is no ':' and thus the REGBOT will continue to the next line right away.

The fourth line is ': dist=1' this line has a ':' and will wait until the robot has moved 1m. This will not happen, as the velocity is zero, so the REGBOT will wait for further instructions.

Now 4 lines are prepared, ready to send to the REGBOT and activated.

sendAndActivateSnippet(lines, 4);

It will take some time before the drive is finished and event 1 generated. To be on the safe side the event 1 flag is cleared with the 'isEventSet(1);' (could have been set by a previous mission).

Music gimmick

Now that the robot has speakers, it could be a gimmick to play some music.

There is a couple of lines to play:

   case 10: // first PART - wait for IR2 then go fwd and turn
     ....
     // play as we go
     play.setFile("../The_thing_goes_Bassim.mp3");
     play.setVolume(5); // % (0..100)
     play.start();
     // go to wait for finished
     state = 11;
     featureCnt = 0;
     break;

The class has a variable called 'play' that has the code to start and stop music files. I have found a royalty-free clip called 'The_thing_goes_Bassim.mp3', so this filename is set to the play object, then the volume is set to just 5% - not to be too loud. And then finally started.

The state is then changed to 11.

Wait for mission snippet event

The 4 lines of REGBOT mission snippet is sent and activated, now we should wait until it has finished. This is what state 11 is doing.

   case 11:
     // wait for event 1 (send when finished driving first part)
     if (bridge->event->isEventSet(1))
     { // finished first drive
       state = 999;
       play.stopPlaying();
     }
     break;
   case 999:
   default:
     printf("mission 1 ended \n");
     bridge->send("oled 5 \"mission 1 ended.\"");
     finished = true;
     break;

When the event 1 is detected, the state is set to 999. State 999 is also the default state, and will just set the 'finished' flag to true. This will tell the mission loop that this segment is finished, and will then reset the state to 0 and call the next mission segment instead.

Mission2 code

This part will look for an ArUco code marker in a camera image. If found then move to the marker, if not then turn a bit.

bool UMission::mission2(int & state)
{
 bool finished = false;
 switch (state)
 {
   case 0:
     system("espeak \"looking for ArUco\" -ven+f4 -s130 -a5 2>/dev/null &"); 
     bridge->send("oled 5 looking 4 ArUco");
     state=11;
     break;
   case 11:
     if (fabsf(bridge->motor->getVelocity()) < 0.001 and bridge->imu->turnrate() < (2*180/M_PI))
     { // finished first drive and turnrate is zero'ish
       state = 12;
       usleep(35000);
       // start aruco analysis 
       cam->arUcos->setNewFlagToFalse();
       cam->doArUcoAnalysis = true;
     }
     break;
   case 12:
     if (not cam->doArUcoAnalysis)
     { // aruco processing finished
       if (cam->arUcos->getMarkerCount(true) > 0)
       { // found a marker - go to marker (any marker)
         state = 30;
       }
       else
       { // turn a bit (more)
         state = 20;
       }
     }
     break;
   case 20: 
     { // turn a bit and then look for a marker again
       int line = 0;
       snprintf(lines[line++], MAX_LEN, "vel=0.25, tr=0.15: turn=10,time=10");
       snprintf(lines[line++], MAX_LEN, "vel=0,event=2:dist=1");
       sendAndActivateSnippet(lines, line);
       // make sure event 2 is cleared
       bridge->event->isEventSet(2);
       state = 21;
       break;
     }
   case 21: // wait until manoeuvre has finished
     if (bridge->event->isEventSet(2))
     {// repeat looking (until all 360 degrees are tested)
       if (featureCnt < 36)
         state = 11;
       else
         state = 999;
       featureCnt++;
     }
     break;
   case 30:
     { // found marker
       // if stop marker, then exit
       ArUcoVal * v = cam->arUcos->getID(6);
       if (v != NULL and v->isNew)
       { // sign to stop
         state = 999;
         break;
       }
       // use the first (assumed only one)
       v = cam->arUcos->getFirstNew();
       // marker position in robot coordinates
       float xm = v->markerPosition.at<float>(0,0);
       float ym = v->markerPosition.at<float>(0,1);
       float hm = v->markerAngle;
       // stop some distance in front of marker
       float dx = 0.3; // distance to stop in front of marker
       float dy = 0.0; // distance to the left of marker
       xm += - dx*cos(hm) + dy*sin(hm);
       ym += - dx*sin(hm) - dy*cos(hm);
       // limits
       float acc = 1.0; // max allowed acceleration - linear and turn
       float vel = 0.3; // desired velocity
       // set parameters
       // end at 0 m/s velocity
       UPose2pose pp4(xm, ym, hm, 0.0);
       printf("\n");
       // calculate turn-straight-turn (Angle-Line-Angle)  manoeuvre
       bool isOK = pp4.calculateALA(vel, acc);
       // use only if distance to destination is more than 3cm
       if (isOK and (pp4.movementDistance() > 0.03))
       { // a solution is found - and more that 3cm away.
         // debug print manoeuvre details
         pp4.printMan();
         printf("\n");
         // debug end
         int line = 0;
         if (pp4.initialBreak > 0.01)
         { // there is a starting straight part
           snprintf(lines[line++], MAX_LEN, "vel=%.3f,acc=%.1f :dist=%.3f", 
                    pp4.straightVel, acc, pp4.straightVel);
         }
         snprintf(lines[line++], MAX_LEN,   "vel=%.3f,tr=%.3f :turn=%.1f", 
                  pp4.straightVel, pp4.radius1, pp4.turnArc1 * 180 / M_PI);
         snprintf(lines[line++], MAX_LEN,   ":dist=%.3f", pp4.straightDist);
         snprintf(lines[line++], MAX_LEN,   "tr=%.3f :turn=%.1f", 
                  pp4.radius2, pp4.turnArc2 * 180 / M_PI);
         if (pp4.finalBreak > 0.01)
         { // there is a straight break distance
           snprintf(lines[line++], MAX_LEN,   "vel=0 : time=%.2f", 
                    sqrt(2*pp4.finalBreak));
         }
         snprintf(lines[line++], MAX_LEN,   "vel=0, event=2: dist=1");
         sendAndActivateSnippet(lines, line);
         bridge->event->isEventSet(2);
       }
       else
       { // look again for marker
         state = 11;
       }
       // wait for movement to finish
       state = 31;
     }
     break;
   case 31:
     // wait for event 2 (send when finished driving)
     if (bridge->event->isEventSet(2))
     { // look for next marker
       state = 11;
       // no, stop
       state = 999;
     }
     break;
   case 999:
   default:
     printf("mission 1 ended \n");
     bridge->send("oled 5 \"mission 1 ended.\"");
     finished = true;
     play.stopPlaying();
     break;
 }
 // printf("# mission1 return (state=%d, finished=%d, )\n", state, finished);
 return finished;
}

The first state 'case 0:' do nothing - just set the state to 11.

The 'case 11:' is

  case 11:
    if (fabsf(bridge->motor->getVelocity()) < 0.001 and bridge->imu->turnrate() < (2*180/M_PI))
    { // finished first drive and turnrate is zero'ish
      state = 12;
      usleep(35000);
      // start aruco analysis 
      cam->arUcos->setNewFlagToFalse();
      cam->doArUcoAnalysis = true;
    }
    break;

This state checks if the robot is stationary by checking if the velocity is zero and the gyro do not detect any movement (less than 2 degrees per second).

If the robot is steady, then it waits a bit (35ms) to allow the camera to start another frame. Then the flag to start ArUco analyses is set, and all previously detected markers have their 'new' flag cleared.

The 'case 12:' is

   case 12:
     if (not cam->doArUcoAnalysis)
     { // aruco processing finished
       if (cam->arUcos->getMarkerCount(true) > 0)
       { // found a marker - go to marker (any marker)
         state = 30;
         // tell the operator
         printf("# case=%d found marker\n", state);
         system("espeak \"found marker.\" -ven+f4 -s130 -a5 2>/dev/null &"); 
         bridge->send("oled 5 found marker");
       }
       else
       { // turn a bit (more)
         state = 20;
       }
     }
     break;

This state waits until the ArUco flag is cleared (is cleared, when the camera process is finished).

Then it checks the result, if the marker count is not zero, then the state is set to 30, if not then to 20 - to make a slight turn.

The 'case 20:' and 'case 21:' is

   case 20: 
     { // turn a bit and then look for a marker again
       int line = 0;
       snprintf(lines[line++], MAX_LEN, "vel=0.25, tr=0.15: turn=10,time=10");
       snprintf(lines[line++], MAX_LEN, "vel=0,event=2:dist=1");
       sendAndActivateSnippet(lines, line);
       // make sure event 2 is cleared
       bridge->event->isEventSet(2);
       // tell the operator
       printf("# case=%d sent mission turn a bit\n", state);
       system("espeak \"turn.\" -ven+f4 -s130 -a5 2>/dev/null &"); 
       bridge->send("oled 5 code turn a bit");
       state = 21;
       break;
     }
   case 21: // wait until manoeuvre has finished
     if (bridge->event->isEventSet(2))
     {// repeat looking (until all 360 degrees are tested)
       if (featureCnt < 36)
         state = 11;
       else
         state = 999;
       featureCnt++;
     }
     break;

The case 20 sends a mission snippet to REGBOT with a turn of 10 degrees and a turning radius of 15cm. And ask REGBOT to emit an event 2 when finished and the velocity set to 0.

'Case 21' waits for event 2 and sets the state back to 11, initiating another image search. There is further a test to see if the robot has turned 36 times.

The 'case 30:' is

  • extract the marker position,
  • calculate manoeuver required to face the marker,
  • translate the manoeuver to a REGBOT snippet.

hertil