Go to the documentation of this file.00001
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
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
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
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
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
00126 cur_vel = (1 / self.ticks_per_meter) / self.dt
00127 if abs(cur_vel) < self.vel_threshold:
00128
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
00137 self.appendVel(cur_vel)
00138 self.calcRollingVel()
00139
00140 else:
00141
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
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
00210
00211
00212 def targetCallback(self, msg):
00213
00214 self.target = msg.data
00215 self.ticks_since_target = 0
00216
00217
00218
00219 if __name__ == '__main__':
00220 """ main """
00221 pidVelocity = PidVelocity()
00222 pidVelocity.spin()
00223
00224