@@ -38,6 +38,9 @@ def __init__(self, filename):
3838 self .InfraredPort = eval (conf .get ('Sensors' , 'InfraredPort' ))
3939 self .TouchPort = eval (conf .get ('Sensors' , 'TouchPort' ))
4040 self .UltrasonicPort = eval (conf .get ('Sensors' , 'UltrasonicPort' ))
41+ self .kpLine = float (conf .get ('Sensors' , 'kpLine' ))
42+ self .kiLine = float (conf .get ('Sensors' , 'kiLine' ))
43+ self .kdLine = float (conf .get ('Sensors' , 'kdLine' ))
4144
4245 self .AuxMotor1 = eval (conf .get ('AuxMotors' , 'AuxMotor1' ))
4346 self .AuxMotor2 = eval (conf .get ('AuxMotors' , 'AuxMotor2' ))
@@ -80,11 +83,11 @@ def reflectCal(self):
8083 global reflHighVal
8184 global reflLowVal
8285 global reflRate
83- self .spkr .speak ( "Show me white" )
86+ self .spkr .beep ( '-f 880' )
8487 self .btn .wait_for_bump ('enter' )
8588 reflHighVal = (self .cs .reflected_light_intensity )
8689 self .spkr .beep ()
87- self .spkr .speak ( "Show me black" )
90+ self .spkr .beep ( '-f 220' )
8891 self .btn .wait_for_bump ('enter' )
8992 reflLowVal = self .cs .reflected_light_intensity
9093 self .spkr .beep ()
@@ -423,4 +426,82 @@ def LineStop(self, Heading, Speed, Stop):
423426 # Set the brick light back to green flashing
424427 #EVS.SetLEDColor("GREEN", "PULSE")
425428
426- def LineFollow (self , Speed , Stop ):
429+ def LineFollow (self , Distance , Speed , Stop ):
430+ """
431+ Follows the edge of a line for a specific distance at a specific speed.
432+
433+ ``Distance``: The distance to drive, in centimeters (positive only).
434+ ``Speed``: The speed at which to drive, in motor percentage (same speed units as EV3-G). (Positive only; there's no going back)
435+ ``Stop``: Stop motors after completion. If ``FALSE``, motors will continue running after ``Distance`` has been traveled. Otherwise, motors will stop after ``Distance`` cm.
436+ """
437+ # Ensure values are within reasonable limits, and change them if necessary (Idiotproofing).
438+ if Speed > 50 :
439+ Speed = 50
440+ print ("Speed must be between -50 and 50 (inclusive)." )
441+ elif Speed < - 50 :
442+ Speed = - 50
443+ print ("Speed must be between -50 and 50 (inclusive)." )
444+
445+ # "Reset" motor encoders by subtracting start values
446+ left_motor_start = self .lm .degrees
447+ right_motor_start = self .rm .degrees
448+ left_motor_now = self .lm .degrees
449+ right_motor_now = self .rm .degrees
450+ left_motor_change = left_motor_now - left_motor_start
451+ right_motor_change = right_motor_now - right_motor_start
452+
453+ # Determine the sign of the speed, for PID correction
454+ sign = Speed / abs (Speed )
455+
456+ # Find number of degrees that motors need to rotate to reach the desired number of cm.
457+ target = (Distance * 360 ) / self .WheelCircumference
458+
459+ # Find the average of the left and right encoders, as they could be different from PID correction
460+ avg = abs ((left_motor_change + right_motor_change ) / 2 )
461+
462+ # Initialize variables for PID control
463+ integral = 0.0
464+ last_error = 0.0
465+ derivative = 0.0
466+
467+ # Check if the motors have gone far enough
468+ while avg < target :
469+ # Read the gyro
470+ current_RLI = self .correctedRLI
471+
472+ # Calculate the PID components
473+ error = 50 - current_RLI
474+ integral = integral + error
475+ derivative = error - last_error
476+ last_error = error
477+
478+ # Calculate Steering value based on PID components and kp, ki, and kd
479+ turn_native_units = sign * max ([min ([(self .kpLine * error ) + (self .kiLine * integral ) + (self .kdLine * derivative ), 100 ]), - 100 ])
480+
481+ # Check if the motors will stop at the end. If not, the speed will be adjusted to come to a smooth stop.
482+ if Stop == False :
483+ speedMult = 1
484+ else :
485+ # Check if the robot has gone 70% or more of the distance. If so, start slowing down
486+ if (target - avg ) <= (0.3 * target ):
487+ # Calculate the pecrentage of the distance left to travel
488+ targDist = 1 - (avg / target )
489+ # Calculate speedMult based on pecentage; linear function was designed to bring the robot to a
490+ # smooth stop while still reaching the target.
491+ speedMult = ((8 / 3 ) * targDist ) + 0.2
492+ else :
493+ speedMult = 1
494+
495+ # Start the motors, using the steering value calculated by the PID control, and the input speed multiplied by speedMult (0 - 1, see above).
496+ self .steer .on (- turn_native_units , (Speed * speedMult ))
497+
498+ # Update corrected encoder values
499+ left_motor_now = self .lm .degrees
500+ right_motor_now = self .rm .degrees
501+ left_motor_change = left_motor_now - left_motor_start
502+ right_motor_change = right_motor_now - right_motor_start
503+ avg = abs ((left_motor_change + right_motor_change ) / 2 )
504+
505+ # If the robot is to stop, stop the motors. Otherwise, leave the motors on and return.
506+ if not Stop == False :
507+ self .steer .stop ()
0 commit comments