The Artificial Intelligence
A major component of any successful robot is its brain. Having been given a highly capable microcontroller, we were free to write extended and spacious code, knowing there was plenty of flash memory and ram to handle our needs. We avoided extensive arrays because these can always present challenges both in processing speed and memory allocation, and opted for a robot that was almost exclusively running on "live" data. In fact, we expected many more issues than actually occurred with this strategy. The vision system, though laggy, provided smooth data that needed only a hundred milliseconds pause to catch up to values that were usable. Considering how much stopping there was naturally as a result of the challenge's requirements, ample time was given to recalibrate and update our position and gyroscope.
Our strategy depended mostly on an extremely shy opponent-avoidance system. In order to prevent collisions, we automatically avoided any sector that was occupied by the other robot. Beyond this, we placed highest priority on dealing with adjacent sectors, since navigation beyond the areas immediately bordering ours would be too costly in terms of time. Sectors we had already captured and with a non-zero number of available balls received the highest priority, then came capturing uncaptured territories, then dumping our payload if it was getting to the maximum our hopper could hold.
Our robot depended on two threads for control; a low-level thread that directed motors and servos for the repetitive tasks of moving to a point, turning to a certain heading, capturing a base, pulling the dispenser lever or going into idle mode, and a high-level thread that calculated the best next move, set the robot to the correct state, and passed the appropriate information to the low-level code. We found this to be a highly effective way of controlling the robot, but a change to our highest-level strategy function and trouble with the way our timeouts were called required us to rush through remaking the code in the last 3 hours before impound. Because of these changes, we did not have enough time to think of all the possible scenarios, and only had one operational timeout. This crippled us in the competition, and the one thing I would do differently would be to put in these necessary timeouts.
Our favorite piece of code was the one that prevented our base-capturing saw-arm from stalling because it's servo was pushing the wheel too hard against the gearbox. To counter this, we used the current-sensing feature of the H-Bridges to identify a motor stall from the amount of current being drawn. If there is above a certain threshold, the servo retracts until the motor current falls back to an acceptable zone. If there is too little current being drawn, the robot assumes the arm must not yet have contacted the gearbox, and further extends the arm. The code for that is as follows:
Our strategy depended mostly on an extremely shy opponent-avoidance system. In order to prevent collisions, we automatically avoided any sector that was occupied by the other robot. Beyond this, we placed highest priority on dealing with adjacent sectors, since navigation beyond the areas immediately bordering ours would be too costly in terms of time. Sectors we had already captured and with a non-zero number of available balls received the highest priority, then came capturing uncaptured territories, then dumping our payload if it was getting to the maximum our hopper could hold.
Our robot depended on two threads for control; a low-level thread that directed motors and servos for the repetitive tasks of moving to a point, turning to a certain heading, capturing a base, pulling the dispenser lever or going into idle mode, and a high-level thread that calculated the best next move, set the robot to the correct state, and passed the appropriate information to the low-level code. We found this to be a highly effective way of controlling the robot, but a change to our highest-level strategy function and trouble with the way our timeouts were called required us to rush through remaking the code in the last 3 hours before impound. Because of these changes, we did not have enough time to think of all the possible scenarios, and only had one operational timeout. This crippled us in the competition, and the one thing I would do differently would be to put in these necessary timeouts.
Our favorite piece of code was the one that prevented our base-capturing saw-arm from stalling because it's servo was pushing the wheel too hard against the gearbox. To counter this, we used the current-sensing feature of the H-Bridges to identify a motor stall from the amount of current being drawn. If there is above a certain threshold, the servo retracts until the motor current falls back to an acceptable zone. If there is too little current being drawn, the robot assumes the arm must not yet have contacted the gearbox, and further extends the arm. The code for that is as follows:
//printf("capturing\n"); uint32_t timeDelay = get_time(); servo_capture(); int j=0; while(!newCommand){ if(motor_get_current_MA(2)>1200 && (get_time() - timeDelay>500)){ //pulls arm in if stalled servo_set_pos(0, 255+j); j+=2; }else if(motor_get_current_MA(2)<800 && (get_time() - timeDelay>500) && (j > -25)){ //pushes arm out if freewheeling, to a limit servo_set_pos(0, 255+j); j-=2; } pause(10); yield();} servo_return();
The heart of our strategy code is included below:
//////////////////////////////////////////////////////////////////// // Strategy function - function for strategy thread // where high level strategy decision come out of //////////////////////////////////////////////////////////////////// int strategy() { //printf("strategy begin\n"); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////// STRATEGY //////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int currentTerritory = ( team == BLUE ) ? 0 : 3; int dropTerritory = ( team == BLUE ) ? 2 : 5; int nextTerritory = currentTerritory; int exploreDir = 1; //CCW int currentPoint = 0; int nextPoint = 0; int changeTerritory = 0; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////// FIRST 10 SECONDS OF EXPLORATION ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// while ( getMatchTime() < (10*1000) ) { nextTerritory = currentTerritory + exploreDir; if ( nextTerritory < 0 ) { nextTerritory = 5; } else if ( nextTerritory > 5 ) { nextTerritory = 0; } if ( enemy_current_territory() == nextTerritory ) { //enemy in that territory!, turn around! exploreDir = ( exploreDir == 1 ) ? -1 : 1; continue; } if ( pathfind_to_point(territory_index(currentTerritory) + TERRITORY_CENTER, territory_index(nextTerritory) + TERRITORY_CENTER) == 0 ) { //if pathfinding wasn't successful, try turning around exploreDir = ( exploreDir == 1 ) ? -1 : 1; nextTerritory = currentTerritory; continue; } currentTerritory = nextTerritory; pause( 10 ); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////// REST OF MATCH ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FIRST 10 SECONDS LOOP SHOULD HAVE LEFT currentPoint AT THE TERRITORY WHERE WE ARE currentPoint = territory_index(currentTerritory)+TERRITORY_CENTER; // WHILE IT'S NOT THE LAST 10 SECONDS DROPPING SEQEUNCE ( CHECKS THAT WE ACTUALLY HAVE BALLS ) while ( !( (( getMatchTime()/1000) > (110) ) && ( nBallsCollected > 0 ) ) ) { if ( nBallsCollected > 10 ) { //CURRRENTLY NO ERROR HANDLING FOR THISS :( // MOVE TO DROP TERRITORY'S CENTER nextPoint = territory_index( dropTerritory )+TERRITORY_CITY; if ( pathfind_to_point( currentPoint, nextPoint ) == 0 ) { // STUCK SOMEWHERE // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = nextPoint; //DROP big_dump( dropTerritory ); currentPoint = territory_index( dropTerritory )+TERRITORY_CITY; // nBallsCollected SHOULD NOW BE RESET AND THINGS CONTINUE } else { changeTerritory = 1; if ( game.territories[point_territory(currentPoint)].owner != 8 &&( enemy_current_territory() == point_territory(currentPoint))) { changeTerritory = 1; exploreDir = ( exploreDir == 1 ) ? -1 : 1; }else if ( game.territories[point_territory(currentPoint)].owner != 8 ) { // **** GEARBOX **** // TERRITORY NOT OURS, CAPTURE nextPoint = territory_index(point_territory(currentPoint))+TERRITORY_GEARBOX_APPROACH; if ( pathfind_to_point( currentPoint, nextPoint ) == 0 ) { // STUCK SOMEWHERE // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = nextPoint; if ( capture( point_territory(currentPoint) ) == 0 ) { // FAILURED CAPTURE // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = territory_index(point_territory(currentPoint))+TERRITORY_GEARBOX; changeTerritory = 0; } else if ( game.territories[point_territory(currentPoint)].remaining > 0 ) { // **** LEVER **** // ALREADY CAPTURED, GET BALLS nextPoint = territory_index(point_territory(currentPoint))+TERRITORY_LEVER; if ( pathfind_to_point( currentPoint, nextPoint ) == 0 ) { //failure // STUCK SOMEWHERE // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = nextPoint; if ( balls( point_territory(currentPoint) ) == 0 ) { // GETTING BALLS FAILED // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = territory_index(point_territory(currentPoint))+TERRITORY_LEVER; changeTerritory = 0; } if ( changeTerritory == 1 ) { // ALREADY CAPTURED AND GOT BALLS OR SOMETHING WENT WRONG // GO TO A DIFFERENT TERRITORY nextTerritory = point_territory(currentPoint)+exploreDir; if ( nextTerritory < 0 ) { nextTerritory = 5; } else if ( nextTerritory > 5 ) { nextTerritory = 0; } if ( enemy_current_territory() == nextTerritory ) { //enemy in that territory!, turn around! exploreDir = ( exploreDir == 1 ) ? -1 : 1; continue; } nextPoint = territory_index(nextTerritory) + TERRITORY_CENTER; if ( pathfind_to_point( currentPoint, nextPoint) == 0 ) { // STUCK SOMEWHERE // REACT & BREAK LOOP, err maybe not break but don't want to continue this loop } currentPoint = nextPoint; } } pause( 10 ); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////// LAST 10 SECONDS, D-D-D-DROP DEM BALLZZZ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// nextPoint = territory_index(dropTerritory)+TERRITORY_CENTER; pathfind_to_point(currentPoint,nextPoint); currentPoint = nextPoint; big_dump( dropTerritory ); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////// END OF STRATEGY ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //printf("set command idle\n"); set_command_idle(); //printf("end set command idle"); for (;;) { pause(10); printf("still idling\n"); yield(); } return 0; }
The entire code
team8 code