/*$Id: brain.c,v 1.83 1997/02/22 12:47:52 nwv Exp $*/ /***************************************************************************** * * * BRAIN.C * * * * Mapping Robot CS148 Final Project * * Murat Gorguner (mg@cs.brown.edu) & Neil W. Van Dyke (nwv@cs.brown.edu) * * CS148, Building Intelligent Robots, Brown University, April 1996 * * * * For use with Interactive C 3.1b3 and the MIT 6.270 2.21 board. * * Requires that the "encoders.lis" shaft encoder support be loaded. * * * *****************************************************************************/ /***************************************************************************** * NOTE: This current version of the file is RCS version 1.82 with dead code * * removed and this comment added. This is the C code for the robot- * * resident control program for Magellan. If you are interested in * * the less important Java map viewer, the Perl SPARCstation serial * * port code, or the Neil's course paper, please contact Neil at * * "nwv@cs.brown.edu" or "nwv@acm.org". Also, if you get any use out * * of this code, I'd appreciate hearing of it. * *****************************************************************************/ /*-------------------------------------------------- Configuration Constants */ /* Misc. */ #define CmProtrudingPastSideRearSensor 0 #define CmTraveledPerWheelEncoderCount 1.67 #define FrontWallSensorThresholdForLeftTurn 8 #define IdealWallFollowingDistance 5 #define Map_MaxSegmentCount 500 #define RobotLengthCm 16 #define Serial_Enabled True #define WallFollowAdjustmentFactor 10 #define WheelEncoderCountsPer90DegreeTurn 6 /* Motor Calibration */ #define DriveMotors_LeftMotorPercent 90 #define DriveMotors_RightMotorPercent 100 /* Analog Ports */ #define WallSensors_FrontDetector 23 #define WallSensors_SideFrontDetector 25 #define WallSensors_SideRearDetector 27 /* Digital Ports */ #define WheelEncoding_LeftEncoder 0 #define WheelEncoding_RightEncoder 1 /* Motor Ports */ #define DriveMotors_RightMotor 0 #define DriveMotors_LeftMotor 1 #define WallSensors_FrontEmitter 2 #define WallSensors_SideEmitters1 3 #define WallSensors_SideFrontEmitter2 4 #define WallSensors_SideRearEmitter2 5 /*------------------------------------------------------------------ Generic */ /* Boolean Type */ #define Boolean int #define True 1 #define False 0 /* Integer Absolute Value */ int abs(int n) { if (n < 0) return (-(n)); return n; } /* Integer Average */ int avg(int a, int b) { return (a + b) / 2; } /* Floating-Point Min (from "menu.c") */ float fmin(float a, float b) { if (a < b) return(a); return(b); } /* Integer Max */ int max(int a, int b) { if (a > b) return(a); return(b); } int sqr(int n) { return (n * n); } /*---------------------------------------------------------------------- Map */ /* Map Turn Codes */ #define Map_TurnCode_Left -1 #define Map_TurnCode_None 0 #define Map_TurnCode_Right 1 #define Map_EnsureSegmentComplete() { \ if (Map_WaitingForTurnCode) { \ Map_AddTurnCode(Map_TurnCode_None); \ } \ } int Map_SegmentCount; int Map_SegmentLength[Map_MaxSegmentCount]; int Map_SegmentTurnCode[Map_MaxSegmentCount]; Boolean Map_WaitingForTurnCode; /* * Note: No overflow checking is done on the map data arrays. */ void Map_AddLength(int length) { Map_EnsureSegmentComplete(); Map_SegmentLength[Map_SegmentCount] = length; Map_WaitingForTurnCode = True; } void Map_AddLengthFromWheelEncoding() { Map_AddLength(WheelEncoding_MaxCounterValueInCm()); } void Map_AddTurnCode(int turn) { if (Map_WaitingForTurnCode) { Map_SegmentTurnCode[Map_SegmentCount] = turn; Map_SegmentCount++; Map_WaitingForTurnCode = False; } else { Map_AddSegment(0, turn); } } void Map_AddSegment(int length, int turn) { Map_EnsureSegmentComplete(); Map_SegmentLength[Map_SegmentCount] = length; Map_SegmentTurnCode[Map_SegmentCount] = turn; Map_SegmentCount++; } void Map_Finalize() { Map_EnsureSegmentComplete(); } void Map_Initialize() { Map_SegmentCount = 0; Map_WaitingForTurnCode = False; } /*------------------------------------------------------------ WheelEncoding */ #define WheelEncoding_Start() { \ enable_encoder(WheelEncoding_LeftEncoder); \ enable_encoder(WheelEncoding_RightEncoder); \ } #define WheelEncoding_Stop() { \ disable_encoder(WheelEncoding_LeftEncoder); \ disable_encoder(WheelEncoding_RightEncoder); \ } #define WheelEncoding_CounterValue(port) read_encoder(port) #define WheelEncoding_CounterValueInCm(port) \ (int)((float)WheelEncoding_CounterValue(port) * CmTraveledPerWheelEncoderCount) #define WheelEncoding_AverageCounterValue() \ ((WheelEncoding_CounterValue(WheelEncoding_LeftEncoder) + \ WheelEncoding_CounterValue(WheelEncoding_RightEncoder)) \ / 2) #define WheelEncoding_MaxCounterValue() \ max(WheelEncoding_CounterValue(WheelEncoding_LeftEncoder), \ WheelEncoding_CounterValue(WheelEncoding_RightEncoder)) #define WheelEncoding_MaxCounterValueInCm() \ (int)((float)WheelEncoding_MaxCounterValue() * \ CmTraveledPerWheelEncoderCount) #define WheelEncoding_WaitUntilNCounts(n) { \ while ((WheelEncoding_CounterValue(WheelEncoding_LeftEncoder) < (n)) && \ (WheelEncoding_CounterValue(WheelEncoding_RightEncoder) < (n))) \ /*NOP*/; \ } /*-------------------------------------------------------------- DriveMotors */ #define DriveMotors_IntegerPercent(n,percent) \ ((((n) * (percent)) / 100) * 80 / 100) #define DriveMotors_SetFullForward() { \ DriveMotors_SetSpeeds(100, 100); \ } int DriveMotors_LeftMotorSpeed; int DriveMotors_RightMotorSpeed; void DriveMotors_Initialize() { DriveMotors_LeftMotorSpeed = 0; DriveMotors_RightMotorSpeed = 0; } void DriveMotors_MoveForwardNCm(int cm) { DriveMotors_TurnOff(); WheelEncoding_Start(); DriveMotors_SetFullForward(); WheelEncoding_WaitUntilNCounts((int)((float)cm / CmTraveledPerWheelEncoderCount)); WheelEncoding_Stop(); DriveMotors_TurnOff(); } void DriveMotors_SetSpeeds(int left, int right) { if (left != DriveMotors_LeftMotorSpeed) { motor(DriveMotors_LeftMotor, DriveMotors_IntegerPercent(left, DriveMotors_LeftMotorPercent)); DriveMotors_LeftMotorSpeed = left; } if (right != DriveMotors_RightMotorSpeed) { motor(DriveMotors_RightMotor, DriveMotors_IntegerPercent(right, DriveMotors_RightMotorPercent)); DriveMotors_RightMotorSpeed = right; } Status_Update(); } void DriveMotors_TurnOff() { off(DriveMotors_LeftMotor); off(DriveMotors_RightMotor); DriveMotors_LeftMotorSpeed = 0; DriveMotors_RightMotorSpeed = 0; Status_Update(); } void DriveMotors_Turn90Degrees(int direction) { DriveMotors_TurnOff(); WheelEncoding_Start(); DriveMotors_SetSpeeds((50 * direction), (50 * direction * -1)); WheelEncoding_WaitUntilNCounts(WheelEncoderCountsPer90DegreeTurn); DriveMotors_SetSpeeds((50 * direction), 0); WheelEncoding_WaitUntilNCounts(WheelEncoderCountsPer90DegreeTurn + 1); DriveMotors_TurnOff(); WheelEncoding_Stop(); } void DriveMotors_TurnLeft90Degrees() { DriveMotors_Turn90Degrees(-1); } void DriveMotors_TurnRight90Degrees() { DriveMotors_Turn90Degrees(1); } /*------------------------------------------------------------------- Escape */ void Escape_Handle() { int leftSpeed = DriveMotors_LeftMotorSpeed; int rightSpeed = DriveMotors_RightMotorSpeed; DriveMotors_TurnOff(); while (escape_button()) /*NOP*/; UI_DisplayTwoLines("Connect to Sun,", "press CHOOSE."); UI_WaitForChooseButton(); Escape_TransmitMap(); UI_DisplayTwoLines("Press CHOOSE to", "resume run."); UI_WaitForChooseButton(); DriveMotors_SetSpeeds(leftSpeed, rightSpeed); } void Escape_TransmitMap() { int i; UI_DisplayLine("Sending..."); Map_EnsureSegmentComplete(); Serial_Start(); for (i = 0; i < Map_SegmentCount; i++) { int length = Map_SegmentLength[i]; int turnCode = Map_SegmentTurnCode[i] + 1; printf("%d: %d %d\n", i, length, turnCode); Serial_SendChr(1); Serial_SendChr(length); Serial_SendChr(turnCode); sleep(0.1); } Serial_SendChr(0); Serial_Stop(); UI_DisplayLine("Send done."); } /*-------------------------------------------------------------------- Sound */ int Sound_StateChangeBeep() { beep(); } /*-------------------------------------------------------------- WallSensors */ #define WallSensors_NoWallReading -1 #define WallSensors_TurnOffEmitter(emitterPort) off(emitterPort) #define WallSensors_TurnOnEmitter(emitterPort) fd(emitterPort) int WallSensors_FrontReading; int WallSensors_SideFrontReading; int WallSensors_SideRearReading; int WallSensors_FrontFirst; int WallSensors_SideFrontFirst; int WallSensors_SideRearFirst; float WallSensors_Calibration; #define WallSensors_GetCorrectedReading(sensor) { \ int reading = WallSensors_Reading_ ## sensor \ ((int)(((float)analog(WallSensors_ ## sensor ## Detector)) * \ WallSensors_Calibration)); \ if ((WallSensors_ ## sensor ## Reading == WallSensors_NoWallReading) || \ (reading == WallSensors_NoWallReading)) { \ WallSensors_ ## sensor ## Reading = reading; \ } else if (WallSensors_ ## sensor ## First) { \ WallSensors_ ## sensor ## First = False; \ WallSensors_ ## sensor ## Reading = reading; \ } else if ((reading - WallSensors_ ## sensor ## Reading) > 2) { \ WallSensors_ ## sensor ## Reading = WallSensors_NoWallReading; \ } else { \ WallSensors_ ## sensor ## Reading = reading; \ } \ } void WallSensors_GetReadings() { WallSensors_GetCorrectedReading(Front); WallSensors_GetCorrectedReading(SideFront); WallSensors_GetCorrectedReading(SideRear); } void WallSensors_Initialize() { /* Turn on the emitters. */ WallSensors_TurnOnEmitter(WallSensors_FrontEmitter); WallSensors_TurnOnEmitter(WallSensors_SideEmitters1); WallSensors_TurnOnEmitter(WallSensors_SideFrontEmitter2); WallSensors_TurnOnEmitter(WallSensors_SideRearEmitter2); WallSensors_FrontFirst = True; WallSensors_SideFrontFirst = True; WallSensors_SideRearFirst = True; } void WallSensors_Finalize() { WallSensors_TurnOffEmitter(WallSensors_FrontEmitter); WallSensors_TurnOffEmitter(WallSensors_SideEmitters1); WallSensors_TurnOffEmitter(WallSensors_SideFrontEmitter2); WallSensors_TurnOffEmitter(WallSensors_SideRearEmitter2); } /* * Note: This is gross. Please see paper for rationale. */ int WallSensors_Reading_Front(int a) { if (a > 247) return WallSensors_NoWallReading; if (a > 245) return 9; if (a > 242) return 8; if (a > 238) return 7; if (a > 231) return 6; if (a > 223) return 5; if (a > 215) return 4; if (a > 200) return 3; return 2; } int WallSensors_Reading_SideFront(int a) { if (a > 245) return WallSensors_NoWallReading; if (a > 240) return 14; if (a > 236) return 13; if (a > 234) return 12; if (a > 228) return 11; if (a > 224) return 10; if (a > 217) return 9; if (a > 211) return 8; if (a > 192) return 7; if (a > 172) return 6; if (a > 150) return 5; if (a > 123) return 4; if (a > 74) return 3; return 2; } int WallSensors_Reading_SideRear(int a) { if (a > 245) return WallSensors_NoWallReading; if (a > 240) return 14; if (a > 236) return 13; if (a > 234) return 12; if (a > 230) return 11; if (a > 226) return 10; if (a > 222) return 9; if (a > 213) return 8; if (a > 201) return 7; if (a > 185) return 6; if (a > 164) return 5; if (a > 127) return 4; if (a > 57) return 3; return 2; } int WallSensors_ReadingRelationToIdeal(int cm) { if (cm == IdealWallFollowingDistance) return 0; if (cm < IdealWallFollowingDistance) return -1; return 1; } /*------------------------------------------------------------------- Serial */ #define Serial_SendChr(chr) { \ if (Serial_Enabled) { \ poke(0x102f, chr); \ while (!(peek(0x102e) & 0x80)); \ } \ } #define Serial_SendNewline() { \ if (Serial_Enabled) { \ Serial_SendChr(0x0a); \ } \ } void Serial_SendStr(char str[]) { #if (Serial_Enabled == True) int len = _array_size(str); int i; for (i = 0; i < len; i++) { Serial_SendChr(str[i]); } #endif } void Serial_SendStrNl(char str[]) { #if (Serial_Enabled == True) Serial_SendStr(str); Serial_SendNewline(); #endif } void Serial_Start() { #if (Serial_Enabled == True) poke(0x3c, 0); #endif } void Serial_Stop() { #if (Serial_Enabled == True) poke(0x3c, 1); #endif } /*----------------------------------------------------------------------- UI */ #define UI_DisplayLine(str) printf("%s\n", str) #define UI_WaitForChooseButton() { \ while (!choose_button()) /*NOP*/; \ while (choose_button()) /*NOP*/; \ } Boolean UI_SleepOrChooseButton(float seconds) { float delay; int i; int times; delay = 0.10; times = (int)(seconds / delay); for (i = 0; i < times; i++) { if (choose_button()) { while (choose_button()) /*NOP*/; return True; } sleep(delay); } return False; } void UI_DisplayTwoLines(char line1[], char line2[]) { char buf[17]; Boolean end; int i; end = False; for (i = 0; i < 16; i++) { if (end) { buf[i] = 32; } else { if (line1[i] == 0) { end = True; buf[i] = 32; } else { buf[i] = line1[i]; } } } buf[16] = 0; printf("%s%s\n", buf, line2); } /*------------------------------------------------------------------- Status */ #define Status_Update() \ Status_UpdateWithMsg("") void Status_InternalError(char msg[]) { Status_UpdateWithMsg(msg); UI_WaitForChooseButton(); } void Status_UpdateWithMsg(char msg[]) { printf("%s L%d R%d F%d SF%d SR%d %s\n", Brain_StateAbbrev[State], DriveMotors_LeftMotorSpeed, DriveMotors_RightMotorSpeed, WallSensors_FrontReading, WallSensors_SideFrontReading, WallSensors_SideRearReading, msg); } /*-------------------------------------------- Brain Initialize and Finalize */ void Brain_Finalize() { UI_DisplayLine("Finalizing..."); /* Turn off all motor ports immediately for safety. */ alloff(); WallSensors_Finalize(); } void Brain_Initialize() { UI_DisplayLine("Initializing..."); /* Ensure all motor ports turned off. */ alloff(); DriveMotors_Initialize(); Map_Initialize(); WallSensors_Initialize(); Status_Update(); } /*------------------------------------------------------------ Brain Welcome */ #define WelcomeDisplay(line1,line2) { \ printf("%s%s\n", line1, line2); \ if (choose_button()) { \ while (choose_button()) /*NOP*/; \ return; \ } \ } #define WelcomeDisplayDelay(line1,line2,delay) { \ printf("%s%s\n", line1, line2); \ if (UI_SleepOrChooseButton(delay)) return; \ } void Brain_Welcome() { while (True) { /*.................("1234567890123456", */ WelcomeDisplayDelay(" Welcome ", " to ", 1.0); WelcomeDisplayDelay(" magellan ", " ", 0.1); WelcomeDisplayDelay(" MAGELLAN ", " ", 0.1); WelcomeDisplayDelay(" Brown CS148 ", " Final Project ", 2.0); WelcomeDisplayDelay("by M. Gorguner ", "and N. Van Dyke ", 2.0); WelcomeDisplayDelay(" Featuring... ", " ", 1.0); WelcomeDisplayDelay("* Mapping ", " ", 0.8); WelcomeDisplayDelay("* Wall Following", " ", 0.8); WelcomeDisplayDelay("* Custom Infra- ", " Red Sensors ", 0.8); WelcomeDisplayDelay("* Wheel Shaft ", " Encoding ", 0.8); WelcomeDisplayDelay("* Differential ", " Drive ", 0.8); WelcomeDisplayDelay("* Kick-Butt ", " Software ", 0.8); WelcomeDisplayDelay("* Artistic ", " Chassis ", 0.8); WelcomeDisplayDelay("* Fun for the ", " Whole Family ", 0.8); WelcomeDisplayDelay("", "", 0.8); WelcomeDisplayDelay(" A A A ", "", 0.1); WelcomeDisplayDelay("", "", 0.8); } } /*------------------------------------------------------ Brain State Machine */ /* (My kingdom for a defmacro!) */ #define ACTIVITY /*NOP*/ #define BODY_BEGIN while (1) { \ if (escape_button()) Escape_Handle(); \ WallSensors_GetReadings(); #define BODY_END } #define ENTRY /*NOP*/ #define ESCAPE_CHECK #define EXIT /*NOP*/ #define LAMBDA_TRANSITION(s) State = (Brain_State_ ## s) #define STATE(s) else if (State == ( Brain_State_ ## s)) #define STATES_BEGIN if (False) /*NOP*/; #define STATES_END else { Status_InternalError("BADSTATE"); } #define TRANSITION(s, cond) {if (cond) {State = (Brain_State_ ## s); break;}} /* State IDs */ #define Brain_State_Forward 0 #define Brain_State_Left 1 #define Brain_State_Right 2 #define Brain_State_Right2 3 #define Brain_State_Right3 4 #define Brain_State_Right4 5 #define Brain_State_Start 6 char Brain_StateAbbrev[7][4] = { "Fwd", "Lft", "Rt1", "Rt2", "Rt3", "Rt4", "Stt" }; int State; int Brain_WallFollowAdjustment() { return ((abs(IdealWallFollowingDistance - WallSensors_SideFrontReading) + abs(IdealWallFollowingDistance - WallSensors_SideRearReading)) * WallFollowAdjustmentFactor); } void Brain_RunStateMachine() { int state; UI_DisplayLine("Running..."); /* * Note: Some of these states are presently unnecessary, but we want to keep * the distinction until everything is working, for ease of extending * behavior. */ State = Brain_State_Forward; while (True) { Status_Update(); Sound_StateChangeBeep(); STATES_BEGIN STATE(Forward) { ENTRY { WheelEncoding_Start(); DriveMotors_SetFullForward(); } BODY_BEGIN { TRANSITION(Right, (WallSensors_SideFrontReading == WallSensors_NoWallReading)); TRANSITION(Left, ((WallSensors_FrontReading != WallSensors_NoWallReading) && (WallSensors_FrontReading <= FrontWallSensorThresholdForLeftTurn))); ACTIVITY { if (WallSensors_SideRearReading == WallSensors_NoWallReading) { DriveMotors_SetSpeeds(50, 100); } else { int frontRel = WallSensors_ReadingRelationToIdeal(WallSensors_SideFrontReading); int rearRel = WallSensors_ReadingRelationToIdeal(WallSensors_SideRearReading); if ((frontRel == 1) && (rearRel == 1) && (WallSensors_SideRearReading > WallSensors_SideFrontReading)) { DriveMotors_SetSpeeds(100 - (10 * (WallSensors_SideRearReading - WallSensors_SideFrontReading)), 100); } else if ((frontRel == -1) || ((frontRel == 0) && (rearRel == 1))) { /* Left */ DriveMotors_SetSpeeds((100 - Brain_WallFollowAdjustment()), 100); } else if ((frontRel == 1) || ((frontRel == 0) && (rearRel == -1))) { /* Right */ DriveMotors_SetSpeeds(100, (100 - Brain_WallFollowAdjustment())); } else { /* Straight */ DriveMotors_SetFullForward(); } } Status_Update(); } } BODY_END; EXIT { Status_Update(); WheelEncoding_Stop(); Map_AddLengthFromWheelEncoding(); } } STATE(Left) { ENTRY { DriveMotors_TurnOff(); WallSensors_GetReadings(); Map_AddLength(WallSensors_FrontReading + avg(WallSensors_SideFrontReading, WallSensors_SideRearReading) + RobotLengthCm); DriveMotors_TurnLeft90Degrees(); Map_AddTurnCode(Map_TurnCode_Left); } LAMBDA_TRANSITION(Forward); } STATE(Right) { ENTRY { WheelEncoding_Start(); DriveMotors_SetFullForward(); } BODY_BEGIN { TRANSITION(Right2, (WallSensors_SideRearReading == WallSensors_NoWallReading)); ACTIVITY { Status_Update(); } } BODY_END; EXIT { Status_Update(); WheelEncoding_Stop(); Map_AddLengthFromWheelEncoding(); } } STATE(Right2) { ENTRY { WheelEncoding_Start(); DriveMotors_MoveForwardNCm((IdealWallFollowingDistance - 2) + CmProtrudingPastSideRearSensor); WheelEncoding_Stop(); DriveMotors_TurnRight90Degrees(); Map_AddTurnCode(Map_TurnCode_Right); } LAMBDA_TRANSITION(Right3); } STATE(Right3) { ENTRY { DriveMotors_SetFullForward(); } BODY_BEGIN { TRANSITION(Right4, (WallSensors_SideFrontReading != WallSensors_NoWallReading)); ACTIVITY { Status_Update(); } } BODY_END; EXIT { Status_Update(); } } STATE(Right4) { ENTRY { DriveMotors_SetFullForward(); } BODY_BEGIN { TRANSITION(Forward, (WallSensors_SideRearReading != WallSensors_NoWallReading)); ACTIVITY { Status_Update(); if ((WallSensors_SideFrontReading == WallSensors_NoWallReading) || ((IdealWallFollowingDistance - WallSensors_SideFrontReading) < -2)) { DriveMotors_SetSpeeds(100, 50); } else if ((IdealWallFollowingDistance - WallSensors_SideFrontReading) > 2) { DriveMotors_SetSpeeds(50, 100); } else { DriveMotors_SetFullForward(); } } } BODY_END; EXIT { Status_Update(); } } STATE(Start) { /* Note: This state is not currently used. */ LAMBDA_TRANSITION(Forward); } STATES_END; } } int Brain_Calibrate() { /* Note: Adapted from "select_float_value" in "menu.c". */ float min_val = 0.5; float max_val = 1.8; float val; float coarseness = 255.0 / (max_val - min_val); float selection; while(!choose_button()) { WallSensors_Calibration = (((float)frob_knob()) / coarseness) + min_val; WallSensors_FrontFirst = True; WallSensors_SideFrontFirst = True; WallSensors_SideRearFirst = True; WallSensors_GetReadings(); printf("Cal F%d SF%d SR%d (%f)\n", WallSensors_FrontReading, WallSensors_SideFrontReading, WallSensors_SideRearReading, WallSensors_Calibration); sleep(0.1); } while (choose_button()) /*NOP*/; } /*--------------------------------------------------------------------- main */ void main() { Brain_Initialize(); Brain_Welcome(); Brain_Calibrate(); Brain_RunStateMachine(); Brain_Finalize(); UI_DisplayLine("End of Run"); } /*EOF*/