def InitMPU6050(self): # Accelerometer Values and Device self._mpuIdx = ['x', 'y', 'z'] self._a = [0.0, 0.0, 0.0] # Acceleration (g's) self._w = [0.0, 0.0, 0.0] # Angular rate (deg/s) self._wFilt = [0.0, 0.0, 0.0] # Filtered Angular rate (deg/s) self._theta = [0.0, 0.0, 0.0] # Angle (deg) self._thetaFilt = [0.0, 0.0, 0.0] # Filtered Angle (deg) self._a_off = [0.66, -0.26, 10.22 - 9.81] self._w_off = [-1.07, 0.67, -1.55] #self._a_off = [0.0, 0.0, 0.0] #self._w_off = [0.0, 0.0, 0.0] self._thetaXFilt = ComplementaryFilter(alpha=0.05) # alpha = 0.02 self._thetaXFilt2 = Filter(timeConst=0.0) self._wXFilt = Filter(timeConst=0.0) self._mpu6050 = mpu6050(0x68) # 0x68 - default I2C slave addr self._mpu6050.set_accel_range(mpu6050.ACCEL_RANGE_2G) self._mpu6050.set_gyro_range(mpu6050.GYRO_RANGE_250DEG) return
class RobotThread(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): super(RobotThread, self).__init__(group=group, target=target, name=name) self.args = args self.kwargs = kwargs self._continueFlag = True # flag to continue running the thread self._resetFlag = False # flag to reset everything self._runControl = False # process control # Initialize the pigpio library self._pi = pigpio.pi() # Initialize all hardware self.InitMPU6050() self.InitMotors() self.InitControl() # Store current time self._currTimePrev = time.time() # Sleep time for control loop self._sleepTime = 0.1 return @property def CtrlErr(self): return self._ctrlErr @property def CtrlErrRate(self): return self._ctrlErrRate @property def CtrlOutput(self): return self._ctrlOutput @property def Kp(self): return self._P @Kp.setter def Kp(self, val): self._P = val self._pid.setKp(val) @property def Ki(self): return self._I @Ki.setter def Ki(self, val): self._I = val self._pid.setKi(val) @property def Kd(self): return self._D @Kd.setter def Kd(self, val): self._D = val self._pid.setKd(val) def InitMPU6050(self): # Accelerometer Values and Device self._mpuIdx = ['x', 'y', 'z'] self._a = [0.0, 0.0, 0.0] # Acceleration (g's) self._w = [0.0, 0.0, 0.0] # Angular rate (deg/s) self._wFilt = [0.0, 0.0, 0.0] # Filtered Angular rate (deg/s) self._theta = [0.0, 0.0, 0.0] # Angle (deg) self._thetaFilt = [0.0, 0.0, 0.0] # Filtered Angle (deg) self._a_off = [0.66, -0.26, 10.22 - 9.81] self._w_off = [-1.07, 0.67, -1.55] #self._a_off = [0.0, 0.0, 0.0] #self._w_off = [0.0, 0.0, 0.0] self._thetaXFilt = ComplementaryFilter(alpha=0.05) # alpha = 0.02 self._thetaXFilt2 = Filter(timeConst=0.0) self._wXFilt = Filter(timeConst=0.0) self._mpu6050 = mpu6050(0x68) # 0x68 - default I2C slave addr self._mpu6050.set_accel_range(mpu6050.ACCEL_RANGE_2G) self._mpu6050.set_gyro_range(mpu6050.GYRO_RANGE_250DEG) return def InitMotors(self): # Motor Controllers # Pins - PWM, FWD, REV #self._motorLookupV = [0, 12] #self._motorLookupPWM = [0, 100] self._motorLookupV = [0.0, 1.0, 1.1, 10.4, 11.0, 11.5, 11.8, 12.0] self._motorLookupPWM = [0.0, 1.0, 2.0, 80.0, 85.0, 90.0, 95.0, 100.0] self._motorLookup = LookupTable(\ self._motorLookupV, self._motorLookupPWM) self._motorLeft = MotorControl_pigpio(\ self._pi, 13, 19, 26) self._motorRight = MotorControl_pigpio(\ self._pi, 16, 20, 21) self._motorLeft.SetVoltageLookupTable(\ self._motorLookup) self._motorRight.SetVoltageLookupTable(\ self._motorLookup) # Motor Encoders # TODO return def InitControl(self): self._P = 0.8 self._I = 14.0 self._D = 0.12 self._maxIntegralOutput = 9.0 self._pid = PID(self._P, self._I, self._D) self._pid.setWindup(100.0) self._ctrlOutputMin = 1.0 # Volts, min motor output self._ctrlOutputMax = 12.0 # Volts, max motor output return def ProcessMPU6050(self): # Get the raw data self._mpu6050AccelData = self._mpu6050.get_accel_data() self._mpu6050GyroData = self._mpu6050.get_gyro_data() #self._mpu6050AccelData = {'x': 0.0, 'y': 0.0, 'z': 9.81} #self._mpu6050GyroData = {'x': 0.0, 'y': 0.0, 'z': 0.0} # Subtract out calibration offsets for i in range(0, 3): # (0, 1, 2) self._a[i] = self._mpu6050AccelData[self._mpuIdx[i]] - \ self._a_off[i] self._w[i] = self._mpu6050GyroData[self._mpuIdx[i]] - \ self._w_off[i] # Calculate angle from accelerometer data self._theta[0] = math.degrees( \ math.atan2(self._a[1], self._a[2])) # atan2(ay, az) # Complementary filter on accel and gyro data thetaFilt = self._thetaXFilt.Filter(self._dT, self._w[0], self._theta[0]) # Filter the resulting angle self._thetaFilt[0] = self._thetaXFilt2.Filter(self._dT, thetaFilt) # Filter the angular velocity for controller deriviative term self._wFilt[0] = self._wXFilt.Filter(self._dT, self._w[0]) # If the robot has tipped over, stop trying to control if math.fabs(self._thetaFilt[0]) > 30.0: self._runControl = False return def ProcessControl(self): # Calculate the control error and rate self._ctrlErr = self._thetaFilt[0] self._ctrlErrRate = -self._wFilt[0] # Adjust the max integral windup if self._pid.Ki > 0.0: self._pid.setWindup(self._maxIntegralOutput / self._pid.Ki) # Run the PID controller self._ctrlOutput = self._pid.update(self._ctrlErr, self._ctrlErrRate) # Control saturation if self._ctrlOutput > self._ctrlOutputMax: self._ctrlOutput = self._ctrlOutputMax if self._ctrlOutput < -self._ctrlOutputMax: self._ctrlOutput = -self._ctrlOutputMax # Clear integrator if not running if not self._runControl: self._pid.ITerm = 0.0 return def ProcessMotors(self): # If not running control, set both motors to brake if not self._runControl: self._motorLeft.Brake() self._motorRight.Brake() return # Set directions for left and right motors self._ctrlLeft = self._ctrlOutput self._ctrlRight = -self._ctrlOutput # Write the motor control signal self._motorLeft.SetVoltage(self._ctrlLeft) self._motorRight.SetVoltage(self._ctrlRight) # Process feedback from encoders # TODO return def run(self): while self._continueFlag: #print self._runControl # Calculate time delta self._currTime = time.time() self._dT = self._currTime - self._currTimePrev self._currTimePrev = self._currTime # Read accelerometer and gyro data and process self.ProcessMPU6050() # Run PID Controller self.ProcessControl() # Run motor output self.ProcessMotors() #logging.debug('running with %s and %s', self.args, self.kwargs) #time.sleep(self._sleepTime) # Stop condition self._pi.stop() return def StartControl(self): self._runControl = True def StopControl(self): self._runControl = False def Stop(self): self._continueFlag = False def Reset(self): self._resetFlag = False def ProcessCommands(self, cmd, conn): #print cmd next = 0 if cmd[0] == "CMD": if cmd[1] == "START": t.StartControl() next = 2 if cmd[1] == "STOP": t.StopControl() next = 2 if cmd[0] == "GET": if cmd[1] == "GAINS": resp = "RESP GAINS {0} {1} {2} ".format( self.Kp, self.Ki, self.Kd) conn.send(resp) next = 2 if cmd[1] == "PARAMS": resp = "RESP PARAMS {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} ".format(\ self.CtrlErr, self.CtrlErrRate, self.CtrlOutput, \ self._pid.PTerm, self._pid.ITerm * self._pid.Ki, self._pid.DTerm * self._pid.Kd) conn.send(resp) next = 2 if cmd[0] == "SET": if cmd[1] == "GAINS": t.Kp = float(cmd[2]) t.Ki = float(cmd[3]) t.Kd = float(cmd[4]) resp = "RESP GAINS {0} {1} {2} ".format( self.Kp, self.Ki, self.Kd) conn.send(resp) next = 5 if (next < len(cmd)) and (next > 0): self.ProcessCommands(cmd[next:], conn)
#9/18/2013 #Initialize IMU object to be used from this module from imu import IMU from robovero.extras import roboveroConfig import time from ComplementaryFilter import ComplementaryFilter from motor import Motor from PID import PIDControl roboveroConfig() # Initialize IMU imu = IMU() #imu.calibrate(0, 0, -1) cfRoll = ComplementaryFilter(0.9, 0) cfPitch = ComplementaryFilter(0.9, 0) motor1 = Motor(1) motor2 = Motor(6) motor3 = Motor(5) motor4 = Motor(4) motors = [motor1, motor2, motor3, motor4] #pidPitch = PIDControl(0, 1) #pidRoll = PIDControl(0, 1) #registers is list of three tuples for registers [(xlow,xhigh),(ylow,yhigh), etc] string = "" while(1):