SPYSpheres
|
00001 //ZRHS2014 00002 #include <string.h> 00003 #include <math.h> 00004 00005 #include "ZRGame.h" 00006 #include "ZRGameInternal.h" 00007 #include "ZR_API_internal.h" 00008 #include "ZR_API.h" 00009 #include "Constants.h" 00010 00011 //SPHERES C includes 00012 extern "C" { 00013 #include "commands.h" 00014 #include "system.h" 00015 #include "math_matrix.h" 00016 #include "gsp.h" 00017 #include "control.h" 00018 #include "find_state_error.h" 00019 #include "housekeeping_internal.h" 00020 } 00021 00022 /** 00023 * This file is used to define the C++ implementation of both the public and the 00024 * private game API functions. Public functions will be prefixed with ZeroRoboticsGame:: 00025 * while private implementation functions will be prefixed with ZeroRoboticsGameImpl:: 00026 */ 00027 00028 /** 00029 * Use these vectors to choose the satellites state when the user is given control. 00030 */ 00031 #if SPHERE_ID==SPHERE1 00032 state_vector initStateZR = {0.4,-0.6,0, 0,0,0, 0.7071,-0.7071,0,0, 0,0,0}; 00033 #else 00034 state_vector initStateZR = {-0.4,-0.6,0, 0,0,0, 0.7071,-0.7071,0,0, 0,0,0}; 00035 #endif 00036 00037 //======= Singleton Instance of Game and Implementation=====// 00038 ZeroRoboticsGameImpl &ZeroRoboticsGameImpl::instance() { 00039 ///Singleton instance of the game implementation 00040 static ZeroRoboticsGameImpl gameImplInstance(ZeroRoboticsAPIImpl::instance()); 00041 return gameImplInstance; 00042 } 00043 ZeroRoboticsGame &ZeroRoboticsGame::instance() { 00044 static ZeroRoboticsGame gameInstance(ZeroRoboticsGameImpl::instance(), ZeroRoboticsAPIImpl::instance()); 00045 return gameInstance; 00046 } 00047 00048 //======== Constructors ========// 00049 00050 //Constructor for ZeroRobotics game with initializers for the ZR API. Do not modify. 00051 ZeroRoboticsGame::ZeroRoboticsGame(ZeroRoboticsGameImpl &impl, ZeroRoboticsAPIImpl &apiImpl): pimpl(impl), apiImpl(apiImpl) 00052 { 00053 impl.game = this; 00054 } 00055 00056 ZeroRoboticsGameImpl::ZeroRoboticsGameImpl(ZeroRoboticsAPIImpl &apiImpl) : apiImpl(apiImpl) {} 00057 00058 00059 //Internal accessor for game implementation 00060 ZeroRoboticsGameImpl *getGameImpl() 00061 { 00062 return &ZeroRoboticsGameImpl::instance(); 00063 } 00064 00065 //======== GAME IMPLEMENTATION METHODS ========// 00066 00067 00068 //Add methods here for the public game 00069 00070 void ZeroRoboticsGameImpl::init() 00071 { 00072 /* Game Initialization setup. 00073 * Then set the locations of the memory packs. 00074 */ 00075 00076 apiImpl.gameTime = GAME_TIME; 00077 memset(&challInfo,0,sizeof(ChallengeInfo)); // general clear of challInfo to 0 = false = empty 00078 00079 // update the state data on challInfo 00080 updateStates(); 00081 challInfo.me.score = START_SCORE; 00082 00083 // Lights, Camera, Action! 00084 initLight(); 00085 initCamera(); 00086 initEnergy(); 00087 00088 initItems(); 00089 00090 00091 00092 // send information to ground and sat2 at the end of init 00093 sendDebug(); 00094 00095 } 00096 00097 void ZeroRoboticsGameImpl::updateStates() { 00098 apiImpl.api->getMyZRState(challInfo.me.zrState); 00099 apiImpl.api->getOtherZRState(challInfo.other.zrState); 00100 00101 apiImpl.api->getMySphState(challInfo.me.sphState); 00102 apiImpl.api->getOtherSphState(challInfo.other.sphState); 00103 00104 challInfo.currentTime = apiImpl.api->getTime(); 00105 00106 challInfo.camera.tookPicture = false; 00107 challInfo.camera.uploadedPictures = false; 00108 } 00109 00110 bool ZeroRoboticsGameImpl::update(float forceTorqueOut[6]) { 00111 updateStates(); 00112 updateLight(); 00113 updateEnergy(); 00114 00115 // empty the userForces array 00116 memset(challInfo.me.userForces,0,sizeof(float)*6); 00117 00118 // declare a static finalResult variable 00119 // updateGameOverManuever will change it 00120 static unsigned char finalResult = 0; 00121 00122 unsigned char useForces = false; 00123 00124 // maneuver 3 = game mode 00125 // maneuver 202 = end game (get final score) 00126 // maneuver 204 = terminate 00127 00128 switch (ctrlManeuverNumGet()) 00129 { 00130 00131 case 3: 00132 useForces = updateGameModeManeuver(forceTorqueOut); 00133 break; 00134 00135 //------End main game code------// 00136 00137 case 202: // Endgame scenario 00138 useForces = updateGameOverManeuver(forceTorqueOut, finalResult); 00139 break; 00140 00141 case 203: // terminate test after determining final score 00142 { 00143 ctrlTestTerminate(finalResult); 00144 } 00145 break; 00146 } 00147 00148 //Store user forces for sending to ground 00149 memcpy(challInfo.me.userForces, forceTorqueOut, sizeof(float)*6); 00150 00151 #ifdef ZR2D 00152 memset(&challInfo.me.userForces[2], 0, sizeof(float)*3); 00153 #endif 00154 00155 //Send Telemetry 00156 sendDebug(); 00157 00158 return useForces; 00159 00160 } 00161 00162 #ifdef ZR2D 00163 static float pintegralX = 0.0f, pintegralY = 0.0f, pintegralZ = 0.0f; 00164 #endif 00165 00166 void ZeroRoboticsGameImpl::modify2DForceTorque(float forceTorque[6]) 00167 { 00168 state_vector myState, targetState; 00169 apiImpl.api->getMySphState(myState); 00170 memcpy(targetState, myState, sizeof(state_vector)); 00171 00172 targetState[POS_Z] = 0.0f; 00173 targetState[VEL_Z] = 0.0f; 00174 targetState[10] = 0.0f; 00175 targetState[11] = 0.0f; 00176 targetState[12] = 0.0f; 00177 00178 float q_sw[4], q_tw[4]; 00179 float axis[3] = {0.0f, 0.0f, -1.0f}; 00180 swingTwistDecomposition(&targetState[QUAT_1], q_sw, q_tw, axis); 00181 quatToPositive(q_tw); 00182 memcpy(&targetState[QUAT_1], q_tw, 4*sizeof(float)); 00183 00184 state_vector error; 00185 findStateError(error, myState, targetState); 00186 00187 float newForces[6] = {0}; 00188 00189 ctrlPositionPD(KPpositionPD, KDpositionPD, error, newForces); 00190 ctrlAttitudeNLPIDwie(KPattitudePID, KIattitudePID, KDattitudePID, 00191 KPattitudePID, KIattitudePID, KDattitudePID, 00192 KPattitudePID, KIattitudePID, KDattitudePID, 00193 1.0f, error, newForces); 00194 forceTorque[FORCE_Z] = newForces[FORCE_Z]; 00195 forceTorque[TORQUE_X] = newForces[TORQUE_X]; 00196 forceTorque[TORQUE_Y] = newForces[TORQUE_Y]; 00197 } 00198 00199 // Decomposes the quaternion into swing and twist rotation quaternions. 00200 // More info http://www.alinenormoyle.com/weblog/?p=726. 00201 void swingTwistDecomposition(float q[4], float q_sw[4], 00202 float q_tw[4], float tw_axis[3]) { 00203 00204 // p = dot(q[0:2], tw_axis) * tw_axis 00205 // q_tw = [p[0], p[1], p[2], q[3]] 00206 float c = mathVecInner(q, tw_axis, 3); 00207 for (int i = 0; i < 3; i++) { 00208 q_tw[i]= tw_axis[i] * c; 00209 } 00210 q_tw[3] = q[3]; 00211 mathVecNormalize(q_tw, 4); 00212 00213 // invert twist quaternion 00214 float q_tw_inv[4]; 00215 memcpy(q_tw_inv, q_tw, 4*sizeof(float)); 00216 for (int i = 0; i < 3; i++) { 00217 q_tw_inv[i] *= -1.0f; 00218 } 00219 00220 quatMult(q_sw, q_tw_inv, q); 00221 } 00222 00223 void quatToPositive(float quat[4]) { 00224 if (quat[3] < 0) { 00225 for (int i = 0; i < 4; i++) { 00226 quat[4] *= -1; 00227 } 00228 } 00229 } 00230 00231 bool ZeroRoboticsGameImpl::updateGameModeManeuver(float forceTorqueOut[6]) { 00232 float collisionInfo[2]; 00233 00234 // get state-of-health of other satellite (mainly for end of test) 00235 comm_payload_soh soh_partner; 00236 #if (SPHERE_ID == SPHERE1) 00237 commBackgroundSOHGet(SPHERE2, &soh_partner); 00238 #else 00239 commBackgroundSOHGet(SPHERE1, &soh_partner); 00240 #endif 00241 00242 // determine current speed of satellite, use to determine collision avoidance motion 00243 float speed = mathVecMagnitude(&challInfo.me.sphState[3],3); 00244 00245 unsigned char useForces = false; 00246 00247 // if camera was used (so it can't be used again for 3 seconds), check when to enable it again 00248 if((!challInfo.camera.cameraOn) && ((challInfo.currentTime - challInfo.camera.cameraOffTime) > DISABLE_CAMERA_TIME)) 00249 { 00250 activateCamera(); 00251 } 00252 00253 // Run user's "loop" function. apiImpl is used because we are accessing 00254 // a private part of the API containing a pointer to the user's API 00255 apiImpl.zruser->loop(); 00256 00257 //Check if items are being picked up 00258 for (int i = 0; i < NUM_ITEMS; i++) 00259 itemPickUp(i); 00260 00261 useForces = useForces || apiImpl.getForceTorque(forceTorqueOut); 00262 00263 prop_time tent_times; 00264 #ifdef ZRFLATFLOOR 00265 ctrlMixWLoc(&tent_times, forceTorqueOut, challInfo.me.sphState, 10, 40.0f, FORCE_FRAME_INERTIAL); 00266 #else 00267 ctrlMixWLoc(&tent_times, forceTorqueOut, challInfo.me.sphState, 10, 20.0f, FORCE_FRAME_INERTIAL); 00268 #endif 00269 00270 float tent_time_sum = 0; 00271 for (int i = 0; i < 12; i++) { 00272 tent_time_sum += tent_times.off_time[i]-tent_times.on_time[i]; 00273 } 00274 00275 //Check for fuel or energy out at this point 00276 if((game->getFuelRemaining() <= 0.0f || game->getEnergy() <= tent_time_sum * ENERGY_COST_THRUSTERS)) 00277 { 00278 memset(forceTorqueOut,0,6*sizeof(float)); // anything the user has commanded is ignored, only firings would be from out-of-bounds or collission avoidance 00279 useForces = false; // assume there will be no firings 00280 } 00281 00282 //Enforce boundaries of the game 00283 if (enforceBoundaries(forceTorqueOut)) 00284 { 00285 //challInfo.me.fuelUsed += OFFSIDES_PENALTY; 00286 GAME_TRACE(("[%d] out of bounds penalty | ", apiImpl.api->getTime())); 00287 useForces = true; 00288 } 00289 00290 //Collision avoidance 00291 //check to see that its speed is not neglible. 00292 if (speed > 0.01f) { 00293 challInfo.me.collisionActive = ctrlAvoidWithOverride(challInfo.me.sphState,&challInfo.other.sphState,1,forceTorqueOut,collisionInfo,3); 00294 } 00295 00296 //------Thruster control------// 00297 // Run standard mixer to get fuel consumption 00298 prop_time times; 00299 ctrlMixWLoc(×, forceTorqueOut, challInfo.me.sphState, 10, 20.0f, FORCE_FRAME_INERTIAL); 00300 00301 //Tally fuel and energy used for firing thrusters 00302 for (int i=0; i<12; i++) 00303 { 00304 float thrusterTime = times.off_time[i]-times.on_time[i]; 00305 00306 challInfo.me.fuelUsed += thrusterTime / 1000.0f; 00307 00308 tryToUseEnergy(thrusterTime * ENERGY_COST_THRUSTERS); 00309 00310 00311 //Protect against NAN or INF hacking 00312 if (challInfo.me.fuelUsed < 0.0f) 00313 { 00314 DEBUG(("WARNING: invalid forces/torques applied to satellite at t=%d. Fuel will be set to 0. Check your code for errors.\n", challInfo.currentTime)); 00315 challInfo.me.fuelUsed = PROP_ALLOWED_SECONDS; 00316 } 00317 } 00318 00319 #ifdef ZR2D 00320 modify2DForceTorque(forceTorqueOut); 00321 useForces = true; 00322 #endif 00323 00324 //------Game end conditions------// 00325 if (challInfo.currentTime >= (MAX_GAME_TIME)) 00326 ctrlManeuverNumSet(202); 00327 00328 return useForces; 00329 } 00330 00331 bool ZeroRoboticsGameImpl::updateGameOverManeuver(float forceTorqueOut[6], unsigned char& finalResult) { 00332 unsigned char useForces = false; 00333 int result; 00334 00335 float collisionInfo[2]; 00336 00337 // get state-of-health of other satellite (mainly for end of test) 00338 comm_payload_soh soh_partner; 00339 #if (SPHERE_ID == SPHERE1) 00340 commBackgroundSOHGet(SPHERE2, &soh_partner); 00341 #else 00342 commBackgroundSOHGet(SPHERE1, &soh_partner); 00343 #endif 00344 00345 float myScore; 00346 float otherScore; 00347 00348 myScore = game->getScore(); 00349 otherScore = game->getOtherScore(); 00350 00351 useForces = ctrlAvoidWithOverride(challInfo.me.sphState, &challInfo.other.sphState, 1, forceTorqueOut, collisionInfo, 3) > 0; 00352 useForces = useForces || enforceBoundaries(forceTorqueOut); 00353 00354 // break ties by making a tiny change based on the random state 00355 if (myScore == otherScore) 00356 { 00357 challInfo.me.score -= (0.01f * sqrtf((challInfo.me.zrState[0]*challInfo.me.zrState[0]) 00358 +(challInfo.me.zrState[1]*challInfo.me.zrState[1])+(challInfo.me.zrState[2]*challInfo.me.zrState[2]))); 00359 GAME_TRACE (("Tie Breaker (%12.10f = %12.10f), new score = %12.10f | ", myScore, otherScore, challInfo.me.score)); 00360 } 00361 00362 // get final score 00363 if ((soh_partner.last_test_result || (soh_partner.maneuver_number > 200) || (NUM_SPHERES == 1))) 00364 { 00365 result = 10*floorf(myScore); 00366 if (result < 10) result = 10; 00367 if (result > 220) result = 220; 00368 if (myScore > otherScore) 00369 result += 10; 00370 finalResult = (result + apiImpl.getTeamId() + 1); 00371 useForces = false; 00372 } 00373 00374 // terminate after we find the final result & timeout (4s) 00375 if (finalResult && (ctrlManeuverTimeGet() > 4000)) 00376 { 00377 GAME_TRACE (("GAME ENDED! Final Score Float: %10.8f / Integer: %d | Test Result %d | ", myScore, result, finalResult)); 00378 ctrlManeuverTerminate(); 00379 } 00380 return useForces; 00381 } 00382 00383 bool ZeroRoboticsGameImpl::enforceBoundaries(float forceTorqueOut[6]) 00384 { 00385 state_vector sphState; 00386 apiImpl.api->getMySphState(sphState); 00387 bool retValue = false; 00388 00389 //Run velocity controller to stop motion out of volume 00390 //Zero out any controls that are leading the user out of the volume 00391 for (int i=0; i<3; i++) 00392 { 00393 if (sphState[POS_X+i] > limits[i]) 00394 { 00395 limitDirection(sphState, forceTorqueOut, i, 1.0f); 00396 retValue = true; 00397 } 00398 else if (sphState[POS_X+i] < -limits[i]) 00399 { 00400 limitDirection(sphState, forceTorqueOut, i, -1.0f); 00401 retValue = true; 00402 } 00403 } 00404 return retValue; 00405 } 00406 00407 void ZeroRoboticsGameImpl::limitDirection(state_vector ctrlState, float ctrlControl[6], unsigned int idx, float dir) 00408 { 00409 // if command is to move out further in same direction as limit 00410 if (ctrlControl[idx]*dir >= 0.0f) 00411 { 00412 ctrlControl[idx] = -1.0f * OOBgain * ctrlState[VEL_X+idx]; // slow down to get 0 velocity (ignore position) 00413 } 00414 } 00415 00416 /******************Collision Avoidance Functions************************/ 00417 void ZeroRoboticsGameImpl::resolveCollision(float zrState[12], float forceTorqueOut[6]) 00418 { 00419 for (int i=0; i<3; i++) 00420 { 00421 if (zrState[i] > 0) 00422 limitDirection(zrState, forceTorqueOut, i, -1.0f); 00423 else if (zrState[i] < 0) 00424 limitDirection(zrState, forceTorqueOut, i, 1.0f); 00425 } 00426 } 00427 00428 float ZeroRoboticsGameImpl::dist3d(float* pos1, float* pos2) const 00429 { 00430 float tmpVec[3]; 00431 mathVecSubtract(tmpVec,static_cast<float*>(pos1),static_cast<float*>(pos2),3); 00432 return mathVecMagnitude(tmpVec,3); 00433 } 00434 00435 00436 00437 /*************************Score functions***********************/ 00438 float ZeroRoboticsGame::getScore() 00439 { 00440 return pimpl.challInfo.me.score; 00441 } 00442 00443 float ZeroRoboticsGame::getOtherScore() 00444 { 00445 return pimpl.challInfo.other.score; 00446 } 00447 00448 //======== PUBLIC API METHODS ========// 00449 //------General Functions------// 00450 00451 float ZeroRoboticsGame::getFuelRemaining() 00452 { 00453 ZeroRoboticsGameImpl::ChallengeInfo& challInfo = pimpl.challInfo; 00454 float total = PROP_ALLOWED_SECONDS-challInfo.me.fuelUsed; 00455 total = MIN_FUEL(total, 0.0f); //~Prevents fuel from being less than 0.0f 00456 total = MAX_FUEL(total, PROP_ALLOWED_SECONDS); //~Prevents fuel from being greater than MAX fuel in tank 00457 return total; 00458 } 00459 00460 int ZeroRoboticsGame::getCurrentTime() 00461 { 00462 return pimpl.challInfo.currentTime; 00463 } 00464 00465 void ZeroRoboticsGame::sendMessage(unsigned char inputMsg) 00466 { 00467 pimpl.challInfo.me.message = (short)inputMsg; 00468 } 00469 00470 unsigned char ZeroRoboticsGame::receiveMessage() 00471 { 00472 return (unsigned char)pimpl.challInfo.other.message; 00473 }