pid_velocity.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 """
00003    pid_velocity - takes messages on wheel_vtarget 
00004       target velocities for the wheels and monitors wheel for feedback
00005       
00006     Copyright (C) 2012 Jon Stephan. 
00007      
00008     This program is free software: you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation, either version 3 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 """
00021 
00022 import rospy
00023 import roslib
00024 
00025 from std_msgs.msg import Int16
00026 from std_msgs.msg import Float32
00027 from numpy import array
00028 
00029     
00030 ######################################################
00031 ######################################################
00032 class PidVelocity():
00033 ######################################################
00034 ######################################################
00035 
00036 
00037     #####################################################
00038     def __init__(self):
00039     #####################################################
00040         rospy.init_node("pid_velocity")
00041         self.nodename = rospy.get_name()
00042         rospy.loginfo("%s started" % self.nodename)
00043         
00044         ### initialize variables
00045         self.target = 0
00046         self.motor = 0
00047         self.vel = 0
00048         self.integral = 0
00049         self.error = 0
00050         self.derivative = 0
00051         self.previous_error = 0
00052         self.wheel_prev = 0
00053         self.wheel_latest = 0
00054         self.then = rospy.Time.now()
00055         self.wheel_mult = 0
00056         self.prev_encoder = 0
00057         
00058         ### get parameters #### 
00059         self.Kp = rospy.get_param('~Kp',10)
00060         self.Ki = rospy.get_param('~Ki',10)
00061         self.Kd = rospy.get_param('~Kd',0.001)
00062         self.out_min = rospy.get_param('~out_min',-255)
00063         self.out_max = rospy.get_param('~out_max',255)
00064         self.rate = rospy.get_param('~rate',30)
00065         self.rolling_pts = rospy.get_param('~rolling_pts',2)
00066         self.timeout_ticks = rospy.get_param('~timeout_ticks',4)
00067         self.ticks_per_meter = rospy.get_param('ticks_meter', 20)
00068         self.vel_threshold = rospy.get_param('~vel_threshold', 0.001)
00069         self.encoder_min = rospy.get_param('encoder_min', -32768)
00070         self.encoder_max = rospy.get_param('encoder_max', 32768)
00071         self.encoder_low_wrap = rospy.get_param('wheel_low_wrap', (self.encoder_max - self.encoder_min) * 0.3 + self.encoder_min )
00072         self.encoder_high_wrap = rospy.get_param('wheel_high_wrap', (self.encoder_max - self.encoder_min) * 0.7 + self.encoder_min )
00073         self.prev_vel = [0.0] * self.rolling_pts
00074         self.wheel_latest = 0.0
00075         self.prev_pid_time = rospy.Time.now()
00076         rospy.logdebug("%s got Kp:%0.3f Ki:%0.3f Kd:%0.3f tpm:%0.3f" % (self.nodename, self.Kp, self.Ki, self.Kd, self.ticks_per_meter))
00077         
00078         #### subscribers/publishers 
00079         rospy.Subscriber("wheel", Int16, self.wheelCallback) 
00080         rospy.Subscriber("wheel_vtarget", Float32, self.targetCallback) 
00081         self.pub_motor = rospy.Publisher('motor_cmd',Float32) 
00082         self.pub_vel = rospy.Publisher('wheel_vel', Float32)
00083    
00084         
00085     #####################################################
00086     def spin(self):
00087     #####################################################
00088         self.r = rospy.Rate(self.rate) 
00089         self.then = rospy.Time.now()
00090         self.ticks_since_target = self.timeout_ticks
00091         self.wheel_prev = self.wheel_latest
00092         self.then = rospy.Time.now()
00093         while not rospy.is_shutdown():
00094             self.spinOnce()
00095             self.r.sleep()
00096             
00097     #####################################################
00098     def spinOnce(self):
00099     #####################################################
00100         self.previous_error = 0.0
00101         self.prev_vel = [0.0] * self.rolling_pts
00102         self.integral = 0.0
00103         self.error = 0.0
00104         self.derivative = 0.0 
00105         self.vel = 0.0
00106         
00107         # only do the loop if we've recently recieved a target velocity message
00108         while not rospy.is_shutdown() and self.ticks_since_target < self.timeout_ticks:
00109             self.calcVelocity()
00110             self.doPid()
00111             self.pub_motor.publish(self.motor)
00112             self.r.sleep()
00113             self.ticks_since_target += 1
00114             if self.ticks_since_target == self.timeout_ticks:
00115                 self.pub_motor.publish(0)
00116             
00117     #####################################################
00118     def calcVelocity(self):
00119     #####################################################
00120         self.dt_duration = rospy.Time.now() - self.then
00121         self.dt = self.dt_duration.to_sec()
00122         rospy.logdebug("-D- %s caclVelocity dt=%0.3f wheel_latest=%0.3f wheel_prev=%0.3f" % (self.nodename, self.dt, self.wheel_latest, self.wheel_prev))
00123         
00124         if (self.wheel_latest == self.wheel_prev):
00125             # we haven't received an updated wheel lately
00126             cur_vel = (1 / self.ticks_per_meter) / self.dt    # if we got a tick right now, this would be the velocity
00127             if abs(cur_vel) < self.vel_threshold: 
00128                 # if the velocity is < threshold, consider our velocity 0
00129                 rospy.logdebug("-D- %s below threshold cur_vel=%0.3f vel=0" % (self.nodename, cur_vel))
00130                 self.appendVel(0)
00131                 self.calcRollingVel()
00132             else:
00133                 rospy.logdebug("-D- %s above threshold cur_vel=%0.3f" % (self.nodename, cur_vel))
00134                 if abs(cur_vel) < self.vel:
00135                     rospy.logdebug("-D- %s cur_vel < self.vel" % self.nodename)
00136                     # we know we're slower than what we're currently publishing as a velocity
00137                     self.appendVel(cur_vel)
00138                     self.calcRollingVel()
00139             
00140         else:
00141             # we received a new wheel value
00142             cur_vel = (self.wheel_latest - self.wheel_prev) / self.dt
00143             self.appendVel(cur_vel)
00144             self.calcRollingVel()
00145             rospy.logdebug("-D- %s **** wheel updated vel=%0.3f **** " % (self.nodename, self.vel))
00146             self.wheel_prev = self.wheel_latest
00147             self.then = rospy.Time.now()
00148             
00149         self.pub_vel.publish(self.vel)
00150         
00151     #####################################################
00152     def appendVel(self, val):
00153     #####################################################
00154         self.prev_vel.append(val)
00155         del self.prev_vel[0]
00156         
00157     #####################################################
00158     def calcRollingVel(self):
00159     #####################################################
00160         p = array(self.prev_vel)
00161         self.vel = p.mean()
00162         
00163     #####################################################
00164     def doPid(self):
00165     #####################################################
00166         pid_dt_duration = rospy.Time.now() - self.prev_pid_time
00167         pid_dt = pid_dt_duration.to_sec()
00168         self.prev_pid_time = rospy.Time.now()
00169         
00170         self.error = self.target - self.vel
00171         self.integral = self.integral + (self.error * pid_dt)
00172         # rospy.loginfo("i = i + (e * dt):  %0.3f = %0.3f + (%0.3f * %0.3f)" % (self.integral, self.integral, self.error, pid_dt))
00173         self.derivative = (self.error - self.previous_error) / pid_dt
00174         self.previous_error = self.error
00175     
00176         self.motor = (self.Kp * self.error) + (self.Ki * self.integral) + (self.Kd * self.derivative)
00177     
00178         if self.motor > self.out_max:
00179             self.motor = self.out_max
00180             self.integral = self.integral - (self.error * pid_dt)
00181         if self.motor < self.out_min:
00182             self.motor = self.out_min
00183             self.integral = self.integral - (self.error * pid_dt)
00184       
00185         if (self.target == 0):
00186             self.motor = 0
00187     
00188         rospy.logdebug("vel:%0.2f tar:%0.2f err:%0.2f int:%0.2f der:%0.2f ## motor:%d " % 
00189                       (self.vel, self.target, self.error, self.integral, self.derivative, self.motor))
00190     
00191     
00192 
00193 
00194     #####################################################
00195     def wheelCallback(self, msg):
00196     ######################################################
00197         enc = msg.data
00198         if (enc < self.encoder_low_wrap and self.prev_encoder > self.encoder_high_wrap) :
00199             self.wheel_mult = self.wheel_mult + 1
00200             
00201         if (enc > self.encoder_high_wrap and self.prev_encoder < self.encoder_low_wrap) :
00202             self.wheel_mult = self.wheel_mult - 1
00203            
00204          
00205         self.wheel_latest = 1.0 * (enc + self.wheel_mult * (self.encoder_max - self.encoder_min)) / self.ticks_per_meter 
00206         self.prev_encoder = enc
00207         
00208         
00209 #        rospy.logdebug("-D- %s wheelCallback msg.data= %0.3f wheel_latest = %0.3f mult=%0.3f" % (self.nodename, enc, self.wheel_latest, self.wheel_mult))
00210     
00211     ######################################################
00212     def targetCallback(self, msg):
00213     ######################################################
00214         self.target = msg.data
00215         self.ticks_since_target = 0
00216         # rospy.logdebug("-D- %s targetCallback " % (self.nodename))
00217     
00218     
00219 if __name__ == '__main__':
00220     """ main """
00221     pidVelocity = PidVelocity()
00222     pidVelocity.spin()
00223    
00224     


differential_drive
Author(s): Jon Stephan
autogenerated on Sun Oct 5 2014 23:28:30