コード例 #1
0
class DriveController:
    wheel_radius = 0.1  # meters
    encoder_ticks_per_revolution = 256 * 3 * 1.8

    def __init__(self, kP, kI, kD, kF, tolerance, encoder_function,
                 update_function, direction, period, is_enabled):
        self.is_enabled = is_enabled
        self.direction = direction
        self.encoder_function = encoder_function
        self.motor_output = 0

        self.pid_controller = PIDController(kP,
                                            kI,
                                            kD,
                                            kF,
                                            source=self,
                                            output=self,
                                            period=period)
        #self.pid_controller.controlLoop._thread.setDaemon(True)
        self.pid_controller.setInputRange(-5, 5)

        self.pid_controller.setOutputRange(-1.0, 1.0)
        self.pid_controller.setAbsoluteTolerance(tolerance)
        self.update_function = update_function
        self.pid_controller.setContinuous(True)

    def start(self, setpoint):
        self.setpoint = setpoint
        self.pid_controller.setSetpoint(setpoint)
        self.pid_controller.enable()

    def pidWrite(self, output):
        print("output voltage", output)
        # print("distance for cotroller", self.pid_controller.getError())
        self.pid_controller.setSetpoint(self.setpoint)
        self.update_function(self.direction * output)

    def is_finished(self):
        return self.pid_controller.onTarget()

    def close(self):
        self.pid_controller.close()

    def pidGet(self):
        distance_in_encoder = self.encoder_function()
        distance_in_meters = (
            distance_in_encoder /
            self.encoder_ticks_per_revolution) * self.wheel_radius
        print("dist", distance_in_meters)
        return distance_in_meters

    def getPIDSourceType(self):
        return "meter"

    def getPIDOutputType(self):
        return "output"
コード例 #2
0
ファイル: turntoangle.py プロジェクト: ruwix/DeepSpace2019
class TurnToAngle(Command):
    def _setMotors(self, signal):
        signal = signal if abs(
            signal) > Constants.TURN_TO_ANGLE_MIN_OUTPUT else math.copysign(
                Constants.TURN_TO_ANGLE_MIN_OUTPUT, signal)
        Dash.putNumber("Turn To Angle Output", signal)
        self.drive.setPercentOutput(signal, -signal, signal, -signal)

    def __init__(self, setpoint):
        """Turn to setpoint (degrees)."""
        super().__init__()
        self.drive = drive.Drive()
        self.odemetry = odemetry.Odemetry()
        self.requires(self.drive)
        self.setpoint = setpoint
        src = self.odemetry.pidgyro
        self.PID = PIDController(Constants.TURN_TO_ANGLE_KP,
                                 Constants.TURN_TO_ANGLE_KI,
                                 Constants.TURN_TO_ANGLE_KD, src,
                                 self._setMotors)
        logging.debug(
            "Turn to angle constructed with angle {}".format(setpoint))
        self.PID.setInputRange(-180.0, 180.0)
        self.PID.setOutputRange(-Constants.TURN_TO_ANGLE_MAX_OUTPUT,
                                Constants.TURN_TO_ANGLE_MAX_OUTPUT)
        self.PID.setContinuous(True)
        self.PID.setAbsoluteTolerance(Constants.TURN_TO_ANGLE_TOLERANCE)
        self.PID.setPIDSourceType(PIDController.PIDSourceType.kDisplacement)

    def initialize(self):
        self.PID.setP(Constants.TURN_TO_ANGLE_KP)
        self.PID.setI(Constants.TURN_TO_ANGLE_KI)
        self.PID.setD(Constants.TURN_TO_ANGLE_KD)
        self.PID.setOutputRange(-Constants.TURN_TO_ANGLE_MAX_OUTPUT,
                                Constants.TURN_TO_ANGLE_MAX_OUTPUT)
        self.PID.setAbsoluteTolerance(Constants.TURN_TO_ANGLE_TOLERANCE)
        self.PID.setSetpoint(self.setpoint)
        self.PID.enable()

    def execute(self):
        Dash.putNumber("Turn To Angle Error", self.PID.getError())
        # logging.info(f"Turn To Angle Error {self.PID.getError()}")

    def isFinished(self):
        return self.PID.onTarget()

    def end(self):
        logging.debug("Finished turning to angle {}".format(self.setpoint))
        self.PID.disable()
        snaplistener.SnapListener(0).start()

    def interrupted(self):
        self.end()
コード例 #3
0
ファイル: chassis.py プロジェクト: james-ward/pystronghold
class Chassis:
    correct_range = 1.65 # m

    length = 498.0  # mm
    width = 600.0  # mm

    motor_dist = math.sqrt((width / 2) ** 2 + (length / 2) ** 2)  # distance of motors from the center of the robot

    #                    x component                   y component
    vz_components = {'x': (width / 2) / motor_dist, 'y': (length / 2) / motor_dist}  # multiply both by vz and the

    # the number that you need to multiply the vz components by to get them in the appropriate directions
    #                   vx   vy
    """module_params = {'a': {'args': {'drive':13, 'steer':14, 'absolute':True,
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':30,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x':-vz_components['x'], 'y': vz_components['y']}},
                     'b': {'args': {'drive':8, 'steer':9, 'absolute':True,
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':109,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x':-vz_components['x'], 'y':-vz_components['y']}},
                     'c': {'args': {'drive':2, 'steer':4, 'absolute':True,
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':536,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x': vz_components['x'], 'y':-vz_components['y']}},
                     'd': {'args': {'drive':3, 'steer':6, 'absolute':True,
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':389,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x': vz_components['x'], 'y': vz_components['y']}}
                     }"""
    module_params = {'a': {'args': {'drive':13, 'steer':11, 'absolute':False,                                     
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':30,      
                                    'drive_encoder':True, 'reverse_drive_encoder':True},                 
                           'vz': {'x':-vz_components['x'], 'y': vz_components['y']}},               
                     'b': {'args': {'drive':8, 'steer':9, 'absolute':False,                         
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':109,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x':-vz_components['x'], 'y':-vz_components['y']}},                
                     'c': {'args': {'drive':2, 'steer':4, 'absolute':False,                          
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':536,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x': vz_components['x'], 'y':-vz_components['y']}},                
                     'd': {'args': {'drive':3, 'steer':6, 'absolute':False,                          
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':389, 
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x': vz_components['x'], 'y': vz_components['y']}}                 
                     }
    # Use the magic here!
    bno055 = BNO055
    vision = Vision
    range_finder = RangeFinder
    heading_hold_pid_output = BlankPIDOutput
    heading_hold_pid = PIDController

    def __init__(self):
        super().__init__()

        #  A - D
        #  |   |
        #  B - C
        self._modules = {}
        for name, params in Chassis.module_params.items():
            self._modules[name] = SwerveModule(**(params['args']))
            self._modules[name]._drive.setVoltageRampRate(50.0)
        self.field_oriented = True
        self.inputs = [0.0, 0.0, 0.0, 0.0]
        self.vx = self.vy = self.vz = 0.0
        self.track_vision = False
        self.range_setpoint = None
        self.heading_hold = True
        self.lock_wheels = False
        self.momentum = False
        import robot
        self.rescale_js = robot.rescale_js

        self.distance_pid_heading = 0.0  # Relative to field
        self.distance_pid_output = BlankPIDOutput()
        # TODO tune the distance PID values
        self.distance_pid = PIDController(1.0, 0.02, 0.0,
                                          self, self.distance_pid_output)
        self.distance_pid.setAbsoluteTolerance(0.05)
        self.distance_pid.setToleranceBuffer(5)
        self.distance_pid.setContinuous(False)
        self.distance_pid.setInputRange(-10.0, 10.0)  # TODO check that this range is good for us
        self.distance_pid.setOutputRange(-0.4, 0.4)
        self.distance_pid.setSetpoint(0.0)
        self.reset_distance_pid = False
        self.pid_counter = 0

    def on_enable(self):
        self.bno055.resetHeading()
        self.heading_hold = True
        self.field_oriented = True
        self.heading_hold_pid.setSetpoint(self.bno055.getAngle())
        self.heading_hold_pid.reset()
        # Update the current module steer setpoint to be the current position
        # Stops the unwind problem
        for module in self._modules.values():
            module._steer.set(module._steer.getPosition())

    def onTarget(self):
        for module in self._modules.values():
            if not abs(module._steer.getError()) < 50:
                return False
        return True

    def toggle_field_oriented(self):
        self.field_oriented = not self.field_oriented

    def toggle_vision_tracking(self):
        self.track_vision = not self.track_vision
        if self.track_vision:
            self.zero_encoders()
            self.distance_pid.setSetpoint(0.0)
            self.distance_pid.enable()

    def toggle_range_holding(self, setpoint=1.65):
        if not self.range_setpoint:
            self.range_setpoint = setpoint
            self.zero_encoders()
            self.distance_pid.setSetpoint(0.0)
            self.distance_pid.enable()
        else:
            self.range_setpoint = 0.0

    def zero_encoders(self):
        for module in self._modules.values():
            module.zero_distance()

    def field_displace(self, x, y):
        '''Use the distance PID to displace the robot by x,y
        in field reference frame.'''
        d = math.sqrt((x ** 2 + y ** 2))
        fx, fy = field_orient(x, y, self.bno055.getHeading())
        self.distance_pid_heading = math.atan2(fy, fx)
        self.distance_pid.disable()
        self.zero_encoders()
        self.distance_pid.setSetpoint(d)
        self.distance_pid.reset()
        self.distance_pid.enable()

    def pidGet(self):
        return self.distance

    def getPIDSourceType(self):
        return PIDSource.PIDSourceType.kDisplacement

    @property
    def distance(self):
        distances = 0.0
        for module in self._modules.values():
            distances += abs(module.distance) / module.drive_counts_per_metre
        return distances / 4.0

    def drive(self, vX, vY, vZ, absolute=False):
        motor_vectors = {}
        for name, params in Chassis.module_params.items():
            motor_vectors[name] = {'x': vX + vZ * params['vz']['x'],
                                   'y': vY + vZ * params['vz']['y']
                                   }
        # convert the vectors to polar coordinates
        polar_vectors = {}
        max_mag = 1.0
        for name, motor_vector in motor_vectors.items():
            polar_vectors[name] = {'dir': math.atan2(motor_vector['y'],
                                                     motor_vector['x']
                                                     ),
                                   'mag': math.sqrt(motor_vector['x'] ** 2
                                                    + motor_vector['y'] ** 2
                                                    )
                                   }
            if abs(polar_vectors[name]['mag']) > max_mag:
                max_mag = polar_vectors[name]['mag']

        for name in polar_vectors.keys():
            polar_vectors[name]['mag'] /= max_mag
            if absolute:
                polar_vectors[name]['mag'] = None
                continue

        for name, polar_vector in polar_vectors.items():
            self._modules[name].steer(polar_vector['dir'], polar_vector['mag'])

    def execute(self):
        if self.field_oriented and self.inputs[3] is not None:
            self.inputs[0:2] = field_orient(self.inputs[0], self.inputs[1], self.bno055.getHeading())

        # Are we in setpoint displacement mode?
        if self.distance_pid.isEnable():
            if self.distance_pid.onTarget():
                if self.pid_counter > 10:
                    self.reset_distance_pid = False
                    # Let's see if we need to move further
                    x = y = 0.0
                    if self.range_setpoint:
                        x = self.range_finder.pidGet() - self.range_setpoint
                        if x > 0.5:
                            x = 0.5
                        elif x < -0.5:
                            x = -0.5
                    if self.track_vision:
                        y = self.vision.pidGet()*0.3#*self.range_finder.pidGet()*0.3  # TODO we need proper scaling factors here
                        if y > 0.3:
                            y = 0.3
                        elif y < -0.3:
                            y = -0.3
                    self.distance_pid.disable()
                    self.zero_encoders()
                    self.distance_pid_heading = constrain_angle(math.atan2(y, x)+self.bno055.getAngle())
                    self.distance_pid.setSetpoint(math.sqrt(x**2+y**2))
                    self.distance_pid.reset()
                    self.distance_pid.enable()
                    self.pid_counter = 0
                else:
                    self.pid_counter += 1

            # Keep driving
            self.vx = math.cos(self.distance_pid_heading) * self.distance_pid_output.output
            self.vy = math.sin(self.distance_pid_heading) * self.distance_pid_output.output
        else:
            self.vx = self.inputs[0] * self.inputs[3]  # multiply by throttle
            self.vy = self.inputs[1] * self.inputs[3]  # multiply by throttle

        if self.heading_hold:
            if self.momentum and abs(self.bno055.getHeadingRate()) < 0.005:
                self.momentum = False

            if self.inputs[2] != 0.0:
                self.momentum = True

            if not self.momentum:
                self.heading_hold_pid.enable()
                self.vz = self.heading_hold_pid_output.output
            else:
                self.heading_hold_pid.setSetpoint(self.bno055.getAngle())
                self.vz = self.inputs[2] * self.inputs[3]  # multiply by throttle

        if self.lock_wheels:
            for _, params, module in zip(Chassis.module_params.items(),
                                         self._modules):
                direction = constrain_angle(math.atan2(params['vz']['y'],
                                                       params['vz']['x']) +
                                            math.pi / 2.0)
                module.steer(direction, 0.0)
        else:
            self.drive(self.vx, self.vy, self.vz)

    def toggle_heading_hold(self):
        self.heading_hold = not self.heading_hold

    def set_heading_setpoint(self, setpoint):
        self.heading_hold_pid.setSetpoint(constrain_angle(setpoint))

    def on_range_target(self):
        return abs(self.range_finder.getDistance() - self.range_setpoint) < 0.1

    def on_vision_target(self):
        return (self.vision.no_vision_counter == 0.0 and
                abs(self.vision.pidGet()) < 0.035)
コード例 #4
0
ファイル: chassis.py プロジェクト: thedropbears/pystronghold
class Chassis:
    correct_range = 1.65 # m

    length = 498.0  # mm
    width = 600.0  # mm

    vision_scale_factor = 0.3  # units of m/(vision unit)
    distance_pid_abs_error = 0.05  # metres

    motor_dist = math.sqrt((width / 2) ** 2 + (length / 2) ** 2)  # distance of motors from the center of the robot

    #                    x component                   y component
    vz_components = {'x': (width / 2) / motor_dist, 'y': (length / 2) / motor_dist}  # multiply both by vz and the

    # the number that you need to multiply the vz components by to get them in the appropriate directions
    #                   vx   vy
    """module_params = {'a': {'args': {'drive':13, 'steer':14, 'absolute':True,
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':30,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x':-vz_components['x'], 'y': vz_components['y']}},
                     'b': {'args': {'drive':8, 'steer':9, 'absolute':True,
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':109,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x':-vz_components['x'], 'y':-vz_components['y']}},
                     'c': {'args': {'drive':2, 'steer':4, 'absolute':True,
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':536,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x': vz_components['x'], 'y':-vz_components['y']}},
                     'd': {'args': {'drive':3, 'steer':6, 'absolute':True,
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':389,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},
                           'vz': {'x': vz_components['x'], 'y': vz_components['y']}}
                     }"""
    module_params = {'a': {'args': {'drive':13, 'steer':11, 'absolute':False,                                     
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':30,      
                                    'drive_encoder':True, 'reverse_drive_encoder':True},                 
                           'vz': {'x':-vz_components['x'], 'y': vz_components['y']}},               
                     'b': {'args': {'drive':8, 'steer':9, 'absolute':False,                         
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':109,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x':-vz_components['x'], 'y':-vz_components['y']}},                
                     'c': {'args': {'drive':2, 'steer':4, 'absolute':False,                          
                                    'reverse_drive':False, 'reverse_steer':True, 'zero_reading':536,
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x': vz_components['x'], 'y':-vz_components['y']}},                
                     'd': {'args': {'drive':3, 'steer':6, 'absolute':False,                          
                                    'reverse_drive':True, 'reverse_steer':True, 'zero_reading':389, 
                                    'drive_encoder':True, 'reverse_drive_encoder':True},             
                           'vz': {'x': vz_components['x'], 'y': vz_components['y']}}                 
                     }
    # Use the magic here!
    bno055 = BNO055
    vision = Vision
    range_finder = RangeFinder
    heading_hold_pid_output = BlankPIDOutput
    heading_hold_pid = PIDController

    def __init__(self):
        super().__init__()

        #  A - D
        #  |   |
        #  B - C
        self._modules = {}
        for name, params in Chassis.module_params.items():
            self._modules[name] = SwerveModule(**(params['args']))
            self._modules[name]._drive.setVoltageRampRate(50.0)
        self.field_oriented = True
        self.inputs = [0.0, 0.0, 0.0, 0.0]
        self.vx = self.vy = self.vz = 0.0
        self.track_vision = False
        self.range_setpoint = None
        self.heading_hold = True
        self.lock_wheels = False
        self.momentum = False
        import robot
        self.rescale_js = robot.rescale_js

        self.distance_pid_heading = 0.0  # Relative to field
        self.distance_pid_output = BlankPIDOutput()
        # TODO tune the distance PID values
        self.distance_pid = PIDController(0.75, 0.02, 1.0,
                                          self, self.distance_pid_output)
        self.distance_pid.setAbsoluteTolerance(self.distance_pid_abs_error)
        self.distance_pid.setToleranceBuffer(3)
        self.distance_pid.setContinuous(False)
        self.distance_pid.setInputRange(-5.0, 5.0)
        self.distance_pid.setOutputRange(-0.4, 0.4)
        self.distance_pid.setSetpoint(0.0)
        self.reset_distance_pid = False
        self.pid_counter = 0
        self.logger = logging.getLogger("chassis")

    def on_enable(self):
        self.bno055.resetHeading()
        self.heading_hold = True
        self.field_oriented = True
        self.heading_hold_pid.setSetpoint(self.bno055.getAngle())
        self.heading_hold_pid.reset()
        # Update the current module steer setpoint to be the current position
        # Stops the unwind problem
        for module in self._modules.values():
            module._steer.set(module._steer.getPosition())

    def onTarget(self):
        for module in self._modules.values():
            if not abs(module._steer.getError()) < 50:
                return False
        return True

    def toggle_field_oriented(self):
        self.field_oriented = not self.field_oriented

    def toggle_vision_tracking(self):
        self.track_vision = not self.track_vision
        self.logger.info("Vision Tracking: " + str(self.track_vision))
        if self.track_vision:
            self.zero_encoders()
            self.distance_pid.setSetpoint(0.0)
            self.distance_pid.enable()

    def toggle_range_holding(self, setpoint=1.65):
        if not self.range_setpoint:
            self.range_setpoint = setpoint
            self.zero_encoders()
            self.distance_pid.setSetpoint(0.0)
            self.distance_pid.enable()
        else:
            self.range_setpoint = 0.0

    def zero_encoders(self):
        for module in self._modules.values():
            module.zero_distance()

    def field_displace(self, x, y):
        '''Use the distance PID to displace the robot by x,y
        in field reference frame.'''
        d = math.sqrt((x ** 2 + y ** 2))
        fx, fy = field_orient(x, y, self.bno055.getHeading())
        self.distance_pid_heading = math.atan2(fy, fx)
        self.distance_pid.disable()
        self.zero_encoders()
        self.distance_pid.setSetpoint(d)
        self.distance_pid.reset()
        self.distance_pid.enable()

    def pidGet(self):
        return self.distance

    def getPIDSourceType(self):
        return PIDSource.PIDSourceType.kDisplacement

    @property
    def distance(self):
        distances = 0.0
        for module in self._modules.values():
            distances += abs(module.distance) / module.drive_counts_per_metre
        return distances / 4.0

    def drive(self, vX, vY, vZ, absolute=False):
        motor_vectors = {}
        for name, params in Chassis.module_params.items():
            motor_vectors[name] = {'x': vX + vZ * params['vz']['x'],
                                   'y': vY + vZ * params['vz']['y']
                                   }
        # convert the vectors to polar coordinates
        polar_vectors = {}
        max_mag = 1.0
        for name, motor_vector in motor_vectors.items():
            polar_vectors[name] = {'dir': math.atan2(motor_vector['y'],
                                                     motor_vector['x']
                                                     ),
                                   'mag': math.sqrt(motor_vector['x'] ** 2
                                                    + motor_vector['y'] ** 2
                                                    )
                                   }
            if abs(polar_vectors[name]['mag']) > max_mag:
                max_mag = polar_vectors[name]['mag']

        for name in polar_vectors.keys():
            polar_vectors[name]['mag'] /= max_mag
            if absolute:
                polar_vectors[name]['mag'] = None
                continue

        for name, polar_vector in polar_vectors.items():
            self._modules[name].steer(polar_vector['dir'], polar_vector['mag'])

    def execute(self):
        if self.field_oriented and self.inputs[3] is not None:
            self.inputs[0:2] = field_orient(self.inputs[0], self.inputs[1], self.bno055.getHeading())

        # Are we in setpoint displacement mode?
        if self.distance_pid.isEnable():
            if self.distance_pid.onTarget():
                if self.pid_counter > 10:
                    self.reset_distance_pid = False
                    # Let's see if we need to move further
                    x = y = 0.0
                    if self.range_setpoint and not self.on_range_target():
                        x = self.range_finder.pidGet() - self.range_setpoint
                        if x > 0.5:
                            x = 0.5
                        elif x < -0.5:
                            x = -0.5
                        self.logger.info("X: " + str(x))
                    if self.track_vision and not self.on_vision_target():
                        self.logger.info("Tracking Vision")
                        y = self.vision.pidGet() * self.vision_scale_factor
                        if y > 0.5:
                            y = 0.5
                        elif y < -0.5:
                            y = -0.5
                        self.logger.info("Y: " + str(y))
                    elif self.on_vision_target():
                        self.track_vision = False
                    self.distance_pid.disable()
                    self.zero_encoders()
                    self.distance_pid_heading = constrain_angle(math.atan2(y, x)+self.bno055.getAngle())
                    self.distance_pid.setSetpoint(math.sqrt(x**2+y**2))
                    self.distance_pid.reset()
                    self.distance_pid.enable()
                    self.pid_counter = 0
                else:
                    self.pid_counter += 1

            # Keep driving
            self.vx = math.cos(self.distance_pid_heading) * self.distance_pid_output.output
            self.vy = math.sin(self.distance_pid_heading) * self.distance_pid_output.output
        else:
            self.vx = self.inputs[0] * self.inputs[3]  # multiply by throttle
            self.vy = self.inputs[1] * self.inputs[3]  # multiply by throttle

        if self.heading_hold:
            if self.momentum and abs(self.bno055.getHeadingRate()) < 0.005:
                self.momentum = False

            if self.inputs[2] != 0.0:
                self.momentum = True

            if not self.momentum:
                self.heading_hold_pid.enable()
                self.vz = self.heading_hold_pid_output.output
            else:
                self.heading_hold_pid.setSetpoint(self.bno055.getAngle())
                self.vz = self.inputs[2] * self.inputs[3]  # multiply by throttle

        self.logger.info("Vision: %s Rangefinder: %s Distance: %s Setpoint: %s" % (self.vision.pidGet(), self.range_finder.pidGet(), self.distance, self.distance_pid.getSetpoint()))

        if self.lock_wheels:
            for _, params, module in zip(Chassis.module_params.items(),
                                         self._modules):
                direction = constrain_angle(math.atan2(params['vz']['y'],
                                                       params['vz']['x']) +
                                            math.pi / 2.0)
                module.steer(direction, 0.0)
        else:
            self.drive(self.vx, self.vy, self.vz)

    def toggle_heading_hold(self):
        self.heading_hold = not self.heading_hold

    def set_heading_setpoint(self, setpoint):
        self.heading_hold_pid.setSetpoint(constrain_angle(setpoint))

    def on_range_target(self):
        return abs(self.range_finder.pidGet() - self.range_setpoint) < self.distance_pid_abs_error * 2.0

    def on_vision_target(self):
        return (self.vision.no_vision_counter == 0.0 and
                abs(self.vision.pidGet() * self.vision_scale_factor) < self.distance_pid_abs_error * 2.0)
コード例 #5
0
    def __init__(self, robot):
        super().__init__('Drive')

        SmartDashboard.putNumber("DriveStraight_P", 0.075)
        SmartDashboard.putNumber("DriveStraight_I", 0.0)
        SmartDashboard.putNumber("DriveStraight_D", 0.42)
        # OLD GAINS 0.075, 0, 0.42

        SmartDashboard.putNumber("DriveAngle_P", 0.009)
        SmartDashboard.putNumber("DriveAngle_I", 0.0)
        SmartDashboard.putNumber("DriveAngle_D", 0.025)

        SmartDashboard.putNumber("DriveStraightAngle_P", 0.025)
        SmartDashboard.putNumber("DriveStraightAngle_I", 0.0)
        SmartDashboard.putNumber("DriveStraightAngle_D", 0.01)

        self.driveStyle = "Tank"
        SmartDashboard.putString("DriveStyle", self.driveStyle)
        #SmartDashboard.putData("Mode", self.mode)

        self.robot = robot
        self.lime = self.robot.limelight
        self.nominalPID = 0.15
        self.nominalPIDAngle = 0.22  # 0.11 - v2

        self.preferences = Preferences.getInstance()
        timeout = 0

        TalonLeft = Talon(map.driveLeft1)
        TalonRight = Talon(map.driveRight1)

        leftInverted = True
        rightInverted = False

        TalonLeft.setInverted(leftInverted)
        TalonRight.setInverted(rightInverted)

        VictorLeft1 = Victor(map.driveLeft2)
        VictorLeft2 = Victor(map.driveLeft3)
        VictorLeft1.follow(TalonLeft)
        VictorLeft2.follow(TalonLeft)

        VictorRight1 = Victor(map.driveRight2)
        VictorRight2 = Victor(map.driveRight3)
        VictorRight1.follow(TalonRight)
        VictorRight2.follow(TalonRight)

        for motor in [VictorLeft1, VictorLeft2]:
            motor.clearStickyFaults(timeout)
            motor.setSafetyEnabled(False)
            #motor.setExpiration(2 * self.robot.period)
            motor.setInverted(leftInverted)

        for motor in [VictorRight1, VictorRight2]:
            motor.clearStickyFaults(timeout)
            motor.setSafetyEnabled(False)
            #motor.setExpiration(2 * self.robot.period)
            motor.setInverted(rightInverted)

        for motor in [TalonLeft, TalonRight]:
            motor.setSafetyEnabled(False)
            #motor.setExpiration(2 * self.robot.period)
            motor.clearStickyFaults(timeout)  #Clears sticky faults

            motor.configContinuousCurrentLimit(40, timeout)  #15 Amps per motor
            motor.configPeakCurrentLimit(
                70, timeout)  #20 Amps during Peak Duration
            motor.configPeakCurrentDuration(
                300, timeout)  #Peak Current for max 100 ms
            motor.enableCurrentLimit(True)

            motor.configVoltageCompSaturation(12,
                                              timeout)  #Sets saturation value
            motor.enableVoltageCompensation(
                True)  #Compensates for lower voltages

            motor.configOpenLoopRamp(0.2,
                                     timeout)  #number of seconds from 0 to 1

        self.left = TalonLeft
        self.right = TalonRight

        self.navx = navx.AHRS.create_spi()

        self.leftEncoder = Encoder(map.leftEncoder[0], map.leftEncoder[1])
        self.leftEncoder.setDistancePerPulse(self.leftConv)
        self.leftEncoder.setSamplesToAverage(10)

        self.rightEncoder = Encoder(map.rightEncoder[0], map.rightEncoder[1])
        self.rightEncoder.setDistancePerPulse(self.rightConv)
        self.rightEncoder.setSamplesToAverage(10)

        self.zero()

        #PID for Drive
        self.TolDist = 0.1  #feet
        [kP, kI, kD, kF] = [0.027, 0.00, 0.20, 0.00]
        if wpilib.RobotBase.isSimulation():
            [kP, kI, kD, kF] = [0.25, 0.00, 1.00, 0.00]
        distController = PIDController(kP,
                                       kI,
                                       kD,
                                       kF,
                                       source=self.__getDistance__,
                                       output=self.__setDistance__)
        distController.setInputRange(0, 50)  #feet
        distController.setOutputRange(-0.6, 0.6)
        distController.setAbsoluteTolerance(self.TolDist)
        distController.setContinuous(False)
        self.distController = distController
        self.distController.disable()
        '''PID for Angle'''
        self.TolAngle = 2  #degrees
        [kP, kI, kD, kF] = [0.025, 0.00, 0.01, 0.00]
        if RobotBase.isSimulation():
            [kP, kI, kD, kF] = [0.005, 0.0, 0.01, 0.00]
        angleController = PIDController(kP,
                                        kI,
                                        kD,
                                        kF,
                                        source=self.__getAngle__,
                                        output=self.__setAngle__)
        angleController.setInputRange(-180, 180)  #degrees
        angleController.setOutputRange(-0.5, 0.5)
        angleController.setAbsoluteTolerance(self.TolAngle)
        angleController.setContinuous(True)
        self.angleController = angleController
        self.angleController.disable()

        self.k = 1
        self.sensitivity = 1

        SmartDashboard.putNumber("K Value", self.k)
        SmartDashboard.putNumber("sensitivity", self.sensitivity)

        self.prevLeft = 0
        self.prevRight = 0
コード例 #6
0
class DriveController(StateMachine):
    chassis: Chassis
    wheel_diameter = 0.1  # wheel diameter in meters
    encoder_ticks_per_revolution = 256 * 1.8 * 3 * 5  # encoder ticks per rev

    distance_setpoint = 0  # Distance setpoint
    angle_setpoint = 0  # Angle setpoint
    left_speed = 0  # Current left speed
    right_speed = 0  # Current right speed
    turn_speed = 0  # Current turn speed

    loop_counter = 0  # A counter that represents the number of times the drive function has been called.
    finished = False  # Is finished?
    enabled = False  # Is enable?

    def setup(self):
        self.motor_updater = Notifier(
            self.update_motors)  # A motor updater thread
        self.setup_values()
        self.start()

    def setup_values(self,
                     input_range=15,
                     output_range=1,
                     tolerance=0.05,
                     period=0.02,
                     kp=0.08,
                     ki=0,
                     kd=0,
                     kf=0):
        """ Sets up all the different values that will be used during the course of the PID control loop. """
        self.input_range = input_range
        self.output_range = output_range
        self.tolerance = tolerance
        self.period = period
        self.kp = kp
        self.ki = ki
        self.kd = kd
        self.kf = kf

    def start(self):
        # Setting up the left controller
        self.left_holder = IOEncoderHolder(self.encoder_ticks_per_revolution,
                                           self.wheel_diameter,
                                           self.chassis.get_left_encoder,
                                           self.left_output)

        self.left_pid_contorller = PIDController(self.kp,
                                                 self.ki,
                                                 self.kd,
                                                 self.kf,
                                                 source=self.left_holder,
                                                 output=self.left_holder,
                                                 period=self.period)

        self.left_pid_contorller.setInputRange(-self.input_range,
                                               self.input_range)
        self.left_pid_contorller.setOutputRange(-self.output_range,
                                                self.output_range)
        self.left_pid_contorller.setAbsoluteTolerance(self.tolerance)
        self.left_pid_contorller.setContinuous(True)

        # Setting up the right controller
        self.right_holder = IOEncoderHolder(self.encoder_ticks_per_revolution,
                                            self.wheel_diameter,
                                            self.chassis.get_right_encoder,
                                            self.right_output)

        self.right_pid_contorller = PIDController(self.kp,
                                                  self.ki,
                                                  self.kd,
                                                  self.kf,
                                                  source=self.right_holder,
                                                  output=self.right_holder,
                                                  period=self.period)
        self.right_pid_contorller.setInputRange(-self.input_range,
                                                self.input_range)
        self.right_pid_contorller.setOutputRange(-self.output_range,
                                                 self.output_range)
        self.right_pid_contorller.setAbsoluteTolerance(self.tolerance)
        self.right_pid_contorller.setContinuous(True)

        # Setting up the turn controller
        self.turn_holder = IOGyroHolder(self.chassis.get_angle,
                                        self.turn_output)
        self.turn_pid_contorller = PIDController(0.02,
                                                 self.ki,
                                                 self.kd,
                                                 self.kf,
                                                 source=self.turn_holder,
                                                 output=self.turn_holder,
                                                 period=self.period)
        self.turn_pid_contorller.setInputRange(0, 360)
        self.turn_pid_contorller.setOutputRange(-self.output_range,
                                                self.output_range)
        self.turn_pid_contorller.setAbsoluteTolerance(10)
        self.turn_pid_contorller.setContinuous(True)

    # put different pidf values for angle control

    def enable(self, distance: float, angle: float):
        self.distance_setpoint = distance
        self.angle_setpoint = angle

        # Setting the setpoints
        self.left_pid_contorller.setSetpoint(distance)
        self.right_pid_contorller.setSetpoint(distance)
        self.turn_pid_contorller.setSetpoint(angle)

        # Enabling the PID Controllers
        self.left_pid_contorller.enable()
        self.right_pid_contorller.enable()
        self.turn_pid_contorller.enable()
        self.motor_updater.startPeriodic(self.period)
        self.enabled = True

    def left_output(self, speed):
        self.left_speed = speed

    def right_output(self, speed):
        self.right_speed = speed

    def turn_output(self, linear_speed):
        self.turn_speed = linear_speed

    def update_motors(self):
        # print("distance for cotroller", self.left_pid_contorller.getError(), self.right_pid_contorller.getError(),
        #     self.turn_pid_contorller.getError())
        self.left_pid_contorller.setSetpoint(self.distance_setpoint)
        self.right_pid_contorller.setSetpoint(self.distance_setpoint)
        self.turn_pid_contorller.setSetpoint(self.angle_setpoint)
        self.chassis.set_motors_values(self.left_speed - self.turn_speed,
                                       self.right_speed + self.turn_speed)

    def set_input_range(self, range):
        self.input_range = range

    def set_tolerance(self, tolerance):
        self.tolerance = tolerance

    def set_kp(self, kp):
        self.kp = kp

    def set_ki(self, ki):
        self.ki = ki

    def set_kd(self, kd):
        self.kd = kd

    def set_kf(self, kf):
        self.kf = kf

    def set_period(self, period):
        self.period = period

    def restart(self):
        self.chassis.reset_encoders()
        self.chassis.reset_navx()
        self.left_speed = 0
        self.right_speed = 0
        self.turn_speed = 0

    def is_finished(self):
        return self.left_pid_contorller.onTarget(
        ) and self.right_pid_contorller.onTarget(
        ) and self.turn_pid_contorller.onTarget()

    def close(self):
        self.left_speed = 0
        self.right_speed = 0
        self.turn_speed = 0
        self.motor_updater.close()
        self.left_pid_contorller.close()
        self.right_pid_contorller.close()
        self.turn_pid_contorller.close()

    @state(first=True)
    def drive(self, initial_call):
        print(self.loop_counter)
        self.loop_counter += 1
        if initial_call:
            self.restart()
            self.enable(1, 90)
        elif self.is_finished():
            self.close()
            self.finished = True
            self.enabled = False
            self.done()