In [1]:
import sys

#SainSmart 16 relay board with USB HID control from here: 
#https://github.com/tatobari/hidrelaypy/blob/master/hidrelay.py
sys.path.insert(0, '/home/larsborm/Documents/Haptic_input/hidrelaypy-master/')
#If you get permission error on Ubuntu, add your username to the dailout group:
# sudo usermod -a -G dialout larsborm
import hidrelay_LB
#Dependencies: pyusb
import time
import numpy as np
import random
import csv
import os.path


Number of arguments: 3 arguments.
Argument List: ['/home/larsborm/anaconda3/envs/py27/lib/python2.7/site-packages/ipykernel_launcher.py', '-f', '/run/user/1000/jupyter/kernel-91856a57-9b14-4563-a7f3-5383ee3ca996.json']
Device not found
Command is: -f

In [3]:
class HapticInput:
    '''
    Class to give haptic inupt to user, using solonoid accuators.
    
    Dependencies: pyusb
    If you encounter permission issues on Ubuntu (and similar presumably)
    Add your username to the dailout group to gain permission:
    sudo usermod -a -G dialout <username>
    
    '''
    #Initiate and connect to Sainsmart 16 relay board with USB HID
    # From: https://github.com/tatobari/hidrelaypy/blob/master/hidrelay.py
    # Which is based on: https://github.com/mvines/relay
    
    def __init__(self):
        self.relay = hidrelay_LB.HIDRelay(verbose=False)
        #Alphabet
        self.braille_numbers ={ 'z':[0,0,0,0],
                              0: [0,1,1,1], '0': [0,1,1,1],
                              1: [1,0,0,0], '1': [1,0,0,0],
                              2: [1,0,1,0], '2': [1,0,1,0],
                              3: [1,1,0,0], '3': [1,1,0,0],
                              4: [1,1,0,1], '4': [1,1,0,1],
                              5: [1,0,0,1], '5': [1,0,0,1],
                              6: [1,1,1,0], '6': [1,1,1,0],
                              7: [1,1,1,1], '7': [1,1,1,1],
                              8: [1,0,1,1], '8': [1,0,1,1],
                              9: [0,1,1,0], '9': [0,1,1,0]}

        self.set_empty()
        time.sleep(0.5)
        self.set_full()
        time.sleep(0.5)
        self.set_empty()
        print 'Ready\n\n'
    
    ###########################################################################################
    #Low level functions
    ###########################################################################################

    #Message construction
    def create_braille(self, number, show_zero=False):
        '''
        Create corresponding Braille pattern for four numbers.
        Each number is made up out of four points following Braille.
        First number in top left corner, second top right etc.
        If number is smaler than 4 digits, padding on the left:
        12 --> [0, 0, 1, 2] which can optionally be displayed.
        Input:
        `number`(int/list): Either an integer with max 4 digits or a 
            list with max four one digit numbers. Like [1,2,3,4]
            Only positive numbers allowed.
        `show_zero`(bool): If True, the braille version of zero will be
            used for the added leading zeros. [0,1,1,1]. Else: [0,0,0,0]
            If leading zeros are specified in the input they will be 
            displayed
        Output:
            4 by 4 numpy array containing four digits as braille numbers

        '''
        #Convert number (int/list) to list of string 
        if type(number) == int:
            if number<0:
                raise KeyError('Error, invalid input: "{}". Input can not be negative'.format(number))
            number = [d for d in str(number)]
            for i in range(4-len(number)):
                if show_zero == True:
                    number.insert(0, '0')
                else:
                    number.insert(0, 'z')

        if type(number) == list:
            number = [str(d) for d in number]
            for i in range(4-len(number)):
                if show_zero == True:
                    number.insert(0, '0')
                else:
                    number.insert(0, 'z')

        #Check format
        if len(number) > 4:
            raise KeyError ('Error, input: "{}" is too long. Max 4 digits'.format(number))

        for i in number:
            if len(i)>1 or i<0:
                print 'Input creat_pattern function: {}'.format(number)
                raise KeyError ('Error, invalid input: "{}". Only single positive digits allowed'.format(i))

        #Convert to braille
        number_braille = []
        for i in number:
            number_braille.append(self.braille_numbers[i])

        #Construct pattern
        template = [[0,0,0,0],
                    [0,0,0,0],
                    [0,0,0,0],
                    [0,0,0,0]]

        template[0] = [number_braille[0][0], number_braille[0][1], 
                       number_braille[1][0], number_braille[1][1]]
        template[1] = [number_braille[0][2], number_braille[0][3], 
                       number_braille[1][2], number_braille[1][3]]
        template[2] = [number_braille[2][0], number_braille[2][1], 
                       number_braille[3][0], number_braille[3][1]]
        template[3] = [number_braille[2][2], number_braille[2][3], 
                       number_braille[3][2], number_braille[3][3]]

        pattern = np.array(template)
        return pattern
    
    
    def create_binary(self, number):
        """
        Convert number to binary and format in 4x4 array.
        (1 is at rtop right corner.)
         Output:
                4 by 4 numpy array containing the numer in binary

        """
        if not (0 <= number <= 65535 and type(number) == int):
            raise ValueError ('Binary input number must be an integer between 0 and 65535, not: {}'.format(number))
      
        pattern = np.flipud(np.array([int(i) for i in'{0:016b}'.format(number)]).reshape(4,4))
        return pattern

    #Set pattern
    def set_pattern(self, pattern): 
        """
        Switch relays on/off acording to pattern.
        Input:
        `pattern`(list/array): 4 by 4 array of zeros and ones. Eiter numpy array
            or a list of 4 sub-lists with length 4.
        (Will work for different sizes of arrays)

        """
        for ir, row in enumerate(pattern):
            for ic, col in enumerate(row):
                relay_n = ir*len(row) + ic
                self.relay.set(relay_n, bool(col))
                
    
    def set_relay(self, relay_n, on_off):
        
        self.relay.set(relay_n, on_off)
        
    #####################################################################################################
    #High level functions
    #####################################################################################################
    
    def set_braille(self, number):
        """
        Create braille 2x2 pattern with max 4 digits.
        Each number is made up out of four points following Braille.
        First number in top left corner, second top right etc.
        If number is smaler than 4 digits, padding on the left:
        12 --> [0, 0, 1, 2]
        Input:
        `number`(int/list): Either an integer with max 4 digits or a 
            list with max four one digit numbers. Like [1,2,3,4]
            Only positive numbers allowed.

        """
        self.set_pattern(self.create_braille(number))
    
    def set_binary(self, number):
        """
        Convert number to binary (max 16 bits: 65535)
        1 will be on relay 3
        Input:
            `number`(int): Number between 0 and 65535 (including)
        
        """
        self.set_pattern(self.create_binary(number))
    
    def set_empty(self):
        """
        switch all relays off.

        """
        pattern = [[0,0,0,0],
                   [0,0,0,0],
                   [0,0,0,0],
                   [0,0,0,0]]
        self.set_pattern(pattern)
        
    def set_full(self):
        """
        Swich all relays on.

        """
        pattern = [[1,1,1,1],
                   [1,1,1,1],
                   [1,1,1,1],
                   [1,1,1,1]]
        self.set_pattern(pattern)

    #####################################################################################################
    #Training functions
    #####################################################################################################
    
    def performace_logfile(self):
        """
        Creates a performace log file with the name: 'performance_log.csv'

        """
        if not os.path.isfile('performance_log.csv'):
            with open('performance_log.csv', 'w') as performance_log:
                writer = csv.writer(performance_log)
                writer.writerow(['date', 'encoding', 'trials_completed', 'correct', 'percentage correct %'])
            
    def performance_logger(self, encoding, trials, correct):
        """
        Writes: 'time', 'number of trials', 'number correct' and 'percentage correct' to 
        the 'performance_log.csv' file

        """
        with open('performance_log.csv', 'a') as performance_log:
            writer = csv.writer(performance_log)
            writer.writerow([time.strftime("%Y-%m-%d %H:%M"), encoding, trials, correct,
                             (float(correct)/float(trials))*100])
    
    def train_braille(self, number_of_digits=4):
        """
        Train to recognize braille patterns.
        Input:
        `number_of_digits`(int): Number o digits to train with. [1-4]

        """
        print number_of_digits
        if number_of_digits>4 or number_of_digits<0 or type(number_of_digits) != int:
            raise ValueError ('number_of_digits should be 1, 2, 3 or 4')
        
        self.performace_logfile()
        n_trials = 0
        n_correct = 0
        try:
            while True:
                n_trials +=1
                number = random.randint(0, int('9'*number_of_digits))
                self.set_braille(number)
                while True:
                    guess = raw_input('Guess the current number: ')
                    try:
                        guess = int(guess)
                        break
                    except ValueError as e:
                        print 'Invalid input'
                if guess == number:
                    print 'Correct!\n\n'
                    n_correct += 1
                else:
                    print 'Wrong, the correct answer is: {}\n\n'.format(number)
                    time.sleep(3)
                self.set_empty()
                time.sleep(1)

        except KeyboardInterrupt:
            self.performance_logger('Braille', n_trials, n_correct)
            print 'Training stopped'
            print 'Out of {} trials, {} were correct.'.format(n_trials, n_correct)
            
    def train_binary(self, number_of_rows=1):
        """
        Train to recognize binary patterns.
        Input:
        `number_of_rows`(int): 

        """
        print number_of_rows
        if number_of_rows>4 or number_of_rows<0 or type(number_of_rows) != int:
            raise ValueError ('number_of_rows should be 1, 2, 3 or 4')
            
        self.performace_logfile()    
        n_trials = 0
        n_correct = 0
        try:
            while True:
                n_trials +=1
                number = random.randint(0, int('1111'*number_of_rows, 2))
                self.set_binary(number)
                while True:
                    guess = raw_input('Guess the current number: ')
                    try:
                        guess = int(guess)
                        break
                    except ValueError as e:
                        print 'Invalid input'
                if guess == number:
                    print 'Correct!\n\n'
                    n_correct += 1
                else:
                    print 'Wrong, the correct answer is: {}\n\n'.format(number)
                    time.sleep(3)
                self.set_empty()
                time.sleep(1)

        except KeyboardInterrupt:
            self.performance_logger('Binary', n_trials, n_correct)
            print 'Training stopped'
            print 'Out of {} trials, {} were correct.'.format(n_trials, n_correct)
            
hi = HapticInput()


Ready



In [ ]:
while True:
    guess = raw_input('Guess the current number: ')
    try:
        guess = int(guess)
        print 'correct'
        break
    except ValueError as e:
        print 'Invalid input'


Guess the current number: 4gg
Invalid input
Guess the current number: 5dd
Invalid input

In [ ]:
8 4 2 1

In [3]:
hi.set_binary(1)

In [4]:
hi.set_empty()

In [6]:
hi.train_binary(number_of_rows=1)


1
Guess the current number: 12
Wrong, the correct answer is: 14


Guess the current number: 6
Wrong, the correct answer is: 14


Guess the current number: 4
Correct!


Guess the current number: 1
Correct!


Guess the current number: 3
Wrong, the correct answer is: 5


Guess the current number: 0
Correct!


Guess the current number: 7
Wrong, the correct answer is: 13


Guess the current number: 7
Wrong, the correct answer is: 13


Guess the current number: 6
Wrong, the correct answer is: 12


Guess the current number: 2
Correct!


Guess the current number: 12
Wrong, the correct answer is: 14


Guess the current number: 2
Correct!


Guess the current number: 15
Correct!


Guess the current number: 15
Wrong, the correct answer is: 11


Guess the current number: 0
Correct!


Guess the current number: 0
Wrong, the correct answer is: 11


Training stopped
Out of 17 trials, 7 were correct.

In [ ]:
8 4 2 1

In [7]:
hi.set_empty()

In [9]:
hi.set_binary(1)


---------------------------------------------------------------------------
USBError                                  Traceback (most recent call last)
<ipython-input-9-3ae25bfb6946> in <module>()
----> 1 hi.set_binary(1)

<ipython-input-2-e03649345187> in set_binary(self, number)
    172 
    173         """
--> 174         self.set_pattern(self.create_binary(number))
    175 
    176     def set_empty(self):

<ipython-input-2-e03649345187> in set_pattern(self, pattern)
    138             for ic, col in enumerate(row):
    139                 relay_n = ir*len(row) + ic
--> 140                 self.relay.set(relay_n, bool(col))
    141 
    142 

/home/larsborm/Documents/Haptic_input/hidrelaypy-master/hidrelay_LB.pyc in set(self, relay_id, state)
    194              raise ValueError('Invalid state: ', state)
    195 
--> 196         readmask = self.read()
    197         if self.verbose == True:
    198                 print "Readmask: ", readmask

/home/larsborm/Documents/Haptic_input/hidrelaypy-master/hidrelay_LB.pyc in read(self)
    128         readCmd_pack = self.pack_bytes(readCmd)
    129         # assert len(self.hid.write(self.ep_out_address, readCmd_pack, 100)) == len(readCmd_pack)
--> 130         self.hid.write(self.ep_out_address, readCmd_pack, 100)
    131 
    132         data_pack = self.hid.read(self.ep_in_address, self.packet_len, 100)

/home/larsborm/anaconda3/envs/py27/lib/python2.7/site-packages/usb/core.pyc in write(self, endpoint, data, timeout)
    946                 intf.bInterfaceNumber,
    947                 _interop.as_array(data),
--> 948                 self.__get_timeout(timeout)
    949             )
    950 

/home/larsborm/anaconda3/envs/py27/lib/python2.7/site-packages/usb/backend/libusb1.pyc in intr_write(self, dev_handle, ep, intf, data, timeout)
    840                             intf,
    841                             data,
--> 842                             timeout)
    843 
    844     @methodtrace(_logger)

/home/larsborm/anaconda3/envs/py27/lib/python2.7/site-packages/usb/backend/libusb1.pyc in __write(self, fn, dev_handle, ep, intf, data, timeout)
    918         # do not assume LIBUSB_ERROR_TIMEOUT means no I/O.
    919         if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
--> 920             _check(retval)
    921 
    922         return transferred.value

/home/larsborm/anaconda3/envs/py27/lib/python2.7/site-packages/usb/backend/libusb1.pyc in _check(ret)
    593             raise NotImplementedError(_strerror(ret))
    594         else:
--> 595             raise USBError(_strerror(ret), ret, _libusb_errno[ret])
    596 
    597     return ret

USBError: [Errno 19] No such device (it may have been disconnected)

In [78]:
hi.set_relay(2, True)

In [47]:
hi.set_relay(0, False)

In [16]:
hi.set_relay(1, True)

In [48]:
hi.set_relay(1, False)

In [108]:
while True:
    number = random.randint(0,3)
    sleep_time = 1
    hi.set_relay(number, True)
    time.sleep(sleep_time)
    hi.set_relay(number, False)
    time.sleep(sleep_time)


---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-108-aef93f3b78c2> in <module>()
      3     sleep_time = 1
      4     hi.set_relay(number, True)
----> 5     time.sleep(sleep_time)
      6     hi.set_relay(number, False)
      7     time.sleep(sleep_time)

KeyboardInterrupt: 

In [52]:
for i in range(20):
    sleep_time = .2
    hi.set_relay(0, True)
    time.sleep(sleep_time)
    hi.set_relay(1, True)
    hi.set_relay(0, False)
    time.sleep(sleep_time)
    hi.set_relay(1, False)
'''
for i in range(10):
    sleep_time = .5
    hi.set_relay(0, True)
    time.sleep(sleep_time)
    hi.set_relay(0, False)
    time.sleep(sleep_time)
for i in range(10):
    sleep_time = .1
    hi.set_relay(0, True)
    time.sleep(sleep_time)
    hi.set_relay(0, False)
    time.sleep(sleep_time)    
for i in range(10):
    sleep_time = .05
    hi.set_relay(0, True)
    time.sleep(sleep_time)
    hi.set_relay(0, False)
    time.sleep(sleep_time)
    '''


Out[52]:
'\nfor i in range(10):\n    sleep_time = .5\n    hi.set_relay(0, True)\n    time.sleep(sleep_time)\n    hi.set_relay(0, False)\n    time.sleep(sleep_time)\nfor i in range(10):\n    sleep_time = .1\n    hi.set_relay(0, True)\n    time.sleep(sleep_time)\n    hi.set_relay(0, False)\n    time.sleep(sleep_time)    \nfor i in range(10):\n    sleep_time = .05\n    hi.set_relay(0, True)\n    time.sleep(sleep_time)\n    hi.set_relay(0, False)\n    time.sleep(sleep_time)\n    '

Test Functions


In [ ]:
rel = hidrelay.HIDRelay()

In [ ]:
pattern = [[0,1,0,0],
           [0,1,0,0],
           [0,1,0,0],
           [0,1,0,0]]

In [ ]:
def set_pattern(pattern): 
    """
    Switch relays on/off acording to pattern.
    Input:
    `pattern`(list/array): 4 by 4 array of zeros and ones. Eiter numpy array
        or a list of 4 sub-lists with length 4.
    (Will work for different sizes of arrays)
    
    """
    for ir, row in enumerate(pattern):
        for ic, col in enumerate(row):
            relay_n = ir*len(row) + ic
            rel.set(relay_n, bool(col))
            time.sleep(0)
       
set_pattern(pattern)

In [ ]:
def set_number(number):
    """
    Create braille 2x2 pattern with max 4 digits.
    Each number is made up out of four points following Braille.
    First number in top left corner, second top right etc.
    If number is smaler than 4 digits, padding on the left:
    12 --> [0, 0, 1, 2]
    Input:
    `number`(int/list): Either an integer with max 4 digits or a 
        list with max four one digit numbers. Like [1,2,3,4]
        Only positive numbers allowed.
    
    """
    set_pattern(create_pattern(number))
    
set_number(7117)

In [ ]:
def set_empty():
    """
    switch all relays off.
    
    """
    pattern = [[0,0,0,0],
               [0,0,0,0],
               [0,0,0,0],
               [0,0,0,0]]
    set_pattern(pattern)

set_empty()

In [ ]:
def set_full():
    """
    Swich all relays on.
    
    """
    pattern = [[1,1,1,1],
               [1,1,1,1],
               [1,1,1,1],
               [1,1,1,1]]
    set_pattern(pattern)

set_full()

In [ ]:
d = {0:12, '0':23}
d['0']

In [ ]:
braille_numbers ={'z':[0,0,0,0], 
                  0: [0,1,1,1], '0': [0,1,1,1],
                  1: [1,0,0,0], '1': [1,0,0,0],
                  2: [1,0,1,0], '2': [1,0,1,0],
                  3: [1,1,0,0], '3': [1,1,0,0],
                  4: [1,1,0,1], '4': [1,1,0,1],
                  5: [1,0,0,1], '5': [1,0,0,1],
                  6: [1,1,1,0], '6': [1,1,1,0],
                  7: [1,1,1,1], '7': [1,1,1,1],
                  8: [1,0,1,1], '8': [1,0,1,1],
                  9: [0,1,1,0], '9': [0,1,1,0]}

In [ ]:
def create_pattern(number, show_zero=False):
    '''
    Create corresponding Braille pattern for four numbers.
    Each number is made up out of four points following Braille.
    First number in top left corner, second top right etc.
    If number is smaler than 4 digits, padding on the left:
    12 --> [0, 0, 1, 2] which can optionally be displayed.
    Input:
    `number`(int/list): Either an integer with max 4 digits or a 
        list with max four one digit numbers. Like [1,2,3,4]
        Only positive numbers allowed.
    `show_zero`(bool): If True, the braille version of zero will be
        used for the added leading zeros. [0,1,1,1]. Else: [0,0,0,0]
        If leading zeros are specified in the input they will be 
        displayed
    Output:
        4 by 4 numpy array containing four digits as braille numbers
    
    '''
    #Convert number (int/list) to list of string 
    if type(number) == int:
        if number<0:
            raise KeyError('Error, invalid input: "{}". Input can not be negative'.format(number))
        number = [d for d in str(number)]
        for i in range(4-len(number)):
            if show_zero == True:
                number.insert(0, '0')
            else:
                number.insert(0, 'z')
            
    if type(number) == list:
        number = [str(d) for d in number]
        for i in range(4-len(number)):
            if show_zero == True:
                number.insert(0, '0')
            else:
                number.insert(0, 'z')
    
    #Check format
    if len(number) > 4:
        raise KeyError ('Error, input: "{}" is too long. Max 4 digits'.format(number))
        
    for i in number:
        if len(i)>1 or i<0:
            print 'Input creat_pattern function: {}'.format(number)
            raise KeyError ('Error, invalid input: "{}". Only single positive digits allowed'.format(i))
    
    #Convert to braille
    number_braille = []
    for i in number:
        number_braille.append(braille_numbers[i])
    
    #Construct pattern
    template = [[0,0,0,0],
                [0,0,0,0],
                [0,0,0,0],
                [0,0,0,0]]
    
    template[0] = [number_braille[0][0], number_braille[0][1], number_braille[1][0], number_braille[1][1]]
    template[1] = [number_braille[0][2], number_braille[0][3], number_braille[1][2], number_braille[1][3]]
    template[2] = [number_braille[2][0], number_braille[2][1], number_braille[3][0], number_braille[3][1]]
    template[3] = [number_braille[2][2], number_braille[2][3], number_braille[3][2], number_braille[3][3]]
    
    pattern = np.array(template)
    
    return pattern


create_pattern([0,3,4,5])
#numb = create_pattern(1)
#np.array(create_pattern(7117))

In [ ]:
import random

In [ ]:
random.randint(0,9999)

In [ ]:
int('9'*3)

In [ ]:
def train(number_of_digits=4):
    """
    Train to recognize the patterns.
    Input:
    `number_of_digits`(int): Number o digits to train with. [1-4]
    
    """
    print number_of_digits
    if number_of_digits>4 or number_of_digits<0 or type(number_of_digits) != int:
        raise ValueError ('number_of_digits should be 1, 2, 3 or 4')
    
    try:
        while True:
            number = random.randint(0, int('9'*number_of_digits))
            set_number(number)
            guess = raw_input('Guess the current number: ')
            if guess == number:
                print 'Correct!\n\n'
            else:
                print 'Wrong, the correct answer is: {}\n\n'.format(number)
                time.sleep(5)
            set_empty()
        
    except KeyboardInterrupt:
        print 'Training stopped'

In [84]:
def create_binary(number):
    """
    Convert number to binary and format in 4x4 array.
    (1 is at right bottom corner.)
    
    """
    if not (0 <= number <= 65535 and type(number) == int):
        raise ValueError ('Binary input number must be an integer between 0 and 65535, not: {}'.format(number))
        
    pattern = np.array([int(i) for i in'{0:016b}'.format(number)]).reshape(4,4)
    return pattern

In [ ]:


In [38]:
import csv
import os.path

def performace_logfile():
    """
    Creates a performace log file with the name: 'performance_log.csv'
    
    """
    if not os.path.isfile('performance_log.csv'):
        with open('performance_log.csv', 'w') as performance_log:
            writer = csv.writer(performance_log)
            writer.writerow(['date', 'trials_completed', 'correct', 'percentage correct %'])

In [ ]:


In [59]:
def performance_logger(trials, correct):
    """
    Writes: 'time', 'number of trials', 'number correct' and 'percentage correct' to 
    the 'performance_log.csv' file
    
    """
    with open('performance_log.csv', 'a') as performance_log:
        writer = csv.writer(performance_log)
        writer.writerow([time.strftime("%Y-%m-%d %H:%M"), trials, correct, (float(correct)/float(trials))*100])
        print()
        
performance_logger(45, 24)


()

In [46]:
25/50


Out[46]:
0

In [ ]:


In [ ]:


In [ ]: