In this lab, we used what we learned from lecture about PID control and implemented it to our car. We first had to set up the bluetooth connection and make sure that the Artemis and laptop could communicate information smoothly so that the laptop can use the information from the sensors to make necessary adjustments, and send that information back to the Artemis and so on.
Prelab - Sending Data Over Bluetooth
In order to get the lab to run smoothly, we need to send data to our laptops from the sensors on the car. Since I was planning on doing Task A, I focused on sending the data of only the front ToF sensor. It would be way too slow to send the data everytime I receive new data (ie every 0.5s) so the best way to go about this is the store the data first in the Artemis, and send it over when needed. The steps I took are as follows:
- Laptop –> Car: Manually send
start_task()
command. - Car: PID control while storing ToF data, speed, and timestamps in arrays.
- Laptop –> Car: Manually send
stop_task()
command when I see that the task has been completed. - Car –> Laptop: Sends the number of data points collected (which is either the size of the array or less) when
get_size()
is sent from laptop to car. - Laptop: Goes through a for loop and requests from the car the ToF data, speed and timestamps at every index from car. Car sends data at every index. Laptop stores it in an array. This step can take quite a while if we have a lot of data points.
- Plot graphs.
When I’m storing data, I don’t want to store too many data points unnecessarily so I chose to store readings every 0.5s. Since the rate I store the data at is slower than the sampling rate, I had two separate functions for them, store_data()
and sensor_data()
. Another issue that I wanted to combat is ensuring that I don’t exceed the internal RAM of 384kB. After testing out how many data points I need to test Task A, I found out that an array size of 150 is sufficient. I would increment the index, and reset it to 0 if I exceed the array size. This is where the timestamps come in handy because it wouldn’t matter where the data points are in the array if they correspond to their timestamp.
Lab Tasks
Task A - P(ID)
For this task, we had to use PID control to ensure that the car stops 300m (30cm) away from the wall. In order to do this, I first used only the P in PID. I calculated the error based on the ToF sensor reading and the target distance, obtained the speed from this value and a Kp value, and set the motor speed based on this as shown below:
I added a limiting range function to ensure that even as the speed goes beyond the maximum or below the minimum, it would still move.
After ensuring that the P(ID) control works, I needed to figure out what Kp value works. I did a lot of trial and error to find the values and these are the plots generated with different starting points and Kp values.
Below is the video demonstration and data points with the Kp = 0.03, starting point = ~2.5m configurations. The video shows the three runs I did to ensure consistency, and the graph shows the three data plots for each run (although the videos look almost identical, it was either that I accidentally used the same video thrice, or that I just took very very consistent videos… you can see in the plots that they are actually different runs).
As you can see from the video, with a Kp value of 0.03, the car almost hits the wall before moving back slightly. This can also be seen in the data plots where the distance hits close to 0 before oscillating once to reach 300mm. The reason the car overshoots is because it’s only using the proportional value of the error and does not take into account the integral or derivative and hence I tried including the derivative.
Although the graph shows that the final distance is around 300mm, I printed the last data point that the sensor read to see what the actual reading was.
It seems like the value was not actually 300mm although according to the conditions set, it should theoretically only stop when it reaches exactly 300mm, but this disparity between what is expected and the actual values is not shocking.
Task A - P(I)D
I added in the equation to find the derivative and add it to the speed of the motors. By adding the Kd value, when the change in the error is negative (the error is getting less), the Kd value would be negative and would subtract PWM value from the Kp to result in a lower speed_val
. This means that the speed would slow down quicker, and can afford to start off at a higher speed.
I tried a few Kp and Kd values and realized that, because of the improvement in performance of the car with the P(I)D control, I was able to use a higher Kp value than previously while still not crashing into a wall. This is good because with the P(ID) control, with a Kp value any higher than 0.3, the car would have crashed into the wall because it would be going too fast. Trial and error has gotten me a Kd value of 0.08.
As seen above, the P(ID) control takes approx. 6s to reach the goal while the P(I)D control takes approx. 4s on average to reach the goal.
Again, I printed out the final sensor values and it seems like the P(I)D control has more accurate results. I’m not 100% sure why, but because there’s less oscillation, the car reaches the target destination faster and more accurately stops at the right position.