My Version of CreaBotLib
Fork of CreaBotLib by
CreaBot.cpp
- Committer:
- sepp_nepp
- Date:
- 2019-01-03
- Revision:
- 9:efe9f76d6f76
- Parent:
- 7:3a793ddc3490
- Child:
- 10:79509113310a
File content as of revision 9:efe9f76d6f76:
#include "CreaBot.h" // ***************************************************************** // BotCommand Structure Helper // ***************************************************************** // Struct Helper; set structure fields to values */ void BotCommand::set(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm) { command = Acommand; dist_cm = Adist_cm; turn_angle_deg= Aturn_angle_deg; }; // Struct Helper; set structure fields to values */ void BotCommand::set(BotCmdVerb Acommand, float Aparam) { command = Acommand; dist_cm = Aparam; turn_angle_deg= Aparam; }; // ***************************************************************** // Creabot Class // ***************************************************************** Creabot::Creabot(motor *left, motor *right, float diameter_wheel_cm, float bot_diam_cm): wheelLeft(left), wheelRight(right), { // TODO: To catch for illegal dimensions (<= 0) // Initialize wheel geometires wheelLeft.setDiam(wheel_diam_cm); wheelRight.setDiam(wheel_diam_cm); // setup the bot parameters: botDiameter = bot_diam_cm; ratio_wheel_bot=bot_diam_cm/wheel_diam_cm; botSpeed_cm_sec = DEFAULT_SPEED_CM_SEC; // Attach the Crabot functions as callbacks to the wheels wheelLeft ->callbackSet(this, &Creabot::wheelLeftStoppedCB); wheelRight->callbackSet(this, &Creabot::wheelRightStoppedCB); // Initialize own callback as empty extCallBack = NULL; // setup the default Directions and State Values: // used below as variable instead of always passing a direction wheelLeft ->defaultDirection = COUNTERCLOCKWISE; wheelLeft ->StateValue = LWHEEL_RUNS; wheelRight->defaultDirection = CLOCKWISE; wheelRight->StateValue = RWHEEL_RUNS; // Initialize the own status fields wheelsState = LRWHEELS_STOP; qReset(); } // ***************************************************************** // Queue Queue management // ***************************************************************** void Creabot::qMove(BotCmdVerb Acommand, float Aparam) { qMove(Acommand, Aparam, Aparam); } /* Add a new movement to the queue, if not full. * ATTENTION: * qExecuteNext() is called asynchronsously by the motor Callback wheelsAllStoppedCB() * qExecuteNext() tries to read access the next command in the queue. * A simultaneous read and write access to the same resources can lead to * conflict and must be carefully avoided. * This conflict concerns the read-modify-write access of variable Count. * It is avoided by using the qBlock variable. With qBlock set during increment * a simultaneous read access is blocked * Any changes to those write and read handlers must take into account that * wheelsAllStoppedCB() and qExecuteNext() may be called any time while * executing qMove command. */ void Creabot::qMove(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm) { if( !qIsFull() ) { cmd[writeIdx].set(Acommand, Aturn_angle_deg, Adist_cm); writeIdx++; if (writeIdx==DEPTH_Queue) writeIdx=0; qCollide = false; // colliding access to qCollide cannot happen while qBlock = false // if callback happens here, there is no collosion, the last old command gets already executed. qBlock = true; // if callback happens here while qBlock = true, the collision is remembered in qCollide Count++; qBlock = false; // if callback happens here there is no more blockage and the next (maybe the new) command is already being executed // in this case qCollide = false, and wheelsState!=LRWHEELS_STOP if ( qCollide || (wheelsState==LRWHEELS_STOP) ) // callback cannot happen here since the wheelState is already STOP, or Collide has happened before! pExecuteNext(); }; } // Queue is worked through while still any elements inside // ATTENTION read above consideration of conflicting accesses between qMove() and wheelsAllStoppedCB() // Think careful if a case can occur that pExecuteNext is called twice immediately following each other, // once by qMove() and once by wheelsAllStoppedCB() ??? void Creabot::qExecuteNext() { if ( !qIsEmpty() ) // see if something in the queue to execute { // Execute and then remove the oldest element at the tail of the Queue iExeCommand( cmd[readIdx] ); readIdx++; if (readIdx==DEPTH_Queue) readIdx=0; Count--; }; } // Immediately end All, the queue and the motor void Creabot::qStopAll() { qReset(); // empty the queue iStop(); // stop the motors, do not call external callback } //************************************************************************** // Using the BotCmdVerb to encode different commands, done immediately //************************************************************************** void Creabot::iMoveAndWait(BotCmdVerb moveType, float Aparam) { botMove(moveType,Aparam); botWaitEnd(); } void Creabot::iMoveAndWait(BotCmdVerb moveType, float Aturn_angle_deg, float Adist_cm) { botMove(moveType,Aturn_angle_deg,Adist_cm); botWaitEnd(); } void Creabot::iMove(BotCmdVerb moveType, float Aparam) { botMove(moveType, Aparam, Aparam); } void Creabot::iMove(BotCmdVerb moveType, float Aturn_angle_deg, float Adist_cm) { BotCommand Anew_cmd; Anew_cmd.set(moveType, Aturn_angle_deg, Adist_cm); executeCommand(&Anew_cmd); } /** High level, immediate: move bot according to prefilled command structures. * Recommended to use botMove() methods to fill the command structure correctly. * Branches to the moveXXXX() methods. For details see docs for those methods. * Warning: Collides with queued commands if called individually. * @param[in] <*cmd> Pointer to type BotCommand, the prefilled command structure,*/ void Creabot::iExeCommand(BotCommand *cmd) { switch (cmd->command) { case FORWARD: moveForward(cmd->dist_cm); break; case BACKWARD: moveBackward(cmd->dist_cm); break; case LEFT: moveLeft(cmd->turn_angle_deg, cmd->dist_cm); break; case RIGHT: moveRight(cmd->turn_angle_deg, cmd->dist_cm); break; case BACKLEFT: moveBackLeft(cmd->turn_angle_deg, cmd->dist_cm); break; case BACKRIGHT: moveBackRight(cmd->turn_angle_deg, cmd->dist_cm); break; case ROTATE: moveRotate(cmd->turn_angle_deg); break; case IDLE: default: break; }; }; // executeCommand /* spends all time with waits, returns only once wheelState = LRWHEELS_STOP */ void Creabot::iWaitEnd() { while(wheelsState!=LRWHEELS_STOP) wait(0.1); } /* spends all time with waits, returns only once wheelState = LRWHEELS_STOP, but maximum max_wait_ms milli seconds */ void Creabot::iWaitEnd(uint32_t max_wait_ms) { uint32_t waited=0; while( (wheelsState!=LRWHEELS_STOP) && (waited < max_wait_ms) ) { wait(0.1); waited += 100; } } // botWaitEnd void Creabot::iStop() { // Call the StopRun method, so wheels detach their tickers wheelLeft->StopRun(); wheelRight->StopRun(); // wheelsAllStoppedCB could handle the rest of the cleanup, but would also trigger external callback // Turn motor power off! wheelLeft->WheelOFF(); wheelRight->WheelOFF(); } // ***************************************************************** // Creabot Mid level control functions: // all directly access the wheel, no Queue */ // ***************************************************************** // set botSpeed_cm_sec parameter for next high level commands void Creabot::setSpeed(float AbotSpeed_cm_sec) { if (AbotSpeed_cm_sec > MAX_SPEED_CM_SEC) botSpeed_cm_sec = MAX_SPEED_CM_SEC; else if (AbotSpeed_cm_sec<MIN_SPEED_CM_SEC) botSpeed_cm_sec = MIN_SPEED_CM_SEC; else botSpeed_cm_sec = AbotSpeed_cm_sec; // Low level setting of wheel speed is done inside SetMotsSpeed: } // Mid level control function: advance bot straight forward for a given distance void Creabot::moveForward(float dist_cm) { if (dist_cm<0) moveBackward(-dist_cm); else { float rot_angle_deg = dist_cm*wheeldims.degree_per_cm; wheelsSetSpeed(botSpeed_cm_sec); wheelLeftRun (rot_angle_deg); wheelRightRun(rot_angle_deg); } } // Mid level control function: reverse bot straight backwards for a given distance void Creabot::moveBackward(float dist_cm) { if(dist_cm<0) moveForward(-dist_cm); else { float rot_angle_deg = dist_cm*wheeldims.degree_per_cm; wheelsSetSpeed(botSpeed_cm_sec); wheelLeftRun (-rot_angle_deg); wheelRightRun(-rot_angle_deg); } } /* Mid level control function: turn bot forward right, around a radius twice the bot size */ void Creabot::moveRight(float turn_angle_deg) { wheelLeft->setSpeed_cm_sec(botSpeed_cm_sec); wheelLeftRun(COUNTERCLOCKWISE, wheelLeft.calcAngle(turn_angle_deg,botDiameter)); } /* Mid level control function: turn bot forward right, around a radius that is center_cm away from the right wheel*/ void Creabot::moveRight(float turn_angle_deg, float turn_radius_cm) { float wheelAngLeft = wheelLeft.calcAngle(turn_angle_deg,turn_radius_cm+botDiameter); float wheelAngRight = wheelRight.calcAngle(turn_angle_deg,turn_radius_cm); wheelLeft->setSpeed_cm_sec(botSpeed_cm_sec); wheelRight->setSpeed_cm_sec(botSpeed_cm_sec * wheelAngRight/wheelAngLeft); wheelLeftRun(COUNTERCLOCKWISE, angleLeft); wheelRightRun(CLOCKWISE, angleRight); } /* Mid level control function: turn bot forward left, around a radius twice the bot size */ void Creabot::moveLeft(float turn_angle_deg) { moveLeft(turn_angle_deg,0); } /* Mid level control function: turn bot forward left, around a radius that is center_cm away from the left wheel*/ void Creabot::moveLeft(float turn_angle_deg, float center_cm) { if (center_cm<0) center_cm=0; /* TO remove? */ float perimeter_outer = 2.0f*turn_angle_deg*(center_cm+botDiameter)*PI/360.0f; float perimeter_inner = 2.0f*turn_angle_deg*(center_cm)*PI/360.0f; float angleLeft = perimeter_inner*wheeldims.degree_per_cm; float angleRight = perimeter_outer*wheeldims.degree_per_cm; float ratio = angleLeft/angleRight; float speedRLeft = botSpeed_cm_sec * ratio; wheelLeft->setSpeed_cm_sec(speedRLeft); wheelRight->setSpeed_cm_sec(botSpeed_cm_sec); wheelLeftRun(COUNTERCLOCKWISE, angleLeft); wheelRightRun(CLOCKWISE, angleRight); } /* Mid level control function: turn bot on its place for a given angle. positive angles turn right, negative angles turn left*/ void Creabot::moveRotate(float angleBot) { float angleWheel = angleBot*ratio_wheel_bot; setSpeed(botSpeed_cm_sec); if(angleBot<0) { wheelLeftRun(CLOCKWISE, -angleWheel); wheelRightRun(CLOCKWISE, -angleWheel); } else { wheelLeftRun(COUNTERCLOCKWISE, angleWheel); wheelRightRun(COUNTERCLOCKWISE, angleWheel); } } //****************************************************************************** // low level wheel functions, only filters for rot_angle_deg>0, then passes // to wheel Class; and remembers that a wheel moves //****************************************************************************** /* Low level access functions: sets both wheel speeds immediately */ void Creabot::wheelsSetSpeed(float mot_speed_cm_sec) { wheelLeft->setSpeed_cm_sec(mot_speed_cm_sec); wheelRight->setSpeed_cm_sec(mot_speed_cm_sec); } void Creabot::wheelLeftRun(float rot_angle_deg) { if (rot_angle_deg>=0) wheelLeft->RunDegrees(wheelLeft->defaultDirection, rot_angle_deg); else wheelLeft->RunDegrees(! wheelLeft->defaultDirection, -rot_angle_deg); if ( (wheelsState==RWHEEL_RUNS) || (wheelsState==LRWHEELS_RUN) ) wheelsState=LRWHEELS_RUN; else wheelsState=LWHEEL_RUNS; } } void Creabot::wheelRightRun(float rot_angle_deg) { if(rot_angle_deg>0) { wheelRight->RunDegrees(dir, rot_angle_deg); if ( (wheelsState==LWHEEL_RUNS) || (wheelsState==LRWHEELS_RUN) ) wheelsState=LRWHEELS_RUN; else wheelsState=RWHEEL_RUNS; } } void Creabot::wheelRunCentimeters(AWheel *Wheel, float dist_cm) { AWheel->RunCentimeters(dist_cm); // Direction used is defaultDirection wheelsState = wheelsState | AWheel->StateValue; // bitwise or of state value, can be either LWHEEL_RUNS or RWHEEL_RUNS } //****************************************************************************** // low level wheel functions, handles callbacks from wheels // ***************************************************************************** void Creabot::wheelLeftStoppedCB() { wheelsState = wheelsState & ! wheelLeft->StateValue; // mask out Left Wheel status bit if (wheelsState == LRWHEELS_STOP) // all wheels are now known to have stopped wheelsAllStoppedCB(); } void Creabot::wheelRightStoppedCB() { wheelsState = wheelsState & ! wheelRight->StateValue; // mask out Left Wheel status bit if (wheelsState == LRWHEELS_STOP) // all wheels are now known to have stopped wheelsAllStoppedCB(); } void Creabot::wheelsAllStoppedCB() { // assume that already at entry wheelsState == LRWHEELS_STOP // Try to execute next entry in queue, if one is present if ( !qBlock ) { qExecuteNext();// not blocked, so can execute now // if wheels are still STOPed, that means no command to execute was left in the queue if (wheelsState==LRWHEELS_STOP) { // Turn motor power off! wheelLeft->WheelOFF(); wheelRight->WheelOFF(); // Call the externall callback function to inform about the end of all movements if(extCallBack!=NULL) extCallBack( wheelsState ); } // if wheelsState==LRWHEELS_STOP } // if ( !qBlock ) else qCollide = true; // Remember the collision to execute the next command. }