Learn EV3 Python‎ > ‎

Keyboard Control

Define 'robot'. It's not easy, is it? But we can probably agree that we expect robots to be able to carry out physical tasks with limited human intervention and thus a good degree of autonomy. If there is very significant human intervention, such as when a pilot on the ground controls a radio-controlled model plane in real time then you would probably refuse to call that plane a robot. But if a Google car or a GPS-equipped drone can find its way to its destination autonomously after having been given the destination before the journey begins then you would accept that this is robotic behaviour, would you not? But limited human intervention does not necessarily mean zero human intervention. We can certainly say that we would want our domestic robot slaves of the future to respond to all kinds of human commands during the day: "Take out the garbage", "Change the baby's diapers" etc, so the question arises: how can we interact with a robot while it is running a program? We already know that we can interact with it by pressing the buttons on the brick, for example, but how can we interact with it remotely? It would be very useful to be able to interact with the robot from the PC, such as by using the PC keyboard. That is exactly what this first program does: the program running on the brick can detect, via the SSH connection between the brick and the PC, which characters on the PC keyboard are being pressed, and they are then printed to the terminal window on the PC. It's not very impressive to see the program running since the keyboard presses and the display of the characters both happen on the PC but it IS impressive to realise that these key presses are being detected by a program running on the brick, and that brick might be on the other side of the room, connected to the PC only via a wireless connection.

'Get character' Keyboard Interaction Example

This program running on the brick can detect keyboard presses on the PC, making use of the SSH connection between the PC and the brick. You can cause the program to exit at any time by pressing the 'q' key.

#!/usr/bin/env python3
# so that script can be run from Brickman

import termios, tty, sys

#==============================================

def getch():
   fd = sys.stdin.fileno()
   old_settings = termios.tcgetattr(fd)
   try:
      tty.setraw(fd)
      ch = sys.stdin.read(1)
   finally:
      termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
   
   return ch

#==============================================

while True:
   k = getch()
   print(k)
   if k == 'q':
      exit()

The above program is extracted from a longer program HERE and also lower down this page. At the present time I cannot properly explain to you how the above program works but I can assure you it DOES work for me, except that sometimes two characters are displayed on the same line in the terminal window.

At the bottom of this page I found some very similar, slightly shorter code which seems to work just as well (also sometimes printing pairs of characters):

import termios, tty, sys

#==============================================

def getch():
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    tty.setcbreak(fd)
    ch = sys.stdin.read(1)
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    
    return ch

#==============================================

while True:
   k = getch()
   print(k)
   if k == 'q':
      exit()


Keyboard Control of a robot

Now for the full program from the page that the first program was extracted from, which you should certainly visit. The program uses the same getch() function as the shorter program above to detect which PC keyboard characters have been pressed and uses certain detected characters to control a mobile robot. It is assumed that the robot's driving wheels are attached to large motors connected to ports B and C and that a medium motor is attached to port A - that medium motor could be attached to a ball-firing mechanism, for example. It should be obvious from studying the end of the program which keys are active and what each of them does. As before, pressing the 'q' key will end the program.

#!/usr/bin/env python3
# so that script can be run from Brickman

import termios, tty, sys
from ev3dev.ev3 import *

# attach large motors to ports B and C, medium motor to port A
motor_left = LargeMotor('outB')
motor_right = LargeMotor('outC')
motor_a = MediumMotor('outA')

#==============================================

def getch():
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    tty.setcbreak(fd)
    ch = sys.stdin.read(1)
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    
    return ch

#==============================================

def fire():
   motor_a.run_timed(time_sp=3000, speed_sp=600)

#==============================================

def forward():
   motor_left.run_forever(speed_sp=450)
   motor_right.run_forever(speed_sp=450)

#==============================================

def back():
   motor_left.run_forever(speed_sp=-450)
   motor_right.run_forever(speed_sp=-450)

#==============================================

def left():
   motor_left.run_forever(speed_sp=-450)
   motor_right.run_forever(speed_sp=450)

#==============================================

def right():
   motor_left.run_forever(speed_sp=450)
   motor_right.run_forever(speed_sp=-450)

#==============================================

def stop():
   motor_left.run_forever(speed_sp=0)
   motor_right.run_forever(speed_sp=0)

#==============================================

while True:
   k = getch()
   print(k)
   if k == 'w':
      forward()
   if k == 's':
      back()
   if k == 'a':
      left()
   if k == 'd':
      right()
   if k == 'f':
      fire()
   if k == ' ':
      stop()
   if k == 'q':
      exit()

Update 2 September 2016:
The 'Keyboard control' program above works correctly without errors only if I use all the declared motors before quitting the program by pressing 'q', otherwise errors are reported and sometimes the motors keep turning after the program has quit. For example, if I start the program then press 'f' to run the medium motor for 3 seconds, then press 'q' to quit the program, I get errors because the motors B and C had been declared and not used. Is there a bug? Can someone confirm my observation?
Comments