Example #1
0
class Behaviours():
    '''
        This class provides avoidance and some other relatively simple behaviours.
    '''
    def __init__(self, motors, ifs, level):
        self._log = Logger("avoid", level)
        self._motors = motors
        self._ifs = ifs
        self._rgbmatrix = RgbMatrix(Level.INFO)
        self._fast_speed = 99.0
        self._slow_speed = 50.0
        self._log.info('ready.')

    # ..........................................................................
    def back_up(self, duration_sec):
        '''
            Back up for the specified duration so we don't hang our bumper.
        '''
        self._motors.astern(self._fast_speed)
        time.sleep(duration_sec)

    # avoid ....................................................................
    def avoid(self, orientation):
        '''
            Avoids differently depending on orientation.           
        '''
        self._log.info(Fore.CYAN + 'avoid {}.'.format(orientation.name))

        if orientation is Orientation.CNTR:
            orientation = Orientation.PORT if random.choice(
                (True, False)) else Orientation.STBD
            self._log.info(Fore.YELLOW +
                           'randomly avoiding to {}.'.format(orientation.name))

        self._ifs.suppress(True)
        self._motors.halt()
        self.back_up(0.5)
        if orientation is Orientation.PORT:
            self._motors.spin_port(self._fast_speed)
        elif orientation is Orientation.STBD:
            self._motors.spin_starboard(self._fast_speed)
        else:
            raise Exception('unexpected center orientation.')
        time.sleep(2.0)
        self._motors.brake()
        self._ifs.suppress(False)

        self._log.info('action complete: avoid.')

    # sniff ....................................................................
    def sniff(self):
        if self._rgbmatrix.is_disabled():
            self._rgbmatrix.set_solid_color(Color.BLUE)
            self._rgbmatrix.set_display_type(DisplayType.SOLID)
            #           self._rgbmatrix.set_display_type(DisplayType.RANDOM)
            self._rgbmatrix.enable()
        else:
            self._rgbmatrix.disable()
Example #2
0
def main():
    print(
        Fore.GREEN +
        'main begin.  ......................................................... '
        + Style.RESET_ALL)

    print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
          ' INFO  : starting test...' + Style.RESET_ALL)

    try:

        _rgbmatrix = RgbMatrix(Level.INFO)

        print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
              ' INFO  : color test...' + Style.RESET_ALL)
        for color in Color:
            _rgbmatrix.set_color(color)
            time.sleep(0.15)
        print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
              ' INFO  : color test complete.' + Style.RESET_ALL)

        for display_type in DisplayType:
            print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
                  ' INFO  : displaying {}...'.format(display_type.name) +
                  Style.RESET_ALL)
            _rgbmatrix.set_display_type(display_type)
            _rgbmatrix.enable()
            time.sleep(2.0)
            _rgbmatrix.disable()
            count = 0
            while not _rgbmatrix.is_disabled():
                count += 1
                time.sleep(1.0)
                if count > 5:
                    print(
                        'rgbmatrix_test    :' + Fore.RED + Style.BRIGHT +
                        ' INFO  : timeout waiting to disable rgbmatrix thread for {}.'
                        .format(display_type.name) + Style.RESET_ALL)
                    sys.exit(1)
            _rgbmatrix.set_color(Color.BLACK)

            print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
                  ' INFO  : {} complete.'.format(display_type.name) +
                  Style.RESET_ALL)
        time.sleep(1.0)

        print('rgbmatrix_test    :' + Fore.CYAN + Style.BRIGHT +
              ' INFO  : test complete.' + Style.RESET_ALL)

    except KeyboardInterrupt:
        print('rgbmatrix_test    :' + Fore.YELLOW +
              ' INFO  : Ctrl-C caught: exiting...' + Style.RESET_ALL)
    finally:
        _rgbmatrix.disable()
        _rgbmatrix.close()
Example #3
0
class Behaviours():
    '''
        This class provides avoidance and some other relatively simple behaviours.

        This version uses the PID controller rathen than directly driving the motors.
    '''
    def __init__(self, config, pid_motor_controller, level):
        self._log = Logger("behave", level)
        if config is None:
            raise ValueError('null configuration argument.')
        self._geo = Geometry(config, level)
        _config = config['ros'].get('behaviours')
        self._accel_range_cm = _config.get('accel_range_cm')
        self._cruising_velocity = _config.get('cruising_velocity')
        self._targeting_velocity = _config.get('targeting_velocity')
        self._pid_motor_controller = pid_motor_controller
        _pid_controllers = pid_motor_controller.get_pid_controllers()
        self._port_pid = _pid_controllers[0]
        self._stbd_pid = _pid_controllers[1]
        self._rgbmatrix = RgbMatrix(Level.INFO)
        self._fast_speed = 99.0
        self._slow_speed = 50.0
        self._log.info('ready.')

    # ..........................................................................
    def get_cruising_velocity(self):
        return self._cruising_velocity

    # ..........................................................................
    def back_up(self, duration_sec):
        '''
            Back up for the specified duration so we don't hang our bumper.
        '''
        self._log.info('back up.')
        #       self._motors.astern(self._fast_speed)
        #       time.sleep(duration_sec)
        pass

    # ==========================================================================
    # one_meter .....................................................................
    '''
        The configuration defines an 'acceleration range', which is the range used
        for both acceleration and deceleration. If the travel distance is greater
        than twice this range we have enough room to reach cruising speed before
        beginning to decelerate to the step target. We also define a targeting speed,
        which is a low velocity from which we are prepared to immediately halt upon
        reaching our step target.
    '''
    '''
        Geometry Notes ...................................

        494 encoder steps per rotation (maybe 493)
        68.5mm diameter tires
        215.19mm/21.2cm wheel circumference
        1 wheel rotation = 215.2mm
        2295 steps per meter
        2295 steps per second  = 1 m/sec
        2295 steps per second  = 100 cm/sec
        229.5 steps per second = 10 cm/sec
        22.95 steps per second = 1 cm/sec

        1 rotation = 215mm = 494 steps
        1 meter = 4.587 rotations
        2295.6 steps per meter
        22.95 steps per cm
    '''

    def _one_meter(self, pid, callback):
        '''
           Threaded method for one_meter, one for each PID controller.
           This will enabled the PID if not already enabled.
        '''
        _current_steps = pid.steps  # get this quickly

        _rate = Rate(20)
        if not pid.enabled:
            pid.enable()
        pid.set_slew_rate(SlewRate.SLOWER)
        pid.enable_slew(True)

        # self._geo.steps_for_distance_cm
        _distance_cm = 200.0  # should be 1m
        _target_step_count = _distance_cm * self._geo.steps_per_cm
        _target_steps = round(_current_steps + _target_step_count)
        _closing_target_steps = _target_steps - self._geo.steps_per_rotation

        # last wheel rotation
        _final_target_step_count = _target_steps - (
            1 * self._geo.steps_per_rotation)

        _proposed_accel_range_cm = _distance_cm / 4.0
        if _proposed_accel_range_cm * 2.0 >= self._accel_range_cm:  # is the proposed range greater than the standard range?
            # then we use standard acceleration/deceleration
            self._log.info(
                'using standard accel/decel range (compressed: {:5.2f}cm; standard: {:5.2f}cm)'
                .format(_proposed_accel_range_cm, self._accel_range_cm))
            _accel_range_cm = self._accel_range_cm
        else:  # otherwise compress to just one fourth of distance
            self._log.info(
                'using compressed accel/decel range (compressed: {:5.2f}cm; standard: {:5.2f}cm)'
                .format(_proposed_accel_range_cm, self._accel_range_cm))
            _accel_range_cm = _proposed_accel_range_cm

        _accel_range_steps = round(_accel_range_cm * self._geo.steps_per_cm)
        self._log.info(
            'using accel/decel range of {:5.2f}cm, or {:d} steps.'.format(
                _accel_range_cm, _accel_range_steps))

        _accel_target_steps = _current_steps + _accel_range_steps  # accelerate til this step count
        _decel_target_steps = _target_steps - _accel_range_steps  # step count when we begin to decelerate

        self._log.info(Fore.YELLOW + 'begin one meter travel for {} motor accelerating from {:d} to {:d} steps, then cruise until {:d} steps, when we decelerate to {:d} steps and halt.'.format(\
                pid.orientation.label, _current_steps, _accel_target_steps, _decel_target_steps, _target_steps))

        _actually_do_this = True

        if _actually_do_this:
            #           _accel_velocity = 0
            # accelerate to cruising velocity ............................................
            #           self._rgbmatrix.show_color(Color.CYAN, Orientation.STBD)
            self._log.info(
                Fore.YELLOW +
                '{} motor accelerating...'.format(pid.orientation.label))
            while pid.steps < _accel_target_steps:
                pid.velocity = self._cruising_velocity
                _rate.wait()

            # cruise until 3/4 of range ..................................................
            self._log.info(Fore.YELLOW +
                           '{} motor reached cruising velocity...'.format(
                               pid.orientation.label))
            #           self._rgbmatrix.show_color(Color.GREEN, Orientation.STBD)
            pid.velocity = self._cruising_velocity
            while pid.steps < _decel_target_steps:  # .....................................
                _rate.wait()

            # slow down until we reach 9/10 of range
#           self._rgbmatrix.show_color(Color.YELLOW, Orientation.STBD)
            pid.velocity = round(
                (self._cruising_velocity + self._targeting_velocity) / 2.0)
            while pid.steps < _decel_target_steps:  # .....................................
                _rate.wait()

#           self._rgbmatrix.show_color(Color.ORANGE, Orientation.STBD)
            self._log.info(
                Fore.YELLOW +
                '{} motor reached 9/10 of target, decelerating to targeting velocity...'
                .format(pid.orientation.label))
            pid.set_slew_rate(SlewRate.NORMAL)
            # decelerate to targeting velocity when we get to one wheel revo of target ...
            pid.velocity = self._targeting_velocity
            while pid.steps < _closing_target_steps:  # ...................................
                _rate.wait()

#           self._rgbmatrix.show_color(Color.RED, Orientation.STBD)
            pid.velocity = self._targeting_velocity
            while pid.steps < _target_steps:  # ...........................................
                #               self._log.info(Fore.YELLOW + Style.DIM + '{:d} steps...'.format(pid.steps))
                time.sleep(0.001)
            pid.velocity = 0.0
            #           self._rgbmatrix.show_color(Color.BLACK, Orientation.STBD)
            self._log.info(
                Fore.YELLOW +
                '{} motor stopping...'.format(pid.orientation.label))

        time.sleep(1.0)
        pid.set_slew_rate(SlewRate.NORMAL)

        self._log.info(
            Fore.YELLOW +
            'executing {} callback...'.format(pid.orientation.label))
        callback()
        self._rgbmatrix.disable()
        #       pid.reset()
        #       pid.disable()
        self._log.info(Fore.YELLOW +
                       'one meter on {} motor complete: {:d} of {:d} steps.'.
                       format(pid.orientation.label, pid.steps, _target_steps))

    def one_meter(self):
        '''
           A behaviour that drives one meter forward in a straight line, accelerating and decelerating to hit the target exactly.
        '''
        self._log.info('start one meter travel...')
        self._one_meter_port_complete = False
        self._one_meter_stbd_complete = False
        _tp = threading.Thread(target=self._one_meter,
                               args=(self._port_pid,
                                     self._one_meter_callback_port))
        _ts = threading.Thread(target=self._one_meter,
                               args=(self._stbd_pid,
                                     self._one_meter_callback_stbd))
        _tp.start()
        _ts.start()
        _tp.join()
        _ts.join()
        self._log.info('one meter travel complete.')

    # ..........................................................................
    def _one_meter_callback_port(self):
        self._log.info(Fore.RED + 'port one meter complete.')
        self._one_meter_port_complete = True

    # ..........................................................................
    def _one_meter_callback_stbd(self):
        self._log.info(Fore.GREEN + 'stbd one meter complete.')
        self._one_meter_stbd_complete = True

    # ..........................................................................
    def is_one_metering(self):
        return not self._one_meter_port_complete and not self._one_meter_stbd_complete

    # ==========================================================================
    # roam .....................................................................

    # ..........................................................................
    def _roam_callback_port(self):
        self._log.info(Fore.RED + 'port roam complete.')
        self._roam_port_complete = True

    # ..........................................................................
    def _roam_callback_stbd(self):
        self._log.info(Fore.GREEN + 'stbd roam complete.')
        self._roam_stbd_complete = True

    # ..........................................................................
    def is_roaming(self):
        return not self._roam_port_complete and not self._roam_stbd_complete

    def _roam(self, pid, velocity, steps, orientation, callback):
        '''
           Thread method for each PID controller.
        '''
        _current_steps = pid.steps
        _target_steps = _current_steps + steps
        self._log.info(Fore.YELLOW +
                       'begin {} roaming from {:d} to {:d} steps.'.format(
                           orientation.label, _current_steps, _target_steps))
        pid.velocity = velocity
        while pid.steps < _target_steps:
            # TODO accelerate
            # TODO cruise
            time.sleep(0.01)
        # TODO decelerate
        pid.velocity = 0.0

        callback()
        time.sleep(1.0)
        self._log.info('{} roaming complete: {:d} of {:d} steps.'.format(
            orientation.label, pid.steps, _target_steps))

    def roam(self):
        '''
           A pseudo-roam behaviour.
        '''
        '''
            Geometry Notes ...................................
    
            494 encoder steps per rotation (maybe 493)
            68.5mm diameter tires
            215.19mm/21.2cm wheel circumference
            1 wheel rotation = 215.2mm
            2295 steps per meter
            2295 steps per second  = 1 m/sec
            2295 steps per second  = 100 cm/sec
            229.5 steps per second = 10 cm/sec
            22.95 steps per second = 1 cm/sec
    
            1 rotation = 215mm = 494 steps
            1 meter = 4.587 rotations
            2295.6 steps per meter
            22.95 steps per cm
        '''
        self._log.info('roam.')
        self._roam_port_complete = False
        self._roam_stbd_complete = False
        _port_current_velocity = 0.0
        self._port_pid
        self._stbd_pid

        _forward_steps_per_rotation = 494
        _rotations = 5
        _forward_steps = _forward_steps_per_rotation * _rotations
        _velocity = 35.0  #Velocity.HALF
        self._log.info(
            'start roaming at velocity {:5.2f} for {:d} steps'.format(
                _velocity, _forward_steps))

        _tp = threading.Thread(target=self._roam,
                               args=(self._port_pid, _velocity, _forward_steps,
                                     Orientation.PORT,
                                     self._roam_callback_port))
        _ts = threading.Thread(target=self._roam,
                               args=(self._stbd_pid, _velocity, _forward_steps,
                                     Orientation.STBD,
                                     self._roam_callback_stbd))
        _tp.start()
        _ts.start()
        _tp.join()
        _ts.join()
        self._log.info('complete.')
        pass

    # avoid ....................................................................
    def avoid(self, orientation):
        '''
            Avoids differently depending on orientation.           
        '''
        self._log.info(Fore.CYAN + 'avoid {}.'.format(orientation.name))

        self._log.info('action complete: avoid.')

    # sniff ....................................................................
    def sniff(self):
        if self._rgbmatrix.is_disabled():
            self._rgbmatrix.set_solid_color(Color.BLUE)
            self._rgbmatrix.set_display_type(DisplayType.SOLID)
            #           self._rgbmatrix.set_display_type(DisplayType.RANDOM)
            self._rgbmatrix.enable()
        else:
            self._rgbmatrix.disable()