2021SummerTeam4
Meet the Misfits - Team 4
From Left to Right: Felipe Ramos - Mechanical Engineering, Esther Grossman - Electrical & Computer Engineering, Paul Bae - Aerospace Engineering
Mistfit Car
Objectives
Mechanical Design
Mount Plate
Camera Mount
Jetson Nano Case
Chassis Hinge
Hood Bumper
Spoiler
Lidar Housing
Evolution
Circuit Design
DonkeyCar Autonomous Racing
{{#evu:https://www.youtube.com/watch?v=aVhgmDBfa-E?rel=0 |alignment=left }}
The footage found here for the autonomous three laps using Donkey Car used a model in which data sets with different purposes were used together to get this working set. The foundation of this model used data that only focused on following the yellow line. This meant that the racing line wasn’t always necessarily smooth. With this in mind, this is where the other sets of data came into play. By mixing in data in which the racing line was prioritized, the model learned to take a path that focused on stability, all while following the yellow line.
OpenCV Autonomous Racing
The objective of our next autonomous racing model was to perform laps around the same track using python OpenCV segmentation. The process was run using ROS Noetic on a docker container created by Dominic.
We first isolated the white lanes of the track, with the intention of doing lane guidance by following the center of all the white lines isolated in the images. We additionally included the dashed yellow center line, which was not a problem since it falls in the center of the track and therefore provided additional guidance data. The algorithm identified the white and yellow contours within a pre-sized field of vision. Then, it drew bounding boxes around the contours and removed boxes below a designated width in order to reduce noise. The centroid of each box was found and its horizontal distance from the center of the car's field of vision was calculated. If the average of the boxes' distances was more than a preset error threshold, the car would assume it is turning and reduce its throttle. When it was within the error_threshold, it would increase its throttle. The steering value was taken as a linear function of the average distance from center.
Isolating white and yellow lines produced too much noise that steered the car off route. We decided to isolate only the yellow center line of the track. This produced a more reliable model. Of course, if the car steered off the track and could no longer see yellow lines, it had little hope of returning to the track, but this was not a frequent problem.
The final issue encountered was the recognition of cones. The car could often not distinguish between the yellow center line and the orange cones, leading it to steer toward the cones in a head-on collision. We added a layer of pre-processing in openCV to remove the orange cones and replace them with black pixels. This led the car to avoid all but one cone, whose pixels were not entirely erased in pre-processing due to its color shading. Nonetheless, a little tweaking of the yellow center line segmentation resulted in good success (balancing the stringency of the criteria to be considered yellow and the need to detect an adequate number of yellow lines).
{{#evu:https://www.youtube.com/watch?v=1kJeqHcrquc?rel=0 |alignment=left |center| }}
Final Project: Obstacle Avoidance Using LIDAR
For our final project, we experimented with a 360 degree RPlidar.
{{#evu:https://www.youtube.com/watch?v=pJxKa6_FetI?rel=0 |alignment=left |center| }}
Python Code
#!/usr/bin/env python # This program implements a path planner and PID controller loosely based on artificial potential field methods import rospy from std_msgs.msg import Float32, Int32, Int32MultiArray from sensor_msgs.msg import LaserScan import numpy as np from numpy import pi from adafruit_servokit import ServoKit OBSTACLE_DETECTION_NODE_NAME = 'obstacle_detection_node' STEERING_TOPIC_NAME = '/steering' THROTTLE_TOPIC_NAME = '/throttle' CENTROID_TOPIC_NAME = '/centroid' SCAN_TOPIC_NAME = '/scan' kit = ServoKit(channels =16) class PathPlanner: def __init__(self): # Initialize node and create publishers/subscribers self.init_node = rospy.init_node(OBSTACLE_DETECTION_NODE_NAME, anonymous=False) self.steering_publisher = rospy.Publisher(STEERING_TOPIC_NAME, Float32, queue_size=1) self.throttle_publisher = rospy.Publisher(THROTTLE_TOPIC_NAME, Float32, queue_size=1) self.steering_float = Float32() self.throttle_float = Float32() self.scan_subscriber = rospy.Subscriber(SCAN_TOPIC_NAME, LaserScan, self.controller) # Calibrate these parameters self.error_threshold = 5 #degrees self.maxSteerL = 40 #degrees self.maxSteerR = 145 #degrees self.SteerC = 90 #degrees self.zero_throttle = 0.0 self.fast_throttle = 0.15 self.slow_throttle = 0.145 self.max_obstacle_influence = 1.5 #meters # # Display Parameters rospy.loginfo( f'\nno_error_throttle: {self.fast_throttle}' f'\nnerror_throttle: {self.slow_throttle}') def controller(self, scandata): #Facing forward is 0 radians for RPlidar. Remap range to 0 is left and 180 is right. left_angle = -pi/3; right_angle = pi/3 first_half_ranges = scandata.ranges[int(left_angle/scandata.angle_increment):] second_half_ranges = scandata.ranges[:int(right_angle/scandata.angle_increment)] forward_ranges = np.concatenate((first_half_ranges,second_half_ranges)) range_min = np.amin(forward_ranges) range_min_index = np.argmin(forward_ranges) range_min_angle = (range_min_index * scandata.angle_increment * 180/pi)+30 wheel_angle = kit.servo[1].angle rospy.logerr(wheel_angle) #Display wheel_angle each iteration #Determine if obstacle is worth avoiding, then determine if it is on then #left or right of the robots field of vision. Ajust steering to go parallel #to any obstacles if range_min <= self.max_obstacle_influence: throttle_float = self.slow_throttle kp = 1.5*np.exp(-range_min) #steering sensitivity if range_min_angle < 90: steer_angle = 90 + range_min_angle #angle perp to obstacle surface obstacle_wheel_diff = wheel_angle - steer_angle if obstacle_wheel_diff < 0 : wheel_angle = wheel_angle + kp*abs(obstacle_wheel_diff) else: wheel_angle = wheel_angle - kp*abs(obstacle_wheel_diff) else: steer_angle = -90 + range_min_angle #angle perp to obstacle surface obstacle_wheel_diff = wheel_angle - steer_angle if obstacle_wheel_diff > 0 : wheel_angle = wheel_angle - kp*abs(obstacle_wheel_diff) else : wheel_angle = wheel_angle + kp*abs(obstacle_wheel_diff) else: throttle_float = self.fast_throttle #Send out the normalized steering instructions if wheel_angle > self.maxSteerR: wheel_angle = self.maxSteerR elif wheel_angle < self.maxSteerL: wheel_angle = self.maxSteerL steering_float = - (wheel_angle/90 - 1) self.steering_float.data = steering_float self.throttle_float.data = throttle_float self.steering_publisher.publish(self.steering_float) self.throttle_publisher.publish(self.throttle_float) rospy.on_shutdown(self.clean_shutdown) def clean_shutdown(self): print("Shutting Down...") self.throttle_float.data = self.zero_throttle self.throttle_publisher.publish(self.throttle_float) print("Shut down complete") def main(): path_planner = PathPlanner() rate = rospy.Rate(15) while not rospy.is_shutdown(): rospy.spin() rate.sleep() if __name__ == '__main__': main()