Encoder Project Introduction
Our project involved adding an encoder component to the MAE 148 Autonomous Vehicle we previously built. The motivation for this project was to have access to data concerning the speed and distance travelled in order to improve speed control.
The original configuration of the vehicle controlled the speed of the car by controlling the voltage from the battery to the motor. This voltage was defined based on data from user inputs(explain in step 1). Adding the encoder allowed the voltage to be based on data concerning the actual current speed, giving the vehicle cruise control capability.
The first challenge our team had to face with this project was mounting the encoder. Once that was completed, we had to establish communication between the encoder and the Raspberry Pi board that served as the car's onboard computer. Since the encoder recorded data at a much faster rate than the Raspberry Pi could read, we needed to add a component between them to slow things down. We decided to use an Arduino Uno to accomplish this and perform a few other tasks, including displaying the speed or distance travelled. Once the communication was set up, it was time to add the PI controller to the code already on the Raspberry Pi to accomplish our project's goal of speed control with feedback.
Step One: Build the Autonomous Vehicle
Acrylic Board: We used calipers to measure the dimensions of the mounts on our RC car base and then laser cut an acrylic board with holes to attach camera mount and serve as a platform for all the electrical components.
Camera Mount: We designed and 3D printed a mount to hold the camera at the appropriate height and angle to capture the track. We included a slot to pass the cord band through to the Raspberry Pi without bending it.
Raspberry Pi Holder: We designed and 3D printed a tray to house the Raspberry Pi and attach it to our car.
Wiring: We wired together and mounted all the electronic components provided in our kit of parts. This required figuring out the pin outs of every part, soldering, and cable management. Then, we clearly documented our wiring in a wiring schematic.
Ubuntu or MobaXterm on our laptops were used to communicate with the Raspberry Pi. These were required because the communication had to be secure shell. We set up the DonkeyCar Framework provided by our professors to be able to calibrate and control the car with a PS3 controller as well as collect data for autonomous training.
The class had an indoor and outdoor track set up on campus with tape on the floor. We drove about sixty laps around each track using the PS3 controller with the camera recording. Then, we used given commands to connect to UCSD's GPU Cluster and send over our images to train a model for the car to drive autonomously.
We completed the class goal of driving 10 autonomous laps on each track on schedule.
Step Two: Mount the Encoder
We decided to mount the encoder on the bottom of the car next to a gear spun by the DC motor. First, we laser cut a gear for the encoder to mesh with the existing one. Then, we designed and 3D printed a mount that would attach the encoder to the car and keep the gears together. The encoder came with M3 holes on it's front face, so the printed mount incorporated clearance holes for those. Since it was too difficult to exactly measure where the holes for the attaching the bottom of the mount to the car needed to go in order to have the gears meshing well, they were made after the fact with heat set inserts. Finally, the gear was attached to the encoder with glue.
Step Three: Ardiuno Communication
As stated before, the encoder that we used sent 600 pulses per revolution. This resolution is amplified by gear ratio so every turn of the wheel causes approximitely three revolutions of the encoder. Additionally, for each pulse from the encoder, four readings can be taken (at the up phase and down phase of each swquare wave). Therefore, the encoder records 27400 data points per meter travelled, meaning at a maximimum velocity of approximately four meters per second, it records at 109.6 kHz. Meanwhile, the Raspberry Pi used for the remainder of our car controls runs its code at only 20 Hz. For this reason, we could not use the Raspberry Pi to directly read the response from the encoder. Instead, we used a Arduino Uno board to read and count the pulses of the encoder, then calculate the distance travelled and current velocity before sending this information over serial to the RPI and an LCD display at a rate of 20 Hz.
When connecting the encoder to the Arduino, we used the digital pin inputs and configured Arduino's built-in pull up resistor. Then, we wrote a code using the attachInterrupt function to signal a change whenever either the A or B phase signal went changed from HIGH to LOW or LOW to HIGH voltage. This ensured maximum resolution and accuracy because the interrupt saves time and ensures that signals are not skipped.
Once the Arduino code was working we calibrated it manually by driving the car 1m ten times and recording then averaging the number of counts the Arduino recorded to find a value of 27400 counts/meter, which gaves us an accuracy of 0.0365mm.
Step Four: Developing PI Control
Creating a Proportional Model
The purpose of having an encoder on our car is to create a closed-loop feedback system to control the velocity of the vehicle as it drives. Prior to installation of the encoder, velocity was controlled indirectly via the throttle, or power provided to the motors from the PWM. However, environmental variables such as the slope of the road, changing friction, or other disturbances make this method of velocity control inaccurate. The car will clearly need more power to maintain its speed while driving up a hill but because it cannot sense the slope of the road it is on, we must instead create a closed-loop system where we measure the car's actual velocity and compare it to the goal velocity, then use this error to adjust the throttle.
Initially, we started basic to come up with a formula for proportional control. We first calculated the approximate Gain value G, defined by v(t)=G*T(t), where v=velocity (m/s) and T(t)=throttle (range 0-1). At full throttle, our car has a maximum velocity of approximately 4 m/s, so we set the gain to G=4. Then, we needed to develop a value of Kp for the equation T(t)=Kp*e(t), where e(t)=SP-v(t) and SP is the "set point" or desired speed of the car. The maximum e(t) could be would be if we set SP to the maximum velocity and started the car from rest, in which case e(t)=4 m/s. Therefore, in order to not exceed a throttle of 1, we set Kp=1/4, which means Kp=1/G. However, with only using this equation of proportional control, the car rises and settles to a velocity of SP*KG/(1+KG)=SP/2. While this could be easily solved by using a SP value of twice our desired speed, if another steady disturbance arose the proportional control would not be able to correct this persisting error. Therefore, to increase the stability of our control system, we decided to add in integral control.
Stabilizing With Integral Control
When using PI control, instead of finding the throttle simply by multiplying the error by a gain Kp, we used this value to adjust the previous throttle value with each loop of the program. Therefore, instead of T(t)=Kp*e(t), we used the equation T(t)=Kp*e(t)+T(t-1). This change stabilized the system so that even with large disturbances, the velocity would settle to the SP value without causing amplifying oscillations.
To be sure that this equation would work the way we had hoped, we decided to model the system using MATLAB. Like our Raspberry Pi program, MATLAB runs in discrete time, not continuous, which makes it a good model to use. We created the program so that with each loop the program would calculate the new throttle using the equation T(t)=Kp*e(t)+T(t-1). However, when modelling the motion we incorporated a delay of one loop, or 0.05 seconds, before the new velocity takes effect because in our real life system the arduino only updates the RPI with the encoder data at a rate of 20 Hz. Additionally, we programmed in random disturbances to the velocity to be simulate environmental factors and show that our control would allow the car to correct for these disturbances. Finally, to verify the value of Kp we calculated before, we created code to simulate the car's response 10 times at 100 different Kp values between 0 and 1 and then pick the one which returned the smallest squared error. The MATLAB code confirmed that the Kp value should be approximately 0.25, or 1/G.