From 0e6f81ac95ef25cbd3abc55d8a598c258909b103 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 21 Aug 2023 15:05:43 -0400 Subject: [PATCH 01/10] Intro, Stop at Line, On/Off improvements --- course/line_following/media/sumo.png | Bin 48870 -> 0 bytes course/line_following/on_off_control.rst | 90 ++++++++++-------- .../line_following/staying_in_the_circle.rst | 24 ----- course/line_following/stopping_at_a_line.rst | 53 +++++++++-- course/line_following/sumo.rst | 22 ----- .../understanding_the_sensor.rst | 5 +- index.rst | 2 - 7 files changed, 97 insertions(+), 99 deletions(-) delete mode 100644 course/line_following/media/sumo.png delete mode 100644 course/line_following/staying_in_the_circle.rst delete mode 100644 course/line_following/sumo.rst diff --git a/course/line_following/media/sumo.png b/course/line_following/media/sumo.png deleted file mode 100644 index 9edab5202f56fc30e084ac2f3bcdf664ff49d468..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48870 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 diff --git a/course/line_following/on_off_control.rst b/course/line_following/on_off_control.rst index c512370..2c6256e 100644 --- a/course/line_following/on_off_control.rst +++ b/course/line_following/on_off_control.rst @@ -5,21 +5,20 @@ Now, let's turn our attention towards one of the core challenges in the final project - following a line. In the project, the robot will need to drive to multiple different locations, but doing this just based on distance can result in the robot not getting to exactly the right place. What if the wheels slip -while driving? What if the robot needs to drive along a complex curve? It's +while driving? What if the robot needs to drive along a more complicated path? It's easier to follow a line than it is to exactly measure out the course the robot needs to follow and program it. How do we follow a line? ------------------------ -Consider using one of the reflectance sensors. As a refresher, gives a reading -from 0 (black) to 1 (white). Assuming that the reflectance sensor is -approximately at the center of the robot, it will at least partially reading the -black line when the robot is centered on the line. What type of logic would we -need if we wanted to follow the center of the line? +Consider using just one of the reflectance sensors. As a refresher, it gives a reading +from 0 (black) to 1 (white). Assuming that the reflectance sensor is at the center of the robot, +it will sense part of the black line when the robot is centered on the line. +What logic would we need if we wanted to follow the center of the line? -Well, if the reflectance sensor reads black, it means the robot is perfectly on -the line, and we'd want to go straight, setting the motors at the same speed. +If the reflectance sensor reads black, it means the robot is perfectly on +the line, and we'd want to go straight, setting both motors at the same speed. But if the reflectance sensor reads grey or white, it would mean that the robot is partially or completely off the line. We'd want to correct this by steering it back to the center, but does it turn left or right? @@ -34,9 +33,9 @@ which the robot reacts. * If the sensor reads closer to black, that means we're too far to the right, so we need to turn slightly to the left. -And that's it! We want to keep polling (getting the value of) the reflectance +And that's it! We want to keep reading values from the reflectance sensor quickly, and at each time determine whether it's closer to white (with a -value less than 0.5) or closer to black (with a value greater than 0.5), and +value less than the threshold) or closer to black (with a value greater than 0.5), and depending on the result, either set the motor to turn right (set left motor speed to be faster than right) or turn left (set right motor speed to be faster than left). @@ -44,37 +43,8 @@ than left). This seems like a solution involving an if-else statement. Our condition would be related to whether the value is greater or less than 0.5. -An :code:`if` / :code:`else` statement allows you to run different blocks of -code based on a *condition* (the same kind of *condition* you used in a -:code:`while` loop) - -Consider the following example code: - -.. tab-set:: - - .. tab-item:: Python - - .. code-block:: python - - from XRPLib.defaults import * - - while True: - if drivetrain.get_left_encoder_position() > 20: - print("Left encoder is greater than 20 cm") - else: - print("Left encoder is less than 20 cm") - - .. tab-item:: Blockly - - .. image:: media/if-else.png - :width: 300 - - -In this example code we just show different messages on the computer based -on the value of the left encoder, but you could put whatever code you want -in the blocks instead. For example, you could have the robot turn clockwise -or counterclockwise depending on a condition using an :code:`if` / -:code:`else` statement. +As you may remember, an :code:`if` / :code:`else` statement allows you to run different blocks of +code based on a *condition* (the same kind of *condition* you used in a :code:`while` loop) .. figure:: media/on_off_control.png :align: center @@ -97,4 +67,40 @@ of the sensor. high and your robot will drive off the line before it gets a chance to correct for it, too low and your robot will not correct in time and will spin in circles. Try to get your robot to follow the line as fast as you - can! \ No newline at end of file + can! + +.. tab-set:: + + .. tab-item:: Hide + + Click the other tab to see a hint. + + .. tab-item:: Hint + + Consider the following example code: + + .. tab-set:: + + .. tab-item:: Python + + .. code-block:: python + + from XRPLib.defaults import * + + while True: + if drivetrain.get_left_encoder_position() > 20: + print("Left encoder is greater than 20 cm") + else: + print("Left encoder is less than 20 cm") + + .. tab-item:: Blockly + + .. image:: media/if-else.png + :width: 300 + + + In this example code we just show different messages on the computer based + on the value of the left encoder, but you could put whatever code you want + in the blocks instead. For example, you could have the robot turn clockwise + or counterclockwise depending on a condition using an :code:`if` / + :code:`else` statement. \ No newline at end of file diff --git a/course/line_following/staying_in_the_circle.rst b/course/line_following/staying_in_the_circle.rst deleted file mode 100644 index e9b65e9..0000000 --- a/course/line_following/staying_in_the_circle.rst +++ /dev/null @@ -1,24 +0,0 @@ -Staying in the Circle -===================== - -Your robot is now capable of stopping when it sees a line. You can use this -functionality to keep your robot trapped inside a circle! You'll find out why -you'd want to do this in the next module, which is a challenge activity! - -Let's break this problem down into a series of steps: - -#. Drive forward until a line is seen (the edge of the circle) -#. Stop driving so that the robot doesn't leave the circle -#. Turn around -#. Repeat - -You already have code which does steps 1 and 2 (``drive_until_line()``), and you -learned back in the robot driving module how to do step 3 -(``drivetrain.turn()``, see :doc:`/course/driving/calling_drive_functions` for a -refresher) - -.. admonition:: Try it out - - Write an infinite loop which keeps the robot in a circle. Try out different - angles when turning around. You may want to try not turning a full 180 - degrees. \ No newline at end of file diff --git a/course/line_following/stopping_at_a_line.rst b/course/line_following/stopping_at_a_line.rst index 92abde3..36c7816 100644 --- a/course/line_following/stopping_at_a_line.rst +++ b/course/line_following/stopping_at_a_line.rst @@ -22,12 +22,12 @@ certain distance: from XRPLib.defaults import * while drivetrain.get_left_encoder_position() < 20: - drivetrain.set_speed(5, 5) + drivetrain.set_speed(10, 10) drivetrain.stop() In this code, the condition being checked is ``drivetrain.get_left_encoder_position() < 20`` meaning that the robot will - drive forward at 5 cm/s until the left encoder reads a distance of 20 cm. This + drive forward at 10 cm/s until the left encoder reads a distance of 20 cm. This code can be easily modified to replace the current condition with a condition that uses the function you wrote. @@ -40,7 +40,7 @@ certain distance: :height: 3ex In this code, the condition being checked is |ico1| meaning that the robot will - drive forward at 5 cm/s until the left encoder reads a distance of 20 cm. This + drive forward at 10 cm/s until the left encoder reads a distance of 20 cm. This code can be easily modified to replace the current condition with a condition that uses the function you wrote. @@ -59,17 +59,56 @@ Once you've tested your code and proved it to meet the challenge, make a new function called ``drive_until_line()`` and put your code in it. Don't delete this function, as you'll need it later! -Challenge activity ------------------- +Challenge activity - Counting Lines +----------------------------------- For an added challenge, try to write code which makes the robot capable of driving over and stopping at several lines. The robot should drive over a line, stop for some amount of time, say two seconds, and then start driving again -until it sees another line. This cycle should repeat forever. +until it sees another line. Then, modify your code to stop after having seen 5 lines. .. tip:: You'll need to write some logic which handles the robot driving *off* of the line too! Your code from the main activity might not be enough to handle this! Think about what your code would do if it started out *already on* a - line. \ No newline at end of file + line. + +Challenge Activity - Staying in the Circle +------------------------------------------ + +Your robot is now capable of stopping when it sees a line. You can use this +functionality to keep your robot trapped inside a circle! This is meant to be a +challenge activity, so you'll need to figure out how to do it on your own. +Start by breaking down the problem into smaller steps + +.. tab-set:: + + .. tab-item:: Hide + + Press the other tab to see the solution. + + .. tab-item:: Solution + + #. Drive forward until a line is seen (the edge of the circle) + #. Stop driving so that the robot doesn't leave the circle + #. Turn around + #. Repeat + +You already have code which does steps 1 and 2 (``drive_until_line()``), and you +learned back in the robot driving module how to do step 3 +(``drivetrain.turn()``, see :doc:`Calling Drive Functions ` for a +refresher) + +.. admonition:: Tip + + Try out different angles when turning around. + You may want to try not turning a full 180 degrees. + +.. admonition:: Extension + + If you want to give yourself an extra challenge, turn this into a sumo competition! + Put two robots in the center of the same circle facing opposide directions, + and modify your code to try to push the other robot out of the circle while staying in yourself. + You can use the distance sensor to detect the other robot. + There's many way to optimize a sumo robot program, so try to be creative! \ No newline at end of file diff --git a/course/line_following/sumo.rst b/course/line_following/sumo.rst deleted file mode 100644 index 7ff7c71..0000000 --- a/course/line_following/sumo.rst +++ /dev/null @@ -1,22 +0,0 @@ -Challenge: Sumo-Bots! -===================== - -.. image:: media/sumo.png - -It's time for SUMO bots! Two XRP bots battle it out in the ring in a completely -autonomous match to push the other robot outside of the ring. - -Robots start facing away from each other in the orientation above, and have one -minute to knock the other robot outside. They may utilize distance sensors to -detect the presence and location of the other robot, and use the reflectance -sensors to keep themselves inside the ring. - -.. hint:: - - A basic SUMO-bots program may consist of a robot continuously point turning - until an enemy robot is found with the distance sensor, and then charging at - the robot until the black line is detected, so that the robot stays inside - the ring. However, worthy extensions include: aligning the robot to be - perpendicular from the black line so that the robot is not misaligned, and - devising an algorithm to attack the opponent robot from the side to avoid a - head-on collision and gain more leverage. \ No newline at end of file diff --git a/course/line_following/understanding_the_sensor.rst b/course/line_following/understanding_the_sensor.rst index 473b789..84f8d7b 100644 --- a/course/line_following/understanding_the_sensor.rst +++ b/course/line_following/understanding_the_sensor.rst @@ -101,7 +101,8 @@ is not seeing a line. (value above the threshold) or ``False`` if it does not. Don't delete this function when you're done, because you'll use it for the rest of the module! - Use the webserver to log the result of calling your function in an infinite - loop. Move your robot around a surface with lines on it to make sure it + Use :code:`print()` to view the result of calling your function in an infinite + loop (Or use the webserver to log values). + Move your robot around a surface with lines on it to make sure it always returns the correct value based on what the sensor is seeing. If you are getting incorrect values, adjust your threshold value. \ No newline at end of file diff --git a/index.rst b/index.rst index 2f2e658..da1411c 100644 --- a/index.rst +++ b/index.rst @@ -92,8 +92,6 @@ ways that make it available to all. course/line_following/index course/line_following/understanding_the_sensor course/line_following/stopping_at_a_line - course/line_following/staying_in_the_circle - course/line_following/sumo course/line_following/on_off_control course/line_following/proportional_control course/line_following/proportional_control_with_two_sensors From a3068779b8310aefc760106c45d8e71fd8125d99 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 21 Aug 2023 17:45:55 -0400 Subject: [PATCH 02/10] One Sensor Proportional Control --- course/line_following/media/print_error.png | Bin 38325 -> 0 bytes course/line_following/on_off_control.rst | 9 +- .../line_following/proportional_control.rst | 164 ++++++++---------- .../proportional_control_with_two_sensors.rst | 7 +- course/line_following/stopping_at_a_line.rst | 4 +- 5 files changed, 79 insertions(+), 105 deletions(-) delete mode 100644 course/line_following/media/print_error.png diff --git a/course/line_following/media/print_error.png b/course/line_following/media/print_error.png deleted file mode 100644 index 6581be0a0b94cee1ce28fa61c9edb1945744a0bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38325 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]?u*COuY7)Hszl$?WK5=;;g!A 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 diff --git a/course/line_following/on_off_control.rst b/course/line_following/on_off_control.rst index 2c6256e..8c55d56 100644 --- a/course/line_following/on_off_control.rst +++ b/course/line_following/on_off_control.rst @@ -86,12 +86,14 @@ of the sensor. .. code-block:: python from XRPLib.defaults import * + import time while True: if drivetrain.get_left_encoder_position() > 20: print("Left encoder is greater than 20 cm") else: print("Left encoder is less than 20 cm") + time.sleep(0.1) .. tab-item:: Blockly @@ -99,8 +101,5 @@ of the sensor. :width: 300 - In this example code we just show different messages on the computer based - on the value of the left encoder, but you could put whatever code you want - in the blocks instead. For example, you could have the robot turn clockwise - or counterclockwise depending on a condition using an :code:`if` / - :code:`else` statement. \ No newline at end of file + In this example code we print different things based on the left motor's encoder position. + You can use a similar structure to set different speeds based on the reading from the reflectance sensor. \ No newline at end of file diff --git a/course/line_following/proportional_control.rst b/course/line_following/proportional_control.rst index a85262c..c4062b9 100644 --- a/course/line_following/proportional_control.rst +++ b/course/line_following/proportional_control.rst @@ -9,17 +9,14 @@ following problem too! The perks of proportional control --------------------------------- -Let's circle back to the previous exercise - following the line by either -turning left or right depending on whether the robot is situated to the left or -the right of the line. - -What is immediately striking about following the line in this way? Well, the -robot must constantly oscillate in order to stay on the edge of the line, -because even the smallest deviation from the edge of the line results in the -robot wildly turning to compensate. In addition, it does not react any more -forcefully to bigger deviations like when the line starts curving, and as soon -as it loses sight of the line, it has no way of recovering. - +Let's think back to the previous exercise - following the line by either +turning slightly left or right depending on whether the robot is situated to the left or +the right of the line. +The issue with that approach is that the robot oscillates (bounces back and forth) around the line, +and if the robot strays too far from the line, it loses track of where the line is. +Since there were only two cases, where the robot turns at a set speed left or right, +being just slightly from the line result in the robot turning too much, +and the robot may not be able to recover. Instead of only having two cases, it seems like we'd want a whole bunch of cases, for anywhere from a sharp left turn to going perfectly straight to a sharp right turn, and everything in between, based on whether the reflectance @@ -40,16 +37,15 @@ concept here? Calculating error ----------------- -With proportional control, we have an error value we desire for it to tend to -zero, and some motor output is controlled proportional to the error to minimize -that error - in the case of maintaining a certain distance to the wall, the -error was the difference between the target and actual distance, and the output -was the speed of both drive motors. In the case of line following, the error is -the difference from 0.5 - since ideally, the robot follows the grey edge of the -line and goes straight - and the motor output is how the robot should turn. +When we were using proportional control to control the robot's distance to the wall, +we calculated the error as the difference between the desired distance and the +actual distance. We can do the same here: the error is the difference between +some threshold value (representing being on the edge of the line) and the actual reflectance value. +In the initial reflectance sensor exercise, you found an approximate threshold value +for the reflectance sensor to determine whether the robot was on or off the line. +We're going to start by using that threshold value to calculate the error. - -So, we can obtain error value with the following code: +So, the error is the following: .. tab-set:: @@ -57,38 +53,21 @@ So, we can obtain error value with the following code: .. code-block:: python - error = reflectance.get_left() - 0.5 + # Input your threshold value here: + threshold = 0.5 + error = reflectance.get_right() - threshold .. tab-item:: Blockly .. image:: media/set_error.png :width: 300 -Above, we subtract 0.5 to *normalize* the reflectance value: the error is +Above, we subtract the threshold to center the reflectance value, so that the error is negative when the robot is too far left and needs to turn right, and positive -when the robot is too far right and needs to turn left. Let's put that code into -the test. We can put it in the loop, print out the error at each iteration, and -move the robot around the line to see how the error changes. The code is as -follows: +when the robot is too far right and needs to turn left. -.. tab-set:: - - .. tab-item:: Python - - .. code-block:: python - - from XRPLib.defaults import * - from time import sleep - - while True: - error = reflectance.get_left() - 0.5 - print("Error: ", error) - sleep(0.1) # This sleep makes the loop run 10 times every second - - .. tab-item:: Blockly - - .. image:: media/print_error.png - :width: 300 +If you want to test to make sure the error is being calculated correctly, you +can print the error to the console in a loop. Implementing proportional control --------------------------------- @@ -103,74 +82,69 @@ Based on the computed error, we want that to determine how much the robot turns. This image illustrates how the error impacts how much we want to turn. Remember: making the robot turn is simply setting the left and right motors to different -efforts. So, the solution is to set a base effort, say, 50% effort, that both -motors move at when the error is at 0. Then, have the calculated error influence -the difference in efforts between the two motors. As explained through code: +efforts. Similarly to how we did wall following, we'll need to apply a base speed +and use the error to adjust it as needed. .. tab-set:: - .. tab-item:: Python + .. tab-item:: Hide - .. code-block:: python + Click the next tab to view the hint. - drivetrain.set_effort(base_effort - KP * error, base_effort + KP * error) + .. tab-item:: Hint - .. tab-item:: Blockly - - .. image:: media/set_effort_error.png - :width: 300 + .. tab-set:: -This would be run inside the loop. The base_effort represents the average effort -of the motors, no matter how much the robot turns. KP scales how much the robot -should turn based on the error - a higher KP means the robot will react more -violently to small deviations in error. + .. tab-item:: Python -Let's do a quick check to make sure the code makes sense. We assume base_effort -= 0.5 and KP = 1. If the reflectance reads whitish-grey and yields a value of -around 0.25, the error would be -0.25, meaning that the left motor's effort is: + .. code-block:: python -.. math:: + drivetrain.set_effort(base_effort - KP * error, base_effort + KP * error) - 0.5 - 1 \cdot -0.25 \\ - \begin{align} - & = 0.5 + 0.25 \\ - & = 0.75 - \end{align} + .. tab-item:: Blockly + + .. image:: media/set_effort_error.png + :width: 300 -and the right motor's speed is: + This would be run inside the loop. The base_effort represents the average effort + of the motors, no matter how much the robot turns. KP scales how much the robot + should turn based on the error - a higher KP means the robot will react more + to smaller deviations in error. -.. math:: + Let's do a quick check to make sure the code makes sense. We assume base_effort + = 0.5 and KP = 1. If the reflectance reads whitish-grey and yields a value of + around 0.25, the error would be -0.25, meaning that the left motor's effort is: - 0.5 + 1 \cdot -0.25 \\ - \begin{align} - & = 0.5 - 0.25 \\ - & = 0.25 - \end{align} + .. math:: -Motor efforts of 0.75 and 0.25 would indicate a turn to the right, and the code -does as desired. + 0.5 - 1 \cdot -0.25 \\ + \begin{align} + & = 0.5 + 0.25 \\ + & = 0.75 + \end{align} -This is a video illustrating line following with one-sensor control. Notice the -smoother tracking compared to on/off control, yet the robot is still unable to -recover from the last bend, because even a small amount of strafing from the -line results in the robot completely losing where it is. Also, the KP value was -not equal to 1 here; it's up to you to figure out the best KP value for your -bot. + and the right motor's speed is: + .. math:: -.. figure:: media/proportional_line_following.gif - :align: center + 0.5 + 1 \cdot -0.25 \\ + \begin{align} + & = 0.5 - 0.25 \\ + & = 0.25 + \end{align} + + Motor efforts of 0.75 and 0.25 would indicate a turn to the right, and the code + does as desired. + +This is a video illustrating line following with one-sensor control. Notice the +smoother tracking compared to on/off control, yet the robot is still unable to +recover perfectly, because even a small amount of strafing from the +line results in the robot completely losing where it is. +We'll look at how to improve this in the next section. +Also, the KP value was not equal to 1 here; it's up to you to figure out the best +KP value for your bot. - XRP following a line with proportional control. The robot would not be able - to follow a curved line this quickly using on-off control! +.. error:: -.. admonition:: Try it out - - Write code for the robot to follow the line with proportional control, as - shown in the video above. Note: this isn't much more than calculating error - as shown in the previous section then integrating the above line of code in - a loop. + Missing one-sensor line following video - Play around with the value of KP. How does a higher or lower KP affect the - amount of oscillation when following the line, and how responsive the robot - is to curved lines? What is the optimal value of KP? \ No newline at end of file diff --git a/course/line_following/proportional_control_with_two_sensors.rst b/course/line_following/proportional_control_with_two_sensors.rst index d40a2c2..b9b9115 100644 --- a/course/line_following/proportional_control_with_two_sensors.rst +++ b/course/line_following/proportional_control_with_two_sensors.rst @@ -132,10 +132,11 @@ And so, our final code is as follows: Here's what that looks like. Note that KP used in this video was not equal to 1: -.. error:: - - TODO add video +.. figure:: media/proportional_line_following.gif + :align: center + XRP following a line with proportional control. The robot would not be able + to follow a curved line this quickly using on-off control! .. admonition:: Try it out diff --git a/course/line_following/stopping_at_a_line.rst b/course/line_following/stopping_at_a_line.rst index 36c7816..f109181 100644 --- a/course/line_following/stopping_at_a_line.rst +++ b/course/line_following/stopping_at_a_line.rst @@ -86,9 +86,9 @@ Start by breaking down the problem into smaller steps .. tab-item:: Hide - Press the other tab to see the solution. + Press the other tab to see a hint. - .. tab-item:: Solution + .. tab-item:: Hint #. Drive forward until a line is seen (the edge of the circle) #. Stop driving so that the robot doesn't leave the circle From ace0e77ffbd7d93e4601bc1fcd9b0be97625c106 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 21 Aug 2023 18:07:21 -0400 Subject: [PATCH 03/10] Two sensor line follow --- course/line_following/parking_garage.rst | 6 +- .../proportional_control_with_two_sensors.rst | 68 +++++++++++-------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/course/line_following/parking_garage.rst b/course/line_following/parking_garage.rst index 5f55215..8a713a3 100644 --- a/course/line_following/parking_garage.rst +++ b/course/line_following/parking_garage.rst @@ -42,8 +42,8 @@ Finally, to turn back to straight, we can turn the XRP clockwise until the left By breaking this complicated problem down into a series of smaller steps, we can easily program our XRP to park itself! -.. error:: +.. .. error:: - TODO a graphic would be super useful throughout this section +.. TODO a graphic would be super useful throughout this section - also include code and a video of the robot parking itself +.. also include code and a video of the robot parking itself diff --git a/course/line_following/proportional_control_with_two_sensors.rst b/course/line_following/proportional_control_with_two_sensors.rst index b9b9115..a78e1e8 100644 --- a/course/line_following/proportional_control_with_two_sensors.rst +++ b/course/line_following/proportional_control_with_two_sensors.rst @@ -10,7 +10,7 @@ crosses over the center of the line, it's game over. Desired steering actions based on what the sensor sees. -The issue is - the robot has no way of knowing which side of the line it is +The issue is - the robot still has no way of knowing which side of the line it is following at all! If it sees the right edge of the line, it will assume it still is detecting the left edge, and thus keep turning right past the point of no return! @@ -49,27 +49,33 @@ Sensors read ~0 and ~1. Robot knows it is on the left side and turns right. Sensors read ~0.5 and ~0.5. Robot knows it is on the center and goes straight. -The other two major categories you can extrapolate for yourself. +The other two cases you can extrapolate for yourself. So, how can we effectively combine the readings of the left and right reflectance sensors using proportional control to have the robot follow the -line? There's quite an elegant solution that I encourage for you to try to -figure out yourselves before the answer is revealed. +line? -Implementation --------------- .. tab-set:: - .. tab-item:: Python + .. tab-item:: Hide - .. code-block:: python + There's a few ways to do this, but there's a really elegant solution that + we'll use here. Try to figure it out yourself before reading on! - error = reflectance.get_left() - reflectance.get_right() + .. tab-item:: Solution - .. tab-item:: Blockly + .. tab-set:: - .. image:: media/error.png - :width: 300 + .. tab-item:: Python + + .. code-block:: python + + error = reflectance.get_left() - reflectance.get_right() + + .. tab-item:: Blockly + + .. image:: media/error.png + :width: 300 At the beginning this line of code may not make a lot of sense - but let's dissect it. Remember our previous convention of positive error meaning the robot @@ -107,28 +113,36 @@ case, both sensors read white, leaving an error of 0, and so the robot just goes straight. Given that the robot wouldn't know which direction to compensate if it was completely off the line, this seems like a reasonable result. -And so, our final code is as follows: +Let's try it out! .. tab-set:: - .. tab-item:: Python + .. tab-item:: Hide + + Try to figure out how to use this error to line follow before reading on! + + .. tab-item:: Solution + + .. tab-set:: + + .. tab-item:: Python - .. code-block:: python + .. code-block:: python - from XRPLib.defaults import * + from XRPLib.defaults import * - # Try different values for KP and base_effort to get things working smoothly - KP = 1 - base_effort = 0.5 + # Try different values for KP and base_effort to get things working smoothly + KP = 1 + base_effort = 0.5 - while True: - error = reflectance.get_left() - reflectance.get_right() - drivetrain.set_effort(base_effort - KP * error, base_effort + KP * error) - - .. tab-item:: Blockly - - .. image:: media/set_effort_program.png - :width: 550 + while True: + error = reflectance.get_left() - reflectance.get_right() + drivetrain.set_effort(base_effort - KP * error, base_effort + KP * error) + + .. tab-item:: Blockly + + .. image:: media/set_effort_program.png + :width: 550 Here's what that looks like. Note that KP used in this video was not equal to 1: From 67c0976370b2e96c4b6750d2b8d19feb41b8f3ae Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Tue, 22 Aug 2023 19:30:15 -0400 Subject: [PATCH 04/10] Parking Challenge --- course/line_following/parking_garage.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/course/line_following/parking_garage.rst b/course/line_following/parking_garage.rst index 8a713a3..656eac4 100644 --- a/course/line_following/parking_garage.rst +++ b/course/line_following/parking_garage.rst @@ -21,24 +21,24 @@ The main process we will employ here is simple: 1. Go forward the length of a parking spot 2. Turn 90 degrees right and check to see if the parking spot is open 3. If the parking spot is not open, turn 180 degrees left and check to see if the parking spot on the left is open -4. If both parking spots are not open, turn back to straight using the line following understanding_the_sensor +4. If both parking spots are not open, turn back to straight using the line following sensors to know when we are over the line. +5. When an open parking spot is found, drive forward into the parking spot until you see a line (the end of the parking spot) In order to see if a parking spot is open, we can use our ultrasonic range finder and see if there is any object in a parking spot. Leveraging the Line Following Sensors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In this activity, we can use our line following sensors for three main purposes: +In this activity, we want to use our line following sensors for three main purposes: 1. To follow the line on the ground to the next parking spot 2. To detect the "end" of the parking spot 3. To turn back to straight after checking the left parking spot In terms of following the line, we can use the same proportional controller that we used in the previous activities. - -As for detecting the end of the parking spot, we can use the same logic that we used when detecting an intersection. - -Finally, to turn back to straight, we can turn the XRP clockwise until the left line following sensor detects the line. +In fact, we've wrote most of the code you will need for this already! +For example, we can use the same logic that we used when detecting an intersection for detecting the end of the parking spot, +and again for when we are turning back to straight after checking the parking spot. By breaking this complicated problem down into a series of smaller steps, we can easily program our XRP to park itself! From 5fbf6e6e58b89345ba1258f65392c1e57565d735 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Tue, 22 Aug 2023 19:56:05 -0400 Subject: [PATCH 05/10] Single edge line follow solution --- .../line_following/proportional_control.rst | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/course/line_following/proportional_control.rst b/course/line_following/proportional_control.rst index c4062b9..58b2e50 100644 --- a/course/line_following/proportional_control.rst +++ b/course/line_following/proportional_control.rst @@ -89,7 +89,8 @@ and use the error to adjust it as needed. .. tab-item:: Hide - Click the next tab to view the hint. + Now it's your turn. Try and implement proportional control for line following using the error. + If you need a hint or want to view the solution, take a look at the next tabs. .. tab-item:: Hint @@ -136,6 +137,49 @@ and use the error to adjust it as needed. Motor efforts of 0.75 and 0.25 would indicate a turn to the right, and the code does as desired. + .. tab-item:: Solution + + .. tab-set:: + + .. tab-item:: Python + + .. code-block:: python + + # Input your threshold value here: + threshold = 0.5 + base_effort = 0.5 + KP = 1 + while True: + error = reflectance.get_right() - threshold + drivetrain.set_effort(base_effort + KP * error, base_effort - KP * error) + time.sleep(0.01) + + .. tab-item:: Blockly + + .. image:: media/one_edge_line_follow.png + :width: 1000 + + Just to review, let's take this code line-by-line and make sure we understand what's going on. + + :code:`threshold = 0.5` sets the threshold value to 0.5. + This is the value that the reflectance sensor will use to determine whether the robot is on or off the line. + + :code:`base_effort = 0.5` sets the base effort to 0.5. + This is the average effort of the motors, which controls how fast we want the robot to follow the line. + A higher base effort means the robot will follow the line faster, but also means the robot will be less able to recover from errors. + + :code:`KP = 1` sets the KP value to 1. + This is the proportional constant, which controls how much the robot reacts to the error. + This is our main tuning value, and you'll have to try different values to see what works best for your robot. + + :code:`error = reflectance.get_right() - threshold` calculates the error. + As we discussed earlier, the error basically is our measurement of how far the robot is from the line. + + :code:`drivetrain.set_effort(base_effort + KP * error, base_effort - KP * error)` sets the motor efforts. + This is where we actually use the error to determine how much the robot turns. + A positive error means we are too far left, and need the left power to be higher than the right power, + and a negative error means we are too far right, and need the right power to be higher than the left power. + This is a video illustrating line following with one-sensor control. Notice the smoother tracking compared to on/off control, yet the robot is still unable to recover perfectly, because even a small amount of strafing from the From 67f4834053b4b345eaee48fb45a774e0dab57953 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Tue, 22 Aug 2023 20:05:30 -0400 Subject: [PATCH 06/10] Elaborated on proportional control smaller error -> smaller correction --- .../proportional_control_with_two_sensors.rst | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/course/line_following/proportional_control_with_two_sensors.rst b/course/line_following/proportional_control_with_two_sensors.rst index a78e1e8..25ccdce 100644 --- a/course/line_following/proportional_control_with_two_sensors.rst +++ b/course/line_following/proportional_control_with_two_sensors.rst @@ -62,7 +62,7 @@ line? There's a few ways to do this, but there's a really elegant solution that we'll use here. Try to figure it out yourself before reading on! - .. tab-item:: Solution + .. tab-item:: Solution: .. tab-set:: @@ -108,10 +108,25 @@ sensor values are the same and so the error is 0, and as the robot starts drifting towards either direction, the magnitude of the error increases and thus the robot compensates accordingly. -The most interesting case is when the robot is completely off the line - in this +When the robot is completely off the line - in this case, both sensors read white, leaving an error of 0, and so the robot just goes straight. Given that the robot wouldn't know which direction to compensate if it -was completely off the line, this seems like a reasonable result. +was completely off the line, this seems like a reasonable result. +The same behavior occurs if the robot is completely on the line. + +There is one last case to cover. What happens if the robot is only slightly off the line? + +.. math:: + + \begin{align} + \text{error} & = 1 - 0.5 \\ + & = 0.5 + \end{align} + +Well in this case, the error will be smaller, which means that a smaller correction is needed. +This is an important property of proportional control - the further the robot is +from the desired state, the more it will try to correct itself, +but will only slightly correct itself for small errors, which minimuxes the chanse of overshooting the target. Let's try it out! From 6299fb4505bb5da6340dec0a489225a5a14c8484 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 27 Nov 2023 22:17:13 -0500 Subject: [PATCH 07/10] Added solution for understanding sensor - needs blockly --- .../understanding_the_sensor.rst | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/course/line_following/understanding_the_sensor.rst b/course/line_following/understanding_the_sensor.rst index 84f8d7b..914e3e6 100644 --- a/course/line_following/understanding_the_sensor.rst +++ b/course/line_following/understanding_the_sensor.rst @@ -105,4 +105,30 @@ is not seeing a line. loop (Or use the webserver to log values). Move your robot around a surface with lines on it to make sure it always returns the correct value based on what the sensor is seeing. If you - are getting incorrect values, adjust your threshold value. \ No newline at end of file + are getting incorrect values, adjust your threshold value. + +.. tab-set:: + +.. tab-item:: Hide + + Give it a try yourself before checking the solution! + +.. tab-item:: Solution: + + .. tab-set:: + + .. tab-item:: Python + + .. code-block:: python + + def is_over_line(): + # This is a tuning value! Depending on your exact line, lighting, and robot, + # the value of what counts as a line may differ! + line_threshold = 0.75 + return reflectance.get_right() > line_threshold + + .. tab-item:: Blockly + + TODO: Add blockly code + .. .. image:: media/error.png + .. :width: 300 \ No newline at end of file From 30b302138225bc7f4399983a66b8cae9db5fd861 Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 27 Nov 2023 22:27:19 -0500 Subject: [PATCH 08/10] Added Staying in the Circle solution - needs blockly --- course/line_following/stopping_at_a_line.rst | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/course/line_following/stopping_at_a_line.rst b/course/line_following/stopping_at_a_line.rst index f109181..696f2c1 100644 --- a/course/line_following/stopping_at_a_line.rst +++ b/course/line_following/stopping_at_a_line.rst @@ -94,6 +94,34 @@ Start by breaking down the problem into smaller steps #. Stop driving so that the robot doesn't leave the circle #. Turn around #. Repeat + + .. tab-item:: Solution + + .. tab-set:: + + .. tab-item:: Python + .. code-block:: python + + def is_over_line(): + # This a slightly modified version of the example from the previous section! + # We now are checking that the average of the two sensors is above the threshold. + line_threshold = 0.75 + return (reflectance.get_right() + reflectance.get_left())/2 > line_threshold + + while True: + # Drive forward until a line is seen + drivetrain.set_effort(0.5, 0.5) + # Check if robot is over a line + if is_over_line(): + # Stop and turn around + drivetrain.turn(180) + + .. tab-item:: Blockly + + TODO: Add blockly code + .. .. image:: media/error.png + .. :width: 300 + You already have code which does steps 1 and 2 (``drive_until_line()``), and you learned back in the robot driving module how to do step 3 From 65d2cf9b82a48bb60bdfee4eab43095fe15a688d Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 27 Nov 2023 22:33:05 -0500 Subject: [PATCH 09/10] Added on-off control example solution - needs blockly --- course/line_following/on_off_control.rst | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/course/line_following/on_off_control.rst b/course/line_following/on_off_control.rst index 8c55d56..d086c5b 100644 --- a/course/line_following/on_off_control.rst +++ b/course/line_following/on_off_control.rst @@ -102,4 +102,34 @@ of the sensor. In this example code we print different things based on the left motor's encoder position. - You can use a similar structure to set different speeds based on the reading from the reflectance sensor. \ No newline at end of file + You can use a similar structure to set different speeds based on the reading from the reflectance sensor. + + .. tab-item:: Solution + + .. tab-set:: + + .. tab-item:: Python + + .. code-block:: python + + from XRPLib.defaults import * + import time + + # This is a tuning value! Based on your line, sensors, and lighting, + # the exact value for this variable may vary. + line_threshold = 0.5 + + while True: + if reflectance.get_right() > line_threshold: + # if the right sensor is on the line, turn slightly left + drivetrain.set_effort(0.5,0.35) + else: + # if the right sensor is on the line, turn slightly left + drivetrain.set_effort(0.35,0.5) + time.sleep(0.1) + + .. tab-item:: Blockly + + TODO: Add blockly code + .. .. image:: media/if-else.png + .. :width: 300 From 7ca4ccbf35671e7caa5f62210cbf69f88588823d Mon Sep 17 00:00:00 2001 From: Kevin Siegall Date: Mon, 27 Nov 2023 22:40:16 -0500 Subject: [PATCH 10/10] Added a minor clarification about line threshold --- course/line_following/proportional_control.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/course/line_following/proportional_control.rst b/course/line_following/proportional_control.rst index 58b2e50..aefc9a2 100644 --- a/course/line_following/proportional_control.rst +++ b/course/line_following/proportional_control.rst @@ -163,6 +163,7 @@ and use the error to adjust it as needed. :code:`threshold = 0.5` sets the threshold value to 0.5. This is the value that the reflectance sensor will use to determine whether the robot is on or off the line. + This is a tuned value that may differ depending on your line, sensor, and lighting. :code:`base_effort = 0.5` sets the base effort to 0.5. This is the average effort of the motors, which controls how fast we want the robot to follow the line.