Oh look, a lame tank.


elw3

ƐʍlƎ
Joined
Aug 10, 2010
Messages
1,832
https://vid.me/uEWd

Made out of an old RC tank, an old Keyboard, 3 relays and some tape. 

Controlled by 30 Lines of python. 

Damnit how do i embed?
 
Last edited by a moderator:
Coool.


I'm at that stage where I'm interested in anything that's done with Python.


Sorry for my naive question, but what do you mean by embed?


Have the tank running python without being connected to the Pandy?


I watched a YouTube vid a couple of weeks ago on porting python to run without an operating system... Must find the link...
 
Last edited by a moderator:
Nope, its an usb keyboard. Capslock on goes left, nummlock right and so on. 
Oooh, so you wired up the caps/num/scrolllock status outputs (that powered the LEDs) to relays/servos or something instead?


That's quite an awesome idea, I shall steal it maybe... I have some old keyboards that could be put to good use :D

PS: Instead of embedding, you could also use an old wireless keyboard (if you had one) I guess? :p


The wire would be gone then at least, even if it's not completely autonomous and can't overthrow the world.
 
Last edited by a moderator:
By embedding i meant to place the video there instead the link  :rolleyes:

Yes with a wireless KB i could have run this thing like a real RC car controlled by the pandora. Pretty cool i guess, but the tank is pretty lame, i might use this method again if i get my hands on a good car. 

The full part chain consists of: Led>opto relay (with resistance)>Transistor>Flip flop relay (powered by a 9V block)>motor. The third LED is simply used for on/off.

Oh btw the tank is strong enough to carry the pandora around, so it could be used as a robot pet. I just removed the holder for the "stunt".
 
Last edited by a moderator:
This is the code, 30 lines was pretty made up tho, random good sounding number. It had 50 but i added a few lines to increase readability just now. 

Run with sudo and note that the command that sets the led is not properly working (one led is missing) in SZ, i use debian.

This is pretty fun if see it the first time, you can record blinking rythms for you keyboard led with it. 

Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Keyboard LED driven car control program by Elw3
# Y replays the whole action and saves it. B replays a saved action and START starts a fresh record.
#This script needs to run an root to set the LEDS
from subprocess import call
from time import time, sleep

#List of pandora keys in the binary reporting format you get by /dev/input
keys=[['Up', '670001', '670000'], ['Down', '6c0001', '6c0000'], ['Left', '690001', '690000'], ['Right', '6a0001', '6a0000'], ['Y', '680001', '680000'], ['B', '6b0001', '6b0000'], ['X', '6d0001', '6d0000'], ['A', '660001', '660000'], ['L', '360001', '360000'], ['R', '610001', '610000'], ['Start', '380001', '380000'], ['Select', '1d0001', '1d0000'], ['Pando', '8b0001', '8b0000']]
#This represents our keyboard leds, we need it two times to compare if the new state differs from the old. Of course we could always set it according to the keys but that is harder to record.
Leds,Leds2=['-','-','-'],['-','-','-']

class p:
 state={'Up':0,'Right':0,'Down':0,'Left':0,'A':0,'B':0,'Start':0,'Y':0,'X':0,'L':0,'R':0,'Select':0,'Pando':0}
 a,b=0,0
 repeat,repeat2=[],[]

#this is the auto replay function
def repeat():
 try:
#the first entry is always "nothing" so the time for it needs to be 0 too.   
  p.repeat[0][1]=0
 except: 
  pass
 for i in p.repeat:
  call("setleds "+ i[0][0] + "num "+ i[0][1] + "caps "+ i[0][2] + "scroll </dev/tty7", shell=True)
  sleep(i[1])  
 p.repeat2= p.repeat
 p.repeat=[]
def repeat2():
 for i in p.repeat2:
  print i
  call("setleds "+ i[0][0] + "num "+ i[0][1] + "caps "+ i[0][2] + "scroll </dev/tty7", shell=True)
  sleep(i[1])  
 
#jep i get the keys from /dev/input... whatever works is fine 
data= open("/dev/input/event4" ,"rb" )
while 1:
#since read stops the script without input the thing is only active at the very moment of a button press.  
 button=data.read(32)
 for i in keys:
#This converts the keylist into a state list. 
  if button.encode('hex_codec')[20:26] == i[2]:
   p.state[i[0]]=0
  if button.encode('hex_codec')[20:26] == i[1]:
   p.state[i[0]]=1   
 if p.state['Y']==1: 
  repeat() 
 elif p.state['B']==1: 
  repeat2()
 elif p.state['R']==1:
  Leds=['+','+','-']
 elif p.state['L']==1:
  Leds=['+','-','+']
 elif p.state['A']==1:
  Leds=['+','-','-']
 elif p.state['Right']==1:
  Leds=['-','+','-']
 elif p.state['Left']==1:
  Leds=['-','-','+']
 elif p.state['X']==1:
  Leds=['-','+','+']
 elif p.state['Start']==1:
  p.repeat=[]
 else:
  Leds=['-','-','-']
 if Leds == Leds2:
  pass
 else:
#times are actually pretty confusing  
  p.b=time()
  p.repeat.append([Leds2,p.b-p.a])
  p.a=time()
 Leds2=Leds
 print Leds
#this call sets the leds. Note the "</dev/tty7" part, setleds does not work in a terminal emulator so we need to use a real tty, and our tty when on the desktop is tty7. 
 call("setleds "+ Leds[0] + "num "+ Leds[1] + "caps "+ Leds[2] + "scroll </dev/tty7", shell=True)
 
 
You can embed videos by placing them between a media tags, works at least for youtube.

Looks like a pretty fun project. Congrats on getting this to work.

I notice that your code is not very pythonic though. If you want to improve your skills and make better use of python idioms, then check some presentations from e.g. Raymond Hettinger (see spoiler).

https://www.youtube.com/watch?v=wf-BqAjZb8M
 
Not very pythonic? you may explain this a bit. 

My usual coding style makes you probably cry then. THIS is pretty clean for me. 

And i dont really care at all how it looks...
 
> My usual coding style makes you probably cry then. THIS is pretty clean for me.

Heh, I've seen plenty of professionally written code that made me cry inside. So I'll survive :)

> And i dont really care at all how it looks...

You should. Code is read more often than written. For quick and dirty scripts this might not seem so, but it is good practice to always write maintainable code.

> Not very pythonic? you may explain this a bit.

Fair point... Hmmm...ok pythonicity, that a hard one to define precisely.

Code tends to be pythonic when it uses python idioms. I.e. uses the features of the language in an optimal way. Usually, these idioms make it both easier to read and quicker to execute.

Note, these are topics to improve your (python) coding skills. Do not do this if it stops you from doing anything at all, have fun and build stuff first. Optimize and improve second.

Some examples:

  • unpacking:
    first = a[0]
    second = a[1]
    third = a[2]
    More pythonic would be:
    Code:
    first, second, third = a
  • String concatenation using + is almost never what you want.In python a string is immutable. When you use + to concatenate strings you get quadratic performance instead of linear. For large strings or applications which make heavy use of strings this is a major loss of performance. E.g.:

    a = "foo"
    b = "bar"
    c = "baz"

    slow = a + b + c
    This translates roughly to the following pseudo-code:
    Code:
    temp: array[6] of char;
    memcopy(src=a, dst=temp[0], len=3)
    memcopy(src=b, dst=temp[3], len=3)
    slow: array[9] of char;
    memcopy(src=temp[0], dst=slow, len=6)
    memcopy(src=c, dst=slow[6], len=3)
    In other words both a and b have effectively been copied twice (due to the copy of temp). The more you concatenate strings like this the slower it gets.Better:

    fast = ''.join([a,b,c]) # the leading string is the separator
    which works roughly like this:
    Code:
    size = len(a)+len(+len(c); # 9
    fast: array[9] of char;
    memcopy(src=a, dst=slow[0], len=3)
    memcopy(src=b, dst=slow[3], len=3)
    memcopy(src=c, dst=slow[6], len=3)
    I.e. memory is allocated only once and each part is copied only once. This is linear performance.
  • Proper use of context managers for data accessYou never close /dev/input/event4, which while not a disaster is not very clean.

    A file in python is a context manager which means you can write:

    with open('/dev/input/event4', 'rb') as data:
    while 1:
    button = data.read(32)
    etc...
    While in this particular case it doesn't make much difference, it's better to get used to it.
  • General styleStyle guides aren't the holy grail of code quality, but they do exist for a reason.

    Read PEP8, the python community style guide and adopt its practices unless you have a reason

    to deviate from it: https://www.python.org/dev/peps/pep-0008/
  • class p:
    This is an old style class, these are gone in python 3. Get used to new style classes:

    class p(object):
    ...
    If you start using object-oriented techniques then old-style classes will screw you over sooner or later (e.g. their Method Resolution Order is significantly different).
    You are using the class as a global, which is not very nice and for mutable data (dicts and lists) not even needed.
  • Don't repeat yourself
    call("setleds "+ i[0][0] + "num "+ i[0][1] + "caps "+ i[0][2] + "scroll </dev/tty7", shell=True)
    sleep(i[1])
    Make a helper for this, it is an implementation detail not essential to your application logic
    Code:
    def setleds(led0, led1, led2, duration=0):
      call("setleds {0}num {1}caps {2}scroll </dev/tty7".format(led0, led1, led2), shell=True)
      sleep(duration)
    I would probably abstract even further from this. You history is just a list of repeatable commands each with a duration.
    Perhaps create a nice class or even class-hierarchy for this.

    Code:
    class CmdAction(object):
      def __init__(self, cmd, duration):
        self.cmd = cmd
        self.duration = duration
      def excecute(self):
        call(self.cmd, shell=True)
        sleep(duration)
    This simplifies your repeat and is more extensible:
    Code:
    for action in p.repeat:
      action.Execute()
 
Wow that really is some text. Thanks for that. I might reflect a bit about it and make some notes.

However problem with you suggestions  is that such style is not really obvious for someone who only uses python every few months or switches between languages often. 

In other words i dont want to get slow downs every few lines because i have to look up the proper way. 

So i doubt i really want to "improve".
 
I do!


Great post Caine. I hope to learn to program Pythonically right from the get go. :)


I imagine it'll come naturally as Python is my first language and I have nothing to unlearn... And I am taking it slow and methodically. On chapter 3 of 'Learning Python', chapter 1 of 'Python Programming for the Absolute Beginner'.
 
Back
Top