|
|
|
## Lab Notebook 4
|
|
|
|
|
|
|
|
**Date:** 3rd of March 2016
|
|
|
|
|
|
|
|
**Group number:** Group 14
|
|
|
|
|
|
|
|
**Group members participating:** Ida Larsen-Ledet and Nicolai Nibe for the first five exercises (Camilla and Emil were absent due to TERRIBLE ILLNESS, so the
|
|
|
|
two remaining members, fueled by energy drinks and a burning passion for embedded systems, powered through on their own!).
|
|
|
|
|
|
|
|
Camilla M. V. Frederiksen and Nicolai Nibe for the 6th exercise on friday the 4th.
|
|
|
|
|
|
|
|
**Activity duration:** 5 hours
|
|
|
|
|
|
|
|
## Goal
|
|
|
|
Do not kill Frej (the robot).
|
|
|
|
Hopefully succeed in making Frej, the robot, follow the black line and stop at the end, when he enters the goal zone (according to the overall goal
|
|
|
|
description in [lesson plan 4](http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson4.dir/Lesson.html).
|
|
|
|
|
|
|
|
## Plan
|
|
|
|
We expect to be able to accomplish the goal by following the instructions in the lesson plan and completing the sub-exercises. As two group members are
|
|
|
|
missing and Nicolai forgot the charger for his computer, we will be completing the exercises using a mix of pair-programming and division into note-taker
|
|
|
|
and robot operator.
|
|
|
|
|
|
|
|
As usual, we intend to take pictures and videos of the exercises to be used for explanations of our approach and results. No specific person will be in
|
|
|
|
charge of this - whoever is available and has a recording device at hand will do it. If no one does, we have no idea what we will do, as Camilla is not here
|
|
|
|
to shout at us.
|
|
|
|
|
|
|
|
## Results
|
|
|
|
### Exercise 1: Testing and calibrating black/white detection
|
|
|
|
|
|
|
|
#### Test setup
|
|
|
|
We implemented a test of **BlackWhiteSensor.java** in the class **RacistCopCalibrationTest.java**. Below is an excerpt of the code:
|
|
|
|
|
|
|
|
```java
|
|
|
|
while (! Button.ESCAPE.isDown())
|
|
|
|
{
|
|
|
|
|
|
|
|
LCD.drawInt(sensor.light(),4,10,2);
|
|
|
|
LCD.drawString("Black: " + sensor.black(), 0, 6);
|
|
|
|
LCD.drawString("White: " + sensor.white(), 0, 8);
|
|
|
|
LCD.refresh();
|
|
|
|
|
|
|
|
Thread.sleep(10);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*Program code 1: The control loop of the RacistCopCalibrationTest class*
|
|
|
|
|
|
|
|
Our test was designed to allow us to manually compare the sensor's reading with what could be observed visually. In the loop, the sensor's reading is
|
|
|
|
checked and the truth values indicating black and white respectively are displayed (as we use strict inequalities, there might actually be a case where
|
|
|
|
both of the values display false - i.e. when the sensor's reading is the same as the threshold value). However, we accidentally supplied coordinates
|
|
|
|
that where outside the LCD screen for the white value, and so it wasn't displayed. We decided not to correct this, as it appeared from the displayed black
|
|
|
|
truth value that the sensor's readings were accurate.
|
|
|
|
|
|
|
|
#### Test execution
|
|
|
|
When calibrating, the robot's value for black was 35 and its reading for white was 58.
|
|
|
|
|
|
|
|
We investigated the functioning of the sensor by moving it over a border between black and white. The shift in registered color seemed to happen exactly
|
|
|
|
when the surface below changed color - the setting of the black/white threshold to the average between the white-value and the black-value thus seems to
|
|
|
|
be a good threshold.
|
|
|
|
|
|
|
|
We then placed the robot on surfaces of different color to see if they registered as black or white. The obtained readings are shown in table 1. The colors
|
|
|
|
chosen for these readings were the accesible colors of the LEGO robot track placed in the Zuse building.
|
|
|
|
|
|
|
|
| Color | Registered color |
|
|
|
|
| ------ | ---------------- |
|
|
|
|
| Blue | Black |
|
|
|
|
| Green | Black |
|
|
|
|
| Yellow | White |
|
|
|
|
| Orange | White |
|
|
|
|
| Red | White |
|
|
|
|
|
|
|
|
*Table 1: Colors registrered by the BlackWhiteSensor program when placing it above different colors.*
|
|
|
|
|
|
|
|
These results fit well with our results from exercise 2 in [lesson 3](https://gitlab.au.dk/LEGO/lego-kode/blob/master/report_week3.md) where we had the robot
|
|
|
|
sense different colors.
|
|
|
|
|
|
|
|
### Exercise 2: Testing the Line Follower program using the BlackWhiteSensor from exercise 1
|
|
|
|
In this exercise we loaded the **LineFollowerCal.java** program onto the robot to observe it 'acting' based on the black-white sensor.
|
|
|
|
|
|
|
|
The robot was able to identifty the line and follow it, although it had some issues with corners: It handled corners by circling a lot until it hit the line
|
|
|
|
again. We speculated that increasing the update frequency might help, as the likelihood of the robot checking the color while above the line would then be
|
|
|
|
greater. Changing the frequency from once per 10 ms to once per 5 ms improved the robot's handling of corners.
|
|
|
|
|
|
|
|
The robot appeared to be 'confused' by a grid of colors on the track; however, the reason for this could also just be that the colors were separated by
|
|
|
|
black lines which were very close to each other. This could be an issue because the robot is constantly turning a little from side to side when driving,
|
|
|
|
whereby frequently occurring turns might cause it to diverge from its path, since the robot could catch on to a side path if acctidentally turning towards
|
|
|
|
one. On the other hand, we also saw the robot missing turns on the line: When it encountered a (90 degree) turn on the line, it would just cross it instead of
|
|
|
|
turning and continuing to follow the line.
|
|
|
|
|
|
|
|
[![LineFollower with Calibration](http://img.youtube.com/vi/X11P_-ep1XA/0.jpg)](https://www.youtube.com/watch?v=X11P_-ep1XA)
|
|
|
|
|
|
|
|
*Video 1: The robot folloing the black line border with the LineFollowerCal program.*
|
|
|
|
|
|
|
|
### Exercise 3: Implementing and testing calibration of three-color detection (black/white/green)
|
|
|
|
We modified our ***BlackWhiteSensor.java*** into ***ThreeColorSensorNaive.java*** (renamed from ***ThreeColorSensor.java*** in a later exercise) and wrote a
|
|
|
|
class, ***RacistAlienCopCalibrationTest.java***, to test the sensor implementation.
|
|
|
|
|
|
|
|
The ***ThreeColorSensorNaive.java*** class decides if the sensor is reading black, white or green in a similar way to ***BlackWhiteSensor.java***,
|
|
|
|
except that it has two thresholds, a **blackGreenThrehshold** and a **whiteGreenThreshold**, which are calculated as the average of our
|
|
|
|
black/green and white/green calibration values respectively. See the code excerpt below.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public void calibrate()
|
|
|
|
{
|
|
|
|
blackLightValue = read("black");
|
|
|
|
whiteLightValue = read("white");
|
|
|
|
greenLightValue = read("green");
|
|
|
|
|
|
|
|
// The thresholds are calculated as the median between
|
|
|
|
// the two readings that they are supposed to distinguish between
|
|
|
|
whiteGreenThreshold = (whiteLightValue+greenLightValue)/2;
|
|
|
|
greenBlackThreshold = (greenLightValue+blackLightValue)/2;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*Program code 2: The calibrate() method of the ThreeColorSensorNaive class*
|
|
|
|
|
|
|
|
The robot displayed true for the correct color and false for the remaining two colors, and so we concluded that it seemed to register the colors correctly.
|
|
|
|
|
|
|
|
We also tested what colors the robot registered not-included colors as. The results from this experiment can be found in table 2:
|
|
|
|
|
|
|
|
| Color | Registered color |
|
|
|
|
| ------ | ---------------- |
|
|
|
|
| Blue | - |
|
|
|
|
| Yellow | White |
|
|
|
|
| Orange | White |
|
|
|
|
| Red | White |
|
|
|
|
|
|
|
|
*Table 2: Colors registrered by the ThreeColorSensorNaive program when placing the robot's sensor above different colors.*
|
|
|
|
|
|
|
|
Blue registered as false on all three - this is possible because the code uses strict inequalities, which leaves empty "pits" for the check to fall into.
|
|
|
|
During further testing, blue sometimes registered as green, sometimes as white. If we were to change this in the code, we would need to make a decision on
|
|
|
|
where to make the cut (i.e. where to introduce equality into the comparison operator). Right now, the robot will simply end in the final else clause, if we
|
|
|
|
use an implementation like the one in the black-white-line follower without the goal registration.
|
|
|
|
|
|
|
|
Furthermore, the robot registers the border between black and white as green. This observation showed us that the robot might misinterpret being on the
|
|
|
|
border of the line as being in the goal zone. We discussed some ideas on how to amend this for the line-follower with a green goal zone:
|
|
|
|
- We could decide to only make the robot stop if it has measured green a certain number of times in a row (smoothing)
|
|
|
|
- We could use a narrower interval for green
|
|
|
|
|
|
|
|
### Exercise 4: Implementing and testing line Follower that stops in a goal zone, using the ThreeColorSensor from exercise 3
|
|
|
|
|
|
|
|
Initially we wrote a program, **ThreeColorNaive.java** which follows a black line and stops on a green area by using our **ThreeColorSensorNaive.java**
|
|
|
|
program to decide if it is seeing black, white or green. The results were expectedly atrocious, as seen in video 2: The robot seems to register borders
|
|
|
|
between black and white as green and thus stops when seen a border. When moved away from the border, it starts searching for the black line again, but
|
|
|
|
is quickly confused by a border again.
|
|
|
|
|
|
|
|
[![LineFollower with Goal Zone, 1st attempt](http://img.youtube.com/vi/MSvhh_LDYcI/0.jpg)](https://www.youtube.com/watch?v=MSvhh_LDYcI)
|
|
|
|
|
|
|
|
*Video 2: LineFollower with Goal Zone, first attempt. Here we recognize "green" lower bound as halfway between green/black calibration
|
|
|
|
value, and its upper bound halfway between green/white calibration values - see program code 2. The robot halts when encountering a border between a black
|
|
|
|
line and the white surface.*
|
|
|
|
|
|
|
|
Since the light value that we calibrated as being "green" lies inbetween the light values seen as black and white (green lies closer to black than
|
|
|
|
white), our program interprets a certain point, where it sees both some black and some white reflection, as "green". Each time it hits this point between
|
|
|
|
black and white, the robot will stop. However, as seen in the video, it doesn't immediately do a full stop, since the momentum carried by the motor doesn't
|
|
|
|
allow it to stop instantly, and by the time it would have reached a full stop, the sensor has swung enough past the "green" zone to no longer be reading a
|
|
|
|
green value, and will as such start driving again. We utilize this by calling **car.stop()**, rather than breaking the loop - this allows the robot to perform
|
|
|
|
another reading after its momentum has potentially pushed it onto a black or white surface. Because our green thresholds are very generous, it however doesn't
|
|
|
|
take very long for our robot to come a full stop.
|
|
|
|
|
|
|
|
In an attempt to improve our robot so it only stops on areas that are actually green, we created a modified version of the control program,
|
|
|
|
***ThreeColorSensorNarrow.java***, which only recognizes a light value as green if it is within +/- 2 of our calibrated green light value (as mentioned in the
|
|
|
|
discussion at the end of exercise 3; hence the extension *narrow*). If it is not green, it differentiates between black and white in the same way as the original
|
|
|
|
***BlackWhiteSensor.java***.
|
|
|
|
|
|
|
|
```java
|
|
|
|
public void calibrate()
|
|
|
|
{
|
|
|
|
blackLightValue = read("black");
|
|
|
|
whiteLightValue = read("white");
|
|
|
|
greenLightValue = read("green");
|
|
|
|
|
|
|
|
// The thresholds is calculated as the median between the black and white readings
|
|
|
|
blackWhiteThreshold = (blackLightValue + whiteLightValue)/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*...*/
|
|
|
|
|
|
|
|
public boolean green() {
|
|
|
|
return (ls.readValue() < greenLightValue + 2 && ls.readValue() > greenLightValue - 2 );
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*Program code 3: The ThreeColorSensorNarrow class' calibrate() method and green-check*
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
while (! Button.ESCAPE.isDown())
|
|
|
|
{
|
|
|
|
|
|
|
|
LCD.drawInt(sensor.light(),4,10,2);
|
|
|
|
LCD.refresh();
|
|
|
|
|
|
|
|
//We have to check for green first, as the green interval is within the black interval
|
|
|
|
if ( sensor.green() )
|
|
|
|
//This way, we can simply move the robot to a white/black surface in order to make it start again
|
|
|
|
Car.stop();
|
|
|
|
else if (sensor.black() )
|
|
|
|
Car.forward(power, 0);
|
|
|
|
else
|
|
|
|
Car.forward(0, power);
|
|
|
|
|
|
|
|
Thread.sleep(10);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*Program code 4: The control loop of the ThreeColorNarrow class*
|
|
|
|
|
|
|
|
The class ***ThreeColorNarrow.java*** uses the *narrow*-sensor, and the resulting behaviour of the robot is seen in video 3.
|
|
|
|
|
|
|
|
[![LineFollower with Goal Zone, 2nd attempt](http://img.youtube.com/vi/Bz6aXjhIfmo/0.jpg)](https://www.youtube.com/watch?v=Bz6aXjhIfmo)
|
|
|
|
|
|
|
|
*Video 3: LineFollower with Goal Zone, second attempt. Here we recognize "green" upper and lower bounds as its calibration value +/- 2.*
|
|
|
|
|
|
|
|
By vastly reducing the interval of light values that are recognized as green, we ensure that the times where the sensor is following the black line
|
|
|
|
and might read green are so few and temporally spread out that the robot will seem to carry on unphased, due to its momentum sending it forward, onto
|
|
|
|
a surface with a clearly black or white color.
|
|
|
|
Potential ways of further preventing the sensor from seeing green when it shouldn't could be to add some counter in the program, requiring it to have
|
|
|
|
read green a certain amount of times in a row in order to actually tell our robot to stop, as mentioned in the discussion in the end of exercise 3.
|
|
|
|
This would cause a small delay when entering a green goal zone, but could eliminate most false green positives when folloing our line. However, we didn't
|
|
|
|
take the time to try to implement this approach.
|
|
|
|
|
|
|
|
### Exercise 5: Implementing a PID regulator in the line follower
|
|
|
|
|
|
|
|
We used a modified version of ***BlackWhiteSensor.java*** to include a method ***getThrehshold()*** to retrieve the ***blackWhiteThrehshold*** variable
|
|
|
|
from the calibration, as the calibrated threshold between black and white is the setpoint for our PID regulator. We then proceeded to write a class
|
|
|
|
***PIDpolice.java*** (don't ask) by basing the PID controller on the pseudo code referenced in the lesson plan [11].
|
|
|
|
|
|
|
|
```java
|
|
|
|
while (! Button.ESCAPE.isDown())
|
|
|
|
{
|
|
|
|
LCD.drawInt(sensor.light(),4,10,2);
|
|
|
|
LCD.refresh();
|
|
|
|
|
|
|
|
int error = sensor.light() - setpoint;
|
|
|
|
int turn = c * error;
|
|
|
|
|
|
|
|
Car.forward(targetPower + turn, targetPower - turn);
|
|
|
|
|
|
|
|
Thread.sleep(10);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
*Program code 5: Control loop of the PIDpolice class. The variables sepoint, c and targetPower are set earlier in the code.*
|
|
|
|
|
|
|
|
For our first attempt, we used a ***targetPower*** of 80 and a scaling value ***c*** of 50 (just to see the effect of it).
|
|
|
|
This resulted in the robot spinning furiously most of the time, as seen in Video 4.
|
|
|
|
|
|
|
|
[![PID C = 50](http://img.youtube.com/vi/gbfT4Hj9My0/0.jpg)](https://www.youtube.com/watch?v=gbfT4Hj9My0)
|
|
|
|
|
|
|
|
*Video 4: PID line follower with targetPower 80, and multiplying the error by 50. Robot goes insane.*
|
|
|
|
|
|
|
|
The circkling motion resulted from one motor running forward on full speed, while the other was running backwards. This makes sense, seeing as the
|
|
|
|
scaling value for the error results in a very large value of ***turn***, which is added and subtracted respectively for the motors, resulting in a large
|
|
|
|
increase (decrease respectively) to the motor power.
|
|
|
|
|
|
|
|
When decreasing ***c*** to 1, the behavoiur of the robot improved significantly (for a ***targetPower*** of 80): It followed the line, only fluctuating slightly.
|
|
|
|
See Video 5.
|
|
|
|
|
|
|
|
[![LineFollower PID](http://img.youtube.com/vi/Qp6U7-HSuss/0.jpg)](https://www.youtube.com/watch?v=Qp6U7-HSuss)
|
|
|
|
|
|
|
|
*Video 5: LineFollower with PID. Target power: 80, no scaling of the error*
|
|
|
|
|
|
|
|
Our reason for including the scaling factor ***c*** was that it was used in the pseudo code. But in their case, the error interval is [-5, 5], which differs a lot from
|
|
|
|
the interval of possible motor power which is [0, 100]. Thereby, it makes sense to scale the error to have it impact the motor power more appropriately. In our case, however,
|
|
|
|
the interval for the possible light measurements is [0, 100]. Thereby, our potential error interval is (theoretically) [-100, 100], the same as for the motor power (including
|
|
|
|
'backwards', i.e. negative, values). This could be the reason that the robot functions so well without any scaling of the error.
|
|
|
|
|
|
|
|
|
|
|
|
### Exercise 6: Color Sensor test
|
|
|
|
Before starting on this exercise, we exchanged the NXT light sensor with the NXT color sensor.
|
|
|
|
To test whether this sensor is better at distinguishing between three different colors, than the light sensor, we used the **ColorSensorSensor.java**
|
|
|
|
program.
|
|
|
|
|
|
|
|
We started out by placing the sensor above the same colors that the light sensor was tested on. The obtained results can be found in
|
|
|
|
table 3. The table includes more than the three colors requested by the exercise description, as this gave us a better idea of what
|
|
|
|
colors the sensor could distinguish.
|
|
|
|
|
|
|
|
| Actual Color | Sensed Color |
|
|
|
|
| ------ | ---------------- |
|
|
|
|
| Blue | Blue |
|
|
|
|
| Yellow | Yellow |
|
|
|
|
| Orange | Yellow |
|
|
|
|
| Red | Red |
|
|
|
|
| Green | Green |
|
|
|
|
| Black | Green |
|
|
|
|
| White | White |
|
|
|
|
|
|
|
|
*Table 3: Colors of Zuse's LEGO robot track, and the readings from the NXT color sensor, when placed above them.*
|
|
|
|
|
|
|
|
We see from our readings that it is not possible to distinguish between black and green, as our color sensor reads a black surface as green.
|
|
|
|
This further means that it will not be possible to follow a black line and stop when reaching a green Goal Zone, since the color sensor cannot
|
|
|
|
distinguish between the black line and the green zone. It is of course still possible to follow a black line using the color sensor by basically
|
|
|
|
programming it to follow a green line, but then we'd have the green goal zone being treated as a black line.
|
|
|
|
|
|
|
|
A way to achieve a robot following a black line and stopping in a goal zone, using the color sensor, would be to use a different color for the goal zone.
|
|
|
|
If our goal zone was blue, yellow or red, we'd be able to distinguish it from the black line with a the color sensor, and there'd be no problem.
|
|
|
|
|
|
|
|
|
|
|
|
After making these rather simple observations about the uncertainty of the color sensor, we noticed that we
|
|
|
|
in no case got a 'black' reading. Therefore we wondered if such a reading were even possible. Motivated by this
|
|
|
|
observation we started playing arround with the color sensor. We found out that a black reading was indeed possible
|
|
|
|
to obtain by measuring out in the open air instead of measuring a colored surface. By this observation we assumed that
|
|
|
|
a color sensor reading was a measure of how much of the light from the color sensor was reflected by the surface.
|
|
|
|
|
|
|
|
Next we tried obtaining black readings of actual black surfaces by finding unreflective black materials. For this
|
|
|
|
purpose, Camillas jacket was used (se figure 1), and did indeed give a reading of black.
|
|
|
|
|
|
|
|
![Camillas jacket](week6/img/IMG_2227.JPG)
|
|
|
|
|
|
|
|
*Figure 1: Camillas jacket that gave a black reading when using the color sensor.*
|
|
|
|
|
|
|
|
These further experiments with the color sensor leads us to conclude that it will be possible to follow a black
|
|
|
|
line and stop in a green goal zone if the black lines are made of very matte, light absorbant materials. This means
|
|
|
|
that it is - as earlier concluded - not possible to follow the black line of the LEGO robot track as these black
|
|
|
|
lines are too reflective, but it might be possible if using another less reflective track. On the other hand, if we
|
|
|
|
simply need to follow a black line on a white surface, without ending in a green goal zone, then this sensor might
|
|
|
|
still function well, as it is able to distinguish white from other colors. And perhaps a goal zone of a different color
|
|
|
|
than green would also enable ending in a goal zone.
|
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
Despite not investigating many different options in the different exercises, due to shortage of crew members (that darn Kraken!), observations from the exercises still
|
|
|
|
led to some interesting discussions and insights. One interesting observation, in exercise 6, was that the actual color sensor does not function as well for distinguishing
|
|
|
|
black as the light sensor. Seeing as black, like white, is sort of a special case of 'colors', in that it is simply the absence of light (if we are talking seriously black black),
|
|
|
|
it makes sense that a light sensor functions better for the purpose of recognizing it than a color sensor.
|
|
|
|
|
|
|
|
It would be interesting to investigate the utilization of both a light sensor and a color sensor, perhaps using one for a double check on the program's interpretations of
|
|
|
|
the other's readings.
|
|
|
|
|
|
|
|
## References
|
|
|
|
[1] Program made for exercise 1: [RacistCopCalibrationTest.java](src/Lesson4programs/RacistCopCalibrationTest.java)
|
|
|
|
|
|
|
|
[2] Program used in exercise 2: [LineFollowerCal.java](src/Lesson4programs/LineFollowerCal.java)
|
|
|
|
|
|
|
|
[3] Program for exercise 3: [ThreeColorSensorNaive.java](src/Lesson4programs/ThreeColorSensorNaive.java)
|
|
|
|
|
|
|
|
[4] Program for exercise 3: [RacistAlienCopCalibrationTest.java](src/Lesson4programs/RacistAlienCopCalibrationTest.java)
|
|
|
|
|
|
|
|
[5] Program for exercise 4: [ThreeColorNaive.java](src/Lesson4programs/ThreeColorSensorNaive.java)
|
|
|
|
|
|
|
|
[6] Program for exercise 4: [ThreeColorSensorNarrow.java](src/Lesson4programs/ThreeColorSensorNarrow.java)
|
|
|
|
|
|
|
|
[7] Program for exercise 4: [ThreeColorNarrow.java](src/Lesson4programs/ThreeColorNarrow.java)
|
|
|
|
|
|
|
|
[8] Modification of **BlackWhiteSensor** program for exercise 5: [BlackWhiteSensor.java](src/Lesson4programs/BlackWhiteSensor.java)
|
|
|
|
|
|
|
|
[9] Pid follower for exercide 5: [PIDpolice.java](src/Lesson4programs/PIDpolice.java)
|
|
|
|
|
|
|
|
[10] Program used in exercise 6: [ColorSensorSensor.java](src/Lesson4programs/ColorSensorSensor.java)
|
|
|
|
|
|
|
|
[11] A PID Controller For Lego Mindstorms Robots: [PID Controller](http://www.inpharmix.com/jps/PID_Controller_For_Lego_Mindstorms_Robots.html)
|
|
|
|
|
|
|
|
[12] Video 1: [LineFollower with Calibration](https://www.youtube.com/watch?v=X11P_-ep1XA)
|
|
|
|
|
|
|
|
[13] Video 2: [LineFollower with Goal Zone, 1st attempt](https://www.youtube.com/watch?v=MSvhh_LDYcI)
|
|
|
|
|
|
|
|
[14] Video 3: [LineFollower with Goal Zone, 2nd attempt](https://www.youtube.com/watch?v=Bz6aXjhIfmo)
|
|
|
|
|
|
|
|
[15] Video 4: [PID C = 50](https://www.youtube.com/watch?v=gbfT4Hj9My0)
|
|
|
|
|
|
|
|
[16] Video 5: [LineFollower PID](https://www.youtube.com/watch?v=Qp6U7-HSuss) |
|
|
|
\ No newline at end of file |