> 文档中心 > 蓝桥ROS之f1tenth简单PID沿墙跑起来(Python)

蓝桥ROS之f1tenth简单PID沿墙跑起来(Python)

在此案例前需完成:

☞ ​​​​​​蓝桥ROS之f1tenth案例学习与调试(成功)

 

 


 

 

遥控肯定不过瘾,那么如何用一个PID程序使小车自动沿墙跑呢???

公式如上……

跑一跑看看?

 

阅读pdf文档:

python程序模板

#!/usr/bin/env pythonfrom __future__ import print_functionimport sysimport mathimport numpy as np#ROS Importsimport rospyfrom sensor_msgs.msg import Image, LaserScanfrom ackermann_msgs.msg import AckermannDriveStamped, AckermannDrive#PID CONTROL PARAMSkp = #TODOkd = #TODOki = #TODOservo_offset = 0.0prev_error = 0.0 error = 0.0integral = 0.0#WALL FOLLOW PARAMSANGLE_RANGE = 270 # Hokuyo 10LX has 270 degrees scanDESIRED_DISTANCE_RIGHT = 0.9 # metersDESIRED_DISTANCE_LEFT = 0.55VELOCITY = 2.00 # meters per secondCAR_LENGTH = 0.50 # Traxxas Rally is 20 inches or 0.5 metersclass WallFollow:    """ Implement Wall Following on the car    """    def __init__(self): #Topics & Subs, Pubs lidarscan_topic = '/scan' drive_topic = '/nav' self.lidar_sub = #TODO: Subscribe to LIDAR self.drive_pub = #TODO: Publish to drive    def getRange(self, data, angle): # data: single message from topic /scan # angle: between -45 to 225 degrees, where 0 degrees is directly to the right # Outputs length in meters to object with angle in lidar scan field of view #make sure to take care of nans etc. #TODO: implement return 0.0    def pid_control(self, error, velocity): global integral global prev_error global kp global ki global kd angle = 0.0 #TODO: Use kp, ki & kd to implement a PID controller for  drive_msg = AckermannDriveStamped() drive_msg.header.stamp = rospy.Time.now() drive_msg.header.frame_id = "laser" drive_msg.drive.steering_angle = angle drive_msg.drive.speed = velocity self.drive_pub.publish(drive_msg)    def followLeft(self, data, leftDist): #Follow left wall as per the algorithm  #TODO:implement return 0.0     def lidar_callback(self, data): """  """ error = 0.0 #TODO: replace with error returned by followLeft #send error to pid_control self.pid_control(error, VELOCITY)def main(args):    rospy.init_node("WallFollow_node", anonymous=True)    wf = WallFollow()    rospy.sleep(0.1)    rospy.spin()if __name__=='__main__':main(sys.argv)

 参考程序:

scan.py

import rospyfrom sensor_msgs.msg import LaserScandef callback(data):    print(data.ranges[270])rospy.init_node("scan_test", anonymous=False)sub = rospy.Subscriber("/scan", LaserScan, callback)rospy.spin()

dist.py 

import rospyimport mathfrom sensor_msgs.msg import LaserScanfrom race.msg import pid_inputangle_range = 360# sensor angle range of the lidarcar_length = 1.5# projection distance we project car forward. vel = 1.5 # used for pid_vel (not much use).error = 0.0dist_from_wall = 0.8pub = rospy.Publisher('error', pid_input, queue_size=10)def getRange(data,theta):angle_range = data.angle_max - data.angle_minangle_increment  = data.angle_incrementscan_range = []rad2deg_factor = 57.296angle_range *= rad2deg_factorangle_increment *= rad2deg_factor for element in data.ranges:if math.isnan(element) or math.isinf(element):element = 100scan_range.append(element)index = round(theta / angle_increment)return scan_range[index]def callback(data):theta = 55left_dist = float(getRange(data, 270))a = getRange(data,(90 + theta)) # Ray at degree theta from right_distright_dist = getRange(data,90)# Ray perpendicular to right side of cartheta_r = math.radians(theta)# dist_from_wall = (left_dist + right_dist)/2 # keep in middle# Calculating the deviation of steering(alpha)alpha = math.atan( (a * math.cos(theta_r) - right_dist)/ a * math.sin(theta_r) )AB = right_dist * math.cos(alpha) # Present PositionCD = AB + car_length * math.sin(alpha) # projection in Future Positionerror = dist_from_wall - CD # total error# print('error: ', error) #Testing# Sending PID error to Controlmsg = pid_input()msg.pid_error = errormsg.pid_vel = vel pub.publish(msg)if __name__ == '__main__':print("Laser node started")rospy.init_node('dist_finder',anonymous = True)rospy.Subscriber("scan",LaserScan,callback)rospy.spin()

control.py

import rospyfrom race.msg import pid_inputfrom ackermann_msgs.msg import AckermannDriveStampedkp = 0.7 #45kd = 0.00125#0.001 #0.09ki = 0#0.5kp_vel = 6 #9 is using keep in middlekd_vel = 0.0001ki_error = 0prev_error = 0.0 vel_input = 2.5# base velocityangle = 0.0# initial steering anglepub = rospy.Publisher("/drive", AckermannDriveStamped, queue_size=5)def control(data):global prev_errorglobal vel_inputglobal kpglobal kp_velglobal kdglobal kd_velglobal kiglobal anglee = data.pid_error# Calculating deviation for lateral deviation from pathkp_error = kp * ekd_error = kd * (e - prev_error)# Calculating error for velocitykp_vel_er = kp_vel * ekd_vel_er = kd * (e - prev_error)# ki_error = ki_error + (ki * e)vel_error = kp_vel_er + kd_vel_erpid_error = kp_error + kd_errormin_angle=-20max_angle= 20# Heigher error results in lower velocity # while lower error results in heigher velocityvelo = vel_input + 1/(abs(vel_error))#corrected steering angleangle = pid_error#print("raw velo:", velo) # Testing#Speed limitif velo > 15 :velo = 10 # Filtering steering angle for Out-of-Range valuesif angle  max_angle:angle = max_angle# print("filtered angle :" , angle) # Testing# Sending Drive information to Carmsg = AckermannDriveStamped()msg.header.stamp = rospy.Time.now()msg.header.frame_id = ''msg.drive.speed = velomsg.drive.steering_angle = anglepub.publish(msg)if __name__ == '__main__':print("Starting control...")rospy.init_node('pid_controller', anonymous=True)rospy.Subscriber("error", pid_input, control)rospy.spin()

 centre_wall_follow.py 

import rospyimport mathfrom sensor_msgs.msg import LaserScanfrom race.msg import pid_inputangle_range = 360# sensor angle range of the lidarcar_length = 1.5# projection distance we project car forward. vel = 1.0 # used for pid_vel (not much use).error = 0.0pub = rospy.Publisher('error', pid_input, queue_size=10)def getRange(data,theta):    angle_range = data.angle_max - data.angle_min    angle_increment  = data.angle_increment    scan_range = []    rad2deg_factor = 57.296    angle_range *= rad2deg_factor    angle_increment *= rad2deg_factor    for element in data.ranges: if math.isnan(element) or math.isinf(element):     element = 100 scan_range.append(element)    index = round(theta / angle_increment)    return scan_range[index]def callback(data):    dist_in_front = getRange(data, 180)    theta = 30    left_dist = getRange(data, 270)    right_dist = getRange(data,90)# Ray perpendicular to right side of car    a_right = getRange(data,(90 + theta)) # Ray at degree theta from right_dist    a_left = getRange(data,(270 - theta))    theta = math.radians(theta)    # Calculating the deviation of steering(alpha) from right    alpha_r = math.atan( (a_right * math.cos(theta) - right_dist)/ a_right * math.sin(theta) )    curr_pos_r = right_dist * math.cos(alpha_r) # Present Position    fut_pos_r = curr_pos_r + car_length * math.sin(alpha_r) # projection in Future Position    # Calculating the deviation of steering(alpha) from left    alpha_l = math.atan( (a_left * math.cos(theta) - left_dist)/ a_left * math.sin(theta) )    curr_pos_l = left_dist * math.cos(alpha_l) # Present Position    fut_pos_l = curr_pos_l + car_length * math.sin(alpha_l) # projection in Future Position    error = - (fut_pos_r - fut_pos_l) # total error    # print('error: ', error) #Testing    # Sending PID error to Control    msg = pid_input()    msg.pid_error = error    msg.pid_vel = dist_in_front # pid_vel used for distance in front    pub.publish(msg)if __name__ == '__main__':    print("Laser node started")    rospy.init_node('dist_finder',anonymous = True)    rospy.Subscriber("scan",LaserScan,callback)    rospy.spin()

centre_wall_control.py 

import rospyimport mathfrom race.msg import pid_inputfrom ackermann_msgs.msg import AckermannDriveStampedkp = 0.27# 0.27 for portokd = 0.0125#0.001 #0.09ki = 0 #0.5kp_vel = 0.9 #9 is using keep in middlekd_vel = 0.0001ki_error = 0prev_error = 0.0 vel_input = 2.5# base velocityangle = 0.0# initial steering anglepub = rospy.Publisher("/drive", AckermannDriveStamped, queue_size=5)def control(data):global prev_errorglobal vel_inputglobal kpglobal kp_velglobal kdglobal kd_velglobal kiglobal angledist_in_front = data.pid_velfront_obs = 1e = data.pid_error# Calculating deviation for lateral deviation from pathkp_error = kp * ekd_error = kd * (e - prev_error)# Calculating error for velocitykp_vel_er = kp_vel * ekd_vel_er = kd * (e - prev_error)# ki_error = ki_error + (ki * e)vel_error = kp_vel_er + kd_vel_erpid_error = kp_error + kd_errormin_angle=-20max_angle= 20# Heigher error results in lower velocity# while lower error results in heigher velocityif dist_in_front  50 :velo = 50# Filtering steering angle for Out-of-Range valuesif angle  max_angle:angle = max_angle# print("filtered angle :" , angle) # Testing# Sending Drive information to Carmsg = AckermannDriveStamped()msg.header.stamp = rospy.Time.now()msg.header.frame_id = ''msg.drive.speed = velomsg.drive.steering_angle = anglepub.publish(msg)if __name__ == '__main__':print("Starting control...")rospy.init_node('pid_controller', anonymous=True)rospy.Subscriber("error", pid_input, control)rospy.spin()

 (⊙﹏⊙)


如果按模板写不行吗???

参考1:

#!/usr/bin/env pythonfrom __future__ import print_functionimport sysimport mathimport numpy as np#ROS Importsimport rospyfrom sensor_msgs.msg import Image, LaserScanfrom ackermann_msgs.msg import AckermannDriveStamped, AckermannDrive#PID CONTROL PARAMSkp = 1.0kd = 0.001ki = 0.005servo_offset = 0.0prev_error = 0.0 error = 0.0integral = 0.0prev_time = 0.0#WALL FOLLOW PARAMSANGLE_RANGE = 270 # Hokuyo 10LX has 270 degrees scanDESIRED_DISTANCE_RIGHT = 0.9 # metersDESIRED_DISTANCE_LEFT = 0.85VELOCITY = 1.5 # meters per secondCAR_LENGTH = 1.0 # Traxxas Rally is 20 inches or 0.5 metersclass WallFollow:    """ Implement Wall Following on the car    """    def __init__(self): global prev_time #Topics & Subs, Pubs lidarscan_topic = '/scan' drive_topic = '/nav' prev_time = rospy.get_time() self.lidar_sub = rospy.Subscriber(lidarscan_topic, LaserScan, self.lidar_callback) self.drive_pub = rospy.Publisher(drive_topic, AckermannDriveStamped, queue_size = 10)    def getRange(self, data, angle): # data: single message from topic /scan # angle: between -45 to 225 degrees, where 0 degrees is directly to the right # Outputs length in meters to object with angle in lidar scan field of view #make sure to take care of nans etc. #TODO: implement if angle >= -45 and angle  math.radians(0) and abs(angle)  math.radians(10) and abs (angle) <= math.radians(20):     drive_msg.drive.speed = 1.0 else:     drive_msg.drive.speed = 0.5 self.drive_pub.publish(drive_msg)    def followLeft(self, data, leftDist): #Follow left wall as per the algorithm  #TODO:implement front_scan_angle = 125 back_scan_angle = 180 teta = math.radians(abs(front_scan_angle - back_scan_angle)) front_scan_dist = self.getRange(data, front_scan_angle) back_scan_dist = self.getRange(data, back_scan_angle) alpha = math.atan2(front_scan_dist * math.cos(teta) - back_scan_dist, front_scan_dist * math.sin(teta)) wall_dist = back_scan_dist * math.cos(alpha) ahead_wall_dist = wall_dist + CAR_LENGTH * math.sin(alpha) return leftDist - ahead_wall_dist    def lidar_callback(self, data): """  """ error = self.followLeft(data.ranges, DESIRED_DISTANCE_LEFT) #TODO: replace with error returned by followLeft #send error to pid_control self.pid_control(error, VELOCITY)def main(args):    rospy.init_node("WallFollow_node", anonymous=True)    wf = WallFollow()    rospy.sleep(0.1)    rospy.spin()if __name__=='__main__':main(sys.argv)

参考2:

#!/usr/bin/env python3import sysimport mathimport numpy as np#ROS Importsimport rospyfrom sensor_msgs.msg import LaserScanfrom ackermann_msgs.msg import AckermannDriveStamped#PID CONTROL PARAMSkp = 10kd = 70ki = 0.00001prev_error = 0 integral = 0#WALL FOLLOW PARAMSANGLE_RANGE = 270 # Hokuyo 10LX has 270 degrees scanclass WallFollow:""" Implement Wall Following on the car"""def __init__(self):self.rate = rospy.Rate(10)self.lidar_sub = rospy.Subscriber('/scan' , LaserScan, self.lidar_callback, queue_size = 1)self.drive_pub = rospy.Publisher('/nav', AckermannDriveStamped, queue_size = 1)def getRange(self, data, angle, distance):self.Dt_max = Falseangle_btwnScan = 70future_dist = 0.55theta = angle[int((180-angle_btwnScan+45)/ANGLE_RANGE * len(angle))]a = distance[int((180-angle_btwnScan+45)/ANGLE_RANGE * len(angle))]b = distance[int((180+45)/ANGLE_RANGE * len(angle))]alpha = math.atan((a*np.cos(np.pi-theta) - b)/a*np.sin(np.pi-theta))Dt = b*np.cos(alpha)Dt1 = Dt + future_dist*np.sin(alpha)# check condition for bottom part of mapa2 = a = distance[int((180-15+45)/ANGLE_RANGE * len(angle))]theta2 = angle[int((180-15+45)/ANGLE_RANGE * len(angle))]alpha2 = math.atan((a2*np.cos(np.pi-theta2) - b)/a2*np.sin(np.pi-theta2))bot_error = a2*np.cos(np.pi-theta2+alpha2)Dt2 = b*np.cos(alpha2)self.bot_state = math.isclose(bot_error, Dt2, rel_tol = 0.1)if Dt > 1.1:self.Dt_max = Truereturn Dt1def filter_scan(self, scan_msg):angle_range = enumerate(scan_msg.ranges)filter_data = [[count*scan_msg.angle_increment, val] for count, val in angle_range if not np.isinf(val) and not np.isnan(val)]filter_data = np.array(filter_data)angles = filter_data[:,0]distance = filter_data[:,1]filter_angle = np.array([])idx = 0start_idx = 0end_idx = 0for angle in angles:if (not 0 <= angle < np.pi/4) and (not 7*np.pi/4 < angle  7*np.pi/4:end_idx = idx - 1idx += 1distance = distance[start_idx: end_idx+1]return filter_angle, distancedef pid_control(self, error):global integralglobal prev_errorglobal kpglobal kiglobal kdintegral += errorangle = kp*error + kd*(error - prev_error) + ki*integralprev_error = errorif self.bot_state == True and self.Dt_max == True:angle = -0.01*np.pi/180if -np.pi/18 < angle < np.pi/18:velocity = 1.5elif -np.pi/9 < angle <= -np.pi/18 or np.pi/18 <= angle < np.pi/9:velocity = 1else:velocity = 0.5drive_msg = AckermannDriveStamped()drive_msg.header.stamp = rospy.Time.now()drive_msg.header.frame_id = "laser"drive_msg.drive.steering_angle = angledrive_msg.drive.speed = velocityself.drive_pub.publish(drive_msg)def followLeft(self, leftDist):distToWall = 0.55error = -(distToWall - leftDist)return error    def lidar_callback(self, data):try:angle, distance = self.filter_scan(data)Dt1 = self.getRange(data, angle, distance)error = self.followLeft(Dt1)self.pid_control(error)except rospy.ROSException as e:#rospy.logerr(e)passdef main(args):rospy.init_node("WallFollow_node", anonymous=True)wf = WallFollow()rospy.spin()if __name__=='__main__':main(sys.argv)

试一试看:

# Keyboard characters for toggling muxjoy_key_char: "j"keyboard_key_char: "k"brake_key_char: "b"random_walk_key_char: "r"nav_key_char: "n"# **Add button for new planning method here**new_key_char: "z"

 


 

蓝桥ROS之f1tenth简单PID沿墙跑起来(Python) 创作打卡挑战赛 蓝桥ROS之f1tenth简单PID沿墙跑起来(Python) 赢取流量/现金/CSDN周边激励大奖