Lab 12 is based on everything we have put onto the car, and let the car hit the waypoints in the lab environment.
You can click on the image to view it in a larger window.
Theoretically, this lab should be implemented based on the full Bayes Filter or at least the update step of the Bayes Filter. But after trying three weeks to solve some
kind of weird bugs that interfere my localization result, I incline to implement something new instead of sticking to the lab requirements.
My goal in this lab is to let the car follow the wall, and it decides when to take a turn as it is at the corner. To achieve stable speed, I utilize the distance PID controller to divide the whole route into
20cm segments. This can prevent the speed being too fast to avoid crashing. When it detects it is in the corner, it will decide which direction to turn according to the readings of the two ToF sensors (one on the front and the
other one on the left side).
When the car is supposed to take a turn, the rotation PID controller will be activated until a 90 / -90 degree turn is achieved.
The code is shown below. The car is set to follow the wall on the left side since this is where the other ToF sensor is mounted. It turns right only when the distance from the two sensors are both limited.
try:
while(True):
ble.send_command(CMD.GET_DS, "")
time.sleep(0.2)
s = ble.receive_string(ble.uuid['RX_STRING']).split()
s = np.array(s).astype('int')
print(s)
if s[1] > 200 and s[2] < 600 :
cmd = "forward"
elif s[1] > 200 and s[2] >= 600 :
cmd = "left"
elif s[1] <= 200 and s[2] < 600:
cmd = "right"
elif s[1] <= 200 and s[2] >= 600:
cmd = "left"
print(cmd)
if cmd == "forward":
# ble.send_command(CMD.MOVE_FB, str(minPWM))
ble.send_command(CMD.SET_MAX_PWM, "220") # 145
time.sleep(0.2)
ble.send_command(CMD.SET_TARGET_DIS, str(max(s[1] - 200, 120)))
time.sleep(0.2)
ble.send_command(CMD.PID_TOF_ON, "145")
time.sleep(2)
ble.send_command(CMD.PID_TOF_OFF, "")
time.sleep(0.2)
cmd = "none"
elif cmd == "left":
ble.send_command(CMD.SET_MAX_PWM, "165") # 145
ble.send_command(CMD.STOP, "")
ble.send_command(CMD.PID_ROT_ON, "")
ble.send_command(CMD.SET_TARGET_YAW, "0")
ble.send_command(CMD.SET_TARGET_YAW, "90")
time.sleep(2.5)
ble.send_command(CMD.PID_ROT_OFF, "")
cmd = "none"
elif cmd == "right":
ble.send_command(CMD.SET_MAX_PWM, "165") # 145
ble.send_command(CMD.STOP, "")
ble.send_command(CMD.PID_ROT_ON, "")
ble.send_command(CMD.SET_TARGET_YAW, "0")
ble.send_command(CMD.SET_TARGET_YAW, "-90")
time.sleep(2.5)
ble.send_command(CMD.PID_ROT_OFF, "")
cmd = "none"
elif cmd == "none":
pass
time.sleep(1)
except KeyboardInterrupt:
ble.send_command(CMD.STOP, "")
print("Exit program")
ble.send_command(CMD.SET_CAL_FAC, "1.05")
ble.send_command(CMD.SET_MAX_PWM, "145") # 145
ble.send_command(CMD.SET_CAL_DT, "1.09") # 1.3752, 1.2117, 1.3126. 1.23967, 1.1664, 1.1958, 1.2434
Kp_R = 12.00
Ki_R = 8.5
Kd_R = 0.5
ble.send_command(CMD.SET_P_ROT, str(Kp_R)) # to be modified
ble.send_command(CMD.SET_I_ROT, str(Ki_R))
ble.send_command(CMD.SET_D_ROT, str(Kd_R))
Kp = 0.35
Ki = 0.015
Kd = 0.15
ble.send_command(CMD.SET_P, str(Kp)) # to be modified
ble.send_command(CMD.SET_I, str(Ki))
ble.send_command(CMD.SET_D, str(Kd))
The car is tested in a \( 1.8m \times 1.5m \) environment. Please refer to the videos below.
# Use Distance and Orientation PID controllers.
cmdr.reset_plotter()
cmdr.plot_bel(-4*0.302, -3*0.302)
# (-4, -3, 45deg) -> (-2, -1, 0deg)
# get the tof reading
ble.send_command(CMD.GET_DS, "")
s = ble.receive_string(ble.uuid['RX_STRING']).split()
DS1 = float(s[1])
print("Distance Sensor 1: {}".format(DS1))
# calculate the distance between waypoints
distance = np.sqrt(np.sum((np.array([-4, -3]) - np.array([-2, -1])) ** 2)) * 0.302 * 1000
print("Distance between two points: {}".format(distance))
# go straight line using distance PID
ble.send_command(CMD.SET_MAX_PWM, "80")
ble.send_command(CMD.SET_TARGET_DIS, str(DS1 - distance))
ble.send_command(CMD.PID_TOF_ON, "80")
print("Set goal distance to {}".format(DS1 - distance))
time.sleep(5.5)
ble.send_command(CMD.PID_TOF_OFF, "")
# turn using rotational PID
ble.send_command(CMD.SET_MAX_PWM, "145")
ble.send_command(CMD.PID_ROT_ON, "")
ble.send_command(CMD.SET_TARGET_YAW, "-45")
print("Set goal yaw to {}".format(-45))
time.sleep(3)
ble.send_command(CMD.PID_ROT_OFF, "")
cmdr.plot_bel(-2 * 0.304, -1 * 0.304)
time.sleep(1)
# (-2, -1, 0deg) -> (1, -1, -75deg)
# get the tof reading
ble.send_command(CMD.GET_DS, "")
s = ble.receive_string(ble.uuid['RX_STRING']).split()
DS1 = float(s[1])
print("Distance Sensor 1: {}".format(DS1))
# calculate the distance between waypoints
distance = np.sqrt(np.sum((np.array([-2, -1]) - np.array([1, -1])) ** 2)) * 0.302 * 1000
print("Distance between two points: {}".format(distance))
# go straight line using distance PID
ble.send_command(CMD.SET_MAX_PWM, "80")
ble.send_command(CMD.SET_TARGET_DIS, str(DS1 - distance))
ble.send_command(CMD.PID_TOF_ON, "80")
print("Set goal distance to {}".format(DS1 - distance))
time.sleep(2)
ble.send_command(CMD.PID_TOF_OFF, "")
# turn using rotational PID
ble.send_command(CMD.SET_MAX_PWM, "145")
targetYaw = -np.arctan(4) * 180 / np.pi
ble.send_command(CMD.PID_ROT_ON, "")
ble.send_command(CMD.SET_TARGET_YAW, str(targetYaw))
print("Set goal yaw to {}".format(targetYaw))
time.sleep(2)
ble.send_command(CMD.PID_ROT_OFF, "")
cmdr.plot_bel(1 * 0.304, -1 * 0.304)
time.sleep(1)
# (1, -1) -> (2, -3, 0deg)
ble.send_command(CMD.GET_DS, "")
s = ble.receive_string(ble.uuid['RX_STRING']).split()
DS1 = float(s[1])
print("Distance Sensor 1: {}".format(DS1))
# calculate the distance between waypoints
distance = np.sqrt(np.sum((np.array([2, -3]) - np.array([1, -1])) ** 2)) * 0.302 * 1000
print("Distance between two points: {}".format(distance))
# go straight line using distance PID
ble.send_command(CMD.SET_MAX_PWM, "80")
ble.send_command(CMD.SET_TARGET_DIS, str(DS1 - distance))
ble.send_command(CMD.PID_TOF_ON, "80")
print("Set goal distance to {}".format(DS1 - distance))
time.sleep(2)
ble.send_command(CMD.PID_TOF_OFF, "")
# turn using rotational PID
ble.send_command(CMD.SET_MAX_PWM, "145")
targetYaw = np.arctan(4) * 180 / np.pi
ble.send_command(CMD.PID_ROT_ON, "")
ble.send_command(CMD.SET_TARGET_YAW, str(targetYaw))
print("Set goal yaw to {}".format(targetYaw))
time.sleep(2)
ble.send_command(CMD.PID_ROT_OFF, "")
cmdr.plot_bel(2 * 0.304, -3 * 0.304)
time.sleep(1)
Many thanks to our instructor Dr. Jonathan Jaramillo and those helpful TAs, and also to Prof. Kirstin Petersen who established this course. I have learned how to build a fast-operating robot from scratch and it is my first time
implementing a PID controller, a Kalman Filter and a Gaussian Filter.
Despite what I have learned, I am not very satisfied with the robot I made. The robot is supposed to be "fast", but I don't think I have successfully achieved this goal. There're a lot of bugs I may not be able to solve before the deadline, many apologies if you're grading my report. But
I have concluded something for myself in case I have other robots to build in the future:
1. Always think ahead. Once the wires are soldered, it is hard to make adjustments. It is necessary to think in the system level to prevent bugs in the hardware level.
2. Code according to the hardware capacity. I thought I did a great job in creating sophisticated condition statements in the Arduino, thinking in a python way. But the Artemis
board may crush if the code is long, also the real-time performance will be damaged. For example, I was using the time difference of two consecutive entries to the rotation PID controller to
calculate the dt for yaw based on GYRO. But once the code gets complex, there's around 30% error of the dt (calculation results in dt = 0.02s, but it should be 1.3 times larger). I might be a good programmer for
better embedded systems, but the mindset is not the same for an arduino chip.
3. Open to a restart. If I redo everything in my car earlier in a way I thought was better, I might have avoided those sunk costs I have paid. The fact is I have spent so much time but the bugs are still rolling bigger.
4. Learn from others. Be open to the ideas with good results, which saves you time for better outcome. You may figure out a better way in the end, but for how long?
5. Secure every step. Otherwise the bugs will be the rolling snowball, and you don't even know which to begin with.
THE END