Esempio n. 1
0
class MotorInterface:
    #Instanciates the object
    def __init__(self):
        self.motor = Motor()
        self.motor.enable()

    '''
    Goes forward x seconds
    '''

    def forward(self, tsec):
        self.motor.forward()
        time.sleep(tsec)
        self.motor.stop()

    '''
    Goes left x seconds
    '''

    def left(self, tsec):
        self.motor.left()
        time.sleep(tsec)
        self.motor.stop()

    '''
    Goes right x seconds
    '''

    def right(self, tsec):
        self.motor.right()
        time.sleep(tsec)
        self.motor.stop()

    '''
    Goes back x seconds
    '''

    def back(self, tsec):
        self.motor.backward()
        time.sleep(tsec)
        self.motor.stop()

    '''
    Stops and disables the motor
    '''

    def disable(self):
        self.motor.stop()
        self.motor.disable()

    '''
    Enables the motor
    '''

    def enable(self):
        self.motor.enable()
Esempio n. 2
0
class App(object):
    date = datetime.datetime.now()
    def __init__(self):
        self.longitude = 13.01053
        self.latitude =  56.69815
        # Pins and settings for altitude motor
        self.altitudeMotorStepPin = 21
        self.altitudeMotorDirPin = 20
        self.altitudeMotorEnablePin = 16
        self.altitudeMotorPosAdress = 0 # Adress of i2c AD converter
        self.altitudeMotorSPR = 400 # Steps per revolution
        self.altitudeMotorMinPos = 2
        self.altitudeMotorMaxPos = 70 
        # create motor object
        self.altitudeMotor = Motor(self.altitudeMotorStepPin,
                                   self.altitudeMotorDirPin,
                                   self.altitudeMotorEnablePin,
                                   self.altitudeMotorPosAdress,
                                   self.altitudeMotorSPR,
                                   self.altitudeMotorMinPos,
                                   self.altitudeMotorMaxPos)

        # Pins and settings for azimuth motor
        self.azimuthMotorStepPin = 26
        self.azimuthMotorDirPin = 19
        self.azimuthMotorEnablePin = 13
        self.azimuthMotorPosAdress = 1 # Adress of i2c AD converter
        self.azimuthMotorSPR = 400 # Steps per revolution
        self.azimuthMotorMinPos = 20
        self.azimuthMotorMaxPos = 280
        # create motor object
        self.azimuthMotor = Motor(self.azimuthMotorStepPin,
                                   self.azimuthMotorDirPin,
                                   self.azimuthMotorEnablePin,
                                   self.azimuthMotorPosAdress,
                                   self.azimuthMotorSPR,
                                   self.azimuthMotorMinPos,
                                   self.azimuthMotorMaxPos)
        # settings
        self.updateTime = 10.0
        self.sunAltitude = 0.0
        self.sunAzimuth = 0.0
        self.jogCCW = 23
        self.jogCW = 24
        self.jogMode = 25
        self.jogAltMotor = 18
        self.wentToSunrise = False
        self.running = False
        self.isRunning = 6
        self.setup_pins()
        print('Init done')
        


    def check_altitude(self):
        print("Altitude motor pos:{} Sun altitude:{}".format(self.altitudeMotor.get_pos(), self.sunAltitude))
        if(self.sunAltitude > self.altitudeMotor.get_pos() and (self.sun_going_up(self.sunAltitude)) or self.sunAltitude - self.altitudeMotor.get_pos() > 2.0):
            self.altitudeMotor.move(self.sunAltitude, False)
        elif(self.sunAltitude < self.altitudeMotor.get_pos() and not self.sun_going_up(self.sunAltitude)):
            self.altitudeMotor.move(self.sunAltitude, True)
        else:
             pass
            
    def sun_going_up(self, current_alt):
        """Check if sun is still rising"""
        new_time = self.date + datetime.timedelta(minutes=1)
        future_alt = solar.get_altitude(self.latitude, self.longitude, new_time)
        return current_alt < future_alt

    def check_azimuth(self):
        print("Azimuth motor pos:{} Sun azimuth:{}".format(self.azimuthMotor.get_pos(), self.sunAzimuth))
        if(self.sunAzimuth > self.azimuthMotor.get_pos()):
            self.azimuthMotor.move(self.sunAzimuth, False)
 
    def setup_pins(self):
        """Ställ in GPIO utgångar"""
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.altitudeMotorStepPin, GPIO.OUT)
        GPIO.setup(self.altitudeMotorDirPin, GPIO.OUT)
        GPIO.setup(self.altitudeMotorEnablePin, GPIO.OUT)
        GPIO.setup(self.azimuthMotorStepPin, GPIO.OUT)
        GPIO.setup(self.azimuthMotorDirPin, GPIO.OUT)
        GPIO.setup(self.azimuthMotorEnablePin, GPIO.OUT)
        GPIO.setup(self.isRunning, GPIO.OUT)
        GPIO.setup(self.jogCCW, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.jogCW, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.jogMode, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.jogAltMotor, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
          
    def get_sun_pos(self):
        """Get sun altitude and azimuth using pysolar"""
        self.date = datetime.datetime.now()
        self.sunAltitude = solar.get_altitude(self.latitude, self.longitude, self.date)
        self.sunAzimuth = self.offset(solar.get_azimuth(self.latitude, self.longitude, self.date))

    def offset(self, sun_value):
        """offset between pot and pysolar"""
        offset = 180.0
        if abs(sun_value) >= 0 and abs(sun_value) < 180:
            return abs(sun_value) + offset
        else:
            return abs(sun_value + offset)


    def run(self):
        """Start app and main loop"""
        startTime = time.time()
        self.running = True
        while self.running:
            GPIO.output(self.isRunning, GPIO.HIGH)
            if(time.time() > (startTime + self.updateTime) and not self.jog_mode()):
                self.update()
                startTime = time.time()
            elif self.jog_mode():
                self.jog()
        

    def jog(self):
        if GPIO.input(self.jogCW) and not GPIO.input(self.jogCCW):
            if not GPIO.input(self.jogAltMotor):
                self.azimuthMotor.jog(self.jogCW, False)
            else:
                self.altitudeMotor.jog(self.jogCW, False)
        elif GPIO.input(self.jogCCW) and not GPIO.input(self.jogCW):
            if not GPIO.input(self.jogAltMotor):
                self.azimuthMotor.jog(self.jogCCW, True)
            else:
                self.altitudeMotor.jog(self.jogCCW, True)
                        
    def update(self):
        """Update App ,general things to do every cycle"""
        self.get_sun_pos()
        if(self.sunAltitude > 15.0):
            self.check_altitude()
            self.check_azimuth()
            self.wentToSunrise = False
        elif(self.sunAltitude < 15.0 and not self.wentToSunrise):
            print('Sun to low\n')
            next_pos = self.check_next_sunrise()
            print('Going to {} pos.\n'.format(next_pos))
            self.azimuthMotor.move(next_pos, True)
            self.wentToSunrise = True
        else:
            pass

    def check_next_sunrise(self):
        """Check azimuth of next sunrise"""
        new_date = self.date
        while(solar.get_altitude(self.latitude, self.longitude, new_date) < 15.0):
            print(new_date)
            new_date += datetime.timedelta(minutes=10)
        return self.offset(solar.get_azimuth(self.latitude, self.longitude, new_date))
    
    def jog_mode(self):
       return GPIO.input(self.jogMode)
        
    def on_exit(self):
        """cleanup when app closes"""
        self.altitudeMotor.disable()
        self.azimuthMotor.disable()
        GPIO.output(self.isRunning, GPIO.LOW)
Esempio n. 3
0
class Driver:
    def __init__(self):
        motors = parse_config()['motors']
        self.motor_left = Motor(en_pin=motors['en_pin'],
                                slew_pin=motors['slew_pin'],
                                pwm_pin=motors['left']['pwm_pin'],
                                forward_pin=motors['left']['forward_pin'],
                                reverse_pin=motors['left']['reverse_pin'],
                                sf_pin=motors['left']['state_flag_pin'],
                                name='left')
        self.motor_right = Motor(en_pin=motors['en_pin'],
                                 slew_pin=motors['slew_pin'],
                                 pwm_pin=motors['right']['pwm_pin'],
                                 forward_pin=motors['right']['forward_pin'],
                                 reverse_pin=motors['right']['reverse_pin'],
                                 sf_pin=motors['right']['state_flag_pin'],
                                 name='right')
        self.motor_left.disable()
        self.motor_right.disable()
        # todo: Implement a 2d matrix to represent direction and duration of previous movement.
        self.travel_memory = list()
        self.turn_a = 22.5
        self.turn_limit = 180 / self.turn_a
        self.last_turn = None
        self.turns = 0
        self.turn_speed = 440
        self.opposites = {"left": "right", "right": "left"}

    def start(self):
        self.motor_left.disable()
        self.motor_right.disable()
        while not rd.current_heading or not rd.current_distances:
            logging.debug("Waiting for sensor data")
            logging.debug(rd.current_heading)
            logging.debug(rd.current_distances)
            sleep(1)
        logging.info('Sensor data populated')
        self.navigate()

    def navigate(self):
        while True:
            # If its clear ahead left and right continue forwards.
            if rd.c_clear and rd.l_clear and rd.r_clear:
                logging.warning('I think its clear... driving')
                self.drive()
                # todo: Use travel_memory to map previous movement
                self.travel_memory.append(
                    [rd.current_heading, int(time.time())])
                self.turns = 0
            else:
                # Evaluate turn options
                decision = self.evaluate_turn()
                logging.info(f'Decided to turn {decision}')
                self.turn(decision)

    def evaluate_turn(self):
        logging.warning(
            f"c_clear: {rd.c_clear} l_clear: {rd.l_clear} r_clear: {rd.r_clear}"
        )
        # If we just drove here, Use USS to determine turn.
        if (self.last_turn is None and rd.l_clear) or (rd.c_clear
                                                       and rd.l_clear):
            turn_choice = 'left'
        elif (self.last_turn is None and rd.r_clear) or (rd.c_clear
                                                         and rd.r_clear):
            turn_choice = 'right'
        # Turn in the same direction until we find a gap
        elif self.turns <= self.turn_limit and self.last_turn is not None:
            turn_choice = self.last_turn
        # If we turn 180 degrees we go back the other way. (prevent erroneous physical navigation loop)
        elif self.turns >= self.turn_limit:
            turn_choice = self.opposites[self.last_turn]
            # Double the turn limit to turn back the other way.
            logging.info(f'Negating turn count: {self.turns}')
            self.turns = (0 - self.turn_limit)
        else:
            # Its the first turn at this position
            turn_choice = choice(['left', 'right'])

        if turn_choice == 'left':
            self.last_turn = turn_choice
            return -self.turn_a
        elif turn_choice == 'right':
            self.last_turn = turn_choice
            return self.turn_a
        else:
            self.last_turn = self.opposites[self.last_turn]
            return -180

    # Drive in a straight(ish) line
    def drive(self, distance_cm: int = 10, direction: int = 0):
        self.last_turn = None
        self.turns = 0
        if direction is 1:
            direction_str = 'Forward'
        else:
            direction_str = 'Reverse'
        logging.debug(f'Driving {distance_cm} centimeters {direction_str}')
        initial_distance = rd.current_distances[1]
        initial_time = time.time()
        motors_on = False
        while True:
            if not (rd.c_clear and rd.l_clear and rd.r_clear):
                # Something got in the way
                return False
            # We only drive for 1000ms or 10cm whichever is first.
            if time.time() > initial_time + 1:
                return True
            if not rd.current_heading or not rd.current_distances:
                self.motor_left.disable()
                self.motor_right.disable()
                logging.warning('Lost navigation data')
                continue
            # todo: discard outliers in ultrasonic thread.
            # If we have driven the distance_cm we return to parent loop
            if rd.current_distances[1] <= (initial_distance - distance_cm):
                self.motor_left.disable()
                self.motor_right.disable()
                return True
            # We were not currently driving so we drive.
            if not motors_on:
                self.motor_left.move(1)
                self.motor_right.move(1)
                motors_on = True

    @staticmethod
    def heading_calc(angle, initial_heading):
        if initial_heading + angle > 360:
            desired_heading = (initial_heading + angle) - 360
        elif initial_heading + angle < 0:
            desired_heading = (initial_heading + angle) + 360
        else:
            desired_heading = rd.current_heading + angle
        return desired_heading

    def turn(self, angle: int = 0, hard: bool = False):
        if angle is 0:
            logging.error('I cant turn 0 degrees!')
            return False
        self.turns += 1
        initial_heading = rd.current_heading
        # If we lose magnetometer heading we wait
        while initial_heading is None:
            logging.warning('Waiting for heading')
            initial_heading = rd.current_heading
        desired_heading = self.heading_calc(angle, initial_heading)
        logging.debug(f'Desired Heading: {desired_heading}')
        # Right turn
        if angle > 0:
            driving = False
            logging.debug(f'Initial heading: {initial_heading}')
            while True:
                # If we lose magnetometer heading mid-turn we return to navigation loop.
                if not rd.current_heading:
                    logging.warning(
                        f'Current_heading is {type(rd.current_heading)}')
                    return False
                # If a path clears mid turn return to navigation loop
                if rd.c_path and rd.l_clear and rd.r_clear:
                    self.motor_left.disable()
                    self.motor_right.disable()
                    return True
                if self.approximate_angle(desired_heading, rd.current_heading):
                    logging.debug(f'Current Heading: {rd.current_heading}')
                    logging.debug(
                        f"{angle} Turn complete attempting to disable motors")
                    self.motor_left.disable()
                    self.motor_right.disable()
                    return True
                if driving is not True:
                    self.motor_left.move(1, speed=self.turn_speed)
                    self.motor_right.move(0, speed=self.turn_speed)
                    driving = True

        # Left turn
        if angle < 0:
            driving = False
            logging.debug(f'Initial heading: {initial_heading}')
            while True:
                if not rd.current_heading:
                    logging.warning(
                        f'Current_heading is {type(rd.current_heading)}')
                    return False
                # If a path clears mid turn return
                if rd.c_path and rd.l_clear and rd.r_clear:
                    self.motor_left.disable()
                    self.motor_right.disable()
                    return True
                if self.approximate_angle(desired_heading, rd.current_heading):
                    logging.debug(f'Current Heading: {rd.current_heading}')
                    logging.debug(f"{angle} Turn complete disabling motors")
                    self.motor_left.disable()
                    self.motor_right.disable()
                    return True
                if driving is not True:
                    self.motor_left.move(0, speed=self.turn_speed)
                    self.motor_right.move(1, speed=self.turn_speed)
                    driving = True
        logging.debug('Finished turn loop')

    def abort(self):
        logging.debug('Aborting')
        self.motor_left.disable()
        self.motor_right.disable()

    @staticmethod
    def approximate_angle(desired, current):
        try:
            fuzzy = 5
            if current >= (desired - fuzzy) and current <= (desired + fuzzy):
                return True
            else:
                return False
        except TypeError as e:
            logging.debug(f'Exception caught attempting None comparison')