bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Multitasking, how do i solve this one?

Sun Sep 02, 2012 5:56 pm

I've written the below script to cycle 2 servo's back and forth.

Now, as python reads line by line i've been unable to make the servo's cycle independantly but similatiously. How can i overcome this?

This is my current version of code:

Code: Select all

#!/usr/bin/python

import os
import time

servo1 = 50
servo2 = 50

while 1 == 1:
        if servo1 > 49:
                   servo1 = servo1 + 2
                   os.system("echo 1=" + str(servo1) + " > /dev/servoblaster")


        if servo1 > 239:
                   servo1 = servo1 - 2
                   os.system("echo 1=" + str(servo1) + " > /dev/servoblaster")

        if servo2 > 49:
                   servo2 = servo2 + 2
                   os.system("echo 0=" + str(servo2) + " > /dev/servoblaster")


        if servo2 > 239:
                   servo2 = servo2 - 2
                   os.system("echo 0=" + str(servo2) + " > /dev/servoblaster")
My previous version used while loop's after the IF statements (slightly different IF statements to the above) which had the effect of cycling servo1 and then servo2, back to servo1 and so on...

Thanks in advance for any help, i'm picking it up slowly :)

Ry

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Sun Sep 02, 2012 6:07 pm

You have bugs in your code :). You probably want a variable that starts at '+2', which you add to the position each tine round. When you hit the upper value set the variable to '-2', when you hit the lower value, set the variable back to '+2'. Also you need a delay of some sort in the loop, so that the servos have time to move before you update their position again. When you've fixed those issues I think the servos should cycle back and fore ok.

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Sun Sep 02, 2012 6:19 pm

Yes, there were deffinately error's in my above code. It didnt work! :lol:

Thanks for the advice, it's working spot on now 8-)

Code: Select all

#!/usr/bin/python

import os
import time

servo1 = 50
servo2 = 50

servo1tick = +2
servo2tick = +2

while 1 == 1:
        if servo1tick == +2:
                   servo1 = servo1 + 2
                   os.system("echo 1=" + str(servo1) + " > /dev/servoblaster")
                   if servo1 == 240:
                        servo1tick = -2

        if servo1tick == -2:
                   servo1 = servo1 - 2
                   os.system("echo 1=" + str(servo1) + " > /dev/servoblaster")
                   if servo1 == 50:
                        servo1tick = +2

        if servo2tick == +2:
                   servo2 = servo2 + 2
                   os.system("echo 0=" + str(servo2) + " > /dev/servoblaster")
                   if servo2 == 240:
                        servo2tick = -2

        if servo2tick == -2:
                   servo2 = servo2 - 2
                   os.system("echo 0=" + str(servo2) + " > /dev/servoblaster")
                   if servo2 == 50:
                        servo2tick = +2
Although, without any added delay time this produces a smooth servo action. If i add a delay it tends to be noticable when watching the servos...

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Sun Sep 02, 2012 7:20 pm

I guess the python runs slow enough that the servos can keep up :) Either that, or the servos are going as fast as they can and maybe not getting right to position 240 (or 50) before changing direction. The code below is what I had in mind with my suggestion of a variable that toggles between +2 and -2, just in case you are interested in making your code more compact. I don't know much python though, so this may not be syntactically correct (it's also untested, but looks right to me)..

Code: Select all

servo1 = 50
servo2 = 50
inc1 = 2
inc2 = 2
while 1 == 1:
           servo1 = servo1 + inc1
           os.system("echo 1=" + str(servo1) + " > /dev/servoblaster")
           if servo1 >= 240 or servo1 <= 50:
                        inc1 = 0 - inc1
           servo2 = servo2 + inc2
           os.system("echo 1=" + str(servo2) + " > /dev/servoblaster")
           if servo2 >= 240 or servo2 <= 50:
                        inc2 = 0 - inc2

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: Multitasking, how do i solve this one?

Sun Sep 02, 2012 8:09 pm

Maybe it is not just Python that is slow but starting a shell just to ``echo`` some value into a file slows down the whole program. Python is capable of writing something into a file without starting external programs…

Code: Select all

while not self.asleep():
    sheep += 1

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Sun Sep 02, 2012 8:41 pm

@BlackJack: Good point. In any case, there is no point updating the servo position more frequently than every 20ms, as the driver only sends a pulse every 20ms for a given servo. I'd expect that loop to run faster than 20ms, even with starting a shell twice each iteration. bulldog5046 is just learning though, so I expect he's happy just to have the servos moving at this stage :) Incidentally, the kernel driver assumes a whole command string arrives in one write call; that works for echo, and would hopefully work with python direct writes too. You can probably open the device once outside the loop and then just call write inside the loop.

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 9:19 am

Thanks guys.

I'm all ears for suggestions on how to do this better. I'm new to python but have PHP experience so i'm tending to use and look for methods from there.

I guess rather then a opening a shell and echo'ing the statement i could be doing something like write file but i'm not sure how in python...

Thanks,
Ry

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 11:13 am

Code: Select all

servo = open("/dev/servoblaster","w")
servo.write(somevalue)

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 11:20 am

The file should be closed or flushed after writing to it. Closing can be done using the ``with`` statement.

Code: Select all

with open('/dev/servoblaster') as servo_file:
    servo_file.write('%d=%d\n' % (servo_number, value))

Code: Select all

while not self.asleep():
    sheep += 1

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 6:30 pm

rgh wrote:@BlackJack: Good point. In any case, there is no point updating the servo position more frequently than every 20ms, as the driver only sends a pulse every 20ms for a given servo. I'd expect that loop to run faster than 20ms, even with starting a shell twice each iteration. bulldog5046 is just learning though, so I expect he's happy just to have the servos moving at this stage :) Incidentally, the kernel driver assumes a whole command string arrives in one write call; that works for echo, and would hopefully work with python direct writes too. You can probably open the device once outside the loop and then just call write inside the loop.
@rhg - Not sure if this a bug or just my incorrect way of coding but i've written a new sequence to make my code lighter and faster, it is faster than the servo's now, infact, so fast it crashes the Pi :?

However, i was having problem getting writes to work correctly. You say writes must be in a single call but it seems the file/driver closes /dev/servoblaster after a single line is written regardless of the number of calls.

This writes a single line fine:

Code: Select all

sb=open("/dev/servoblaster","w")
sb.write("0="+str(s1)+"\n")
sb.close
But this causes python to error on sb.close():

Code: Select all

sb=open("/dev/servoblaster","w")
sb.write("0="+str(s1)+"\n1="+str(s2)+"\n")
sb.close
Is this expected functionality?

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 6:34 pm

Right, here is my revised lightweight version.

Could be lighter if i didnt need to make 2 calls to /dev/servoblaster as per my previous post.

Does this look better guys?

Code: Select all

#!/usr/bin/python

import time

s1=50
s2=50
c1=2
c2=2

while 1==1:
        s1=s1+c1
        if s1 >= 240 or s1 <= 50:
                c1=0-c1
        s2=s2+c2
        if s2 >= 240 or s2 <= 50:
                c2=0-c2
        open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
        open('/dev/servoblaster','w').write('1='+str(s2)+'\n')
        time.sleep(0.01)

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 6:51 pm

bulldog5046 wrote:But this causes python to error on sb.close():

Code: Select all

sb=open("/dev/servoblaster","w")
sb.write("0="+str(s1)+"\n1="+str(s2)+"\n")
sb.close
Is this expected functionality?
I would expect an error on the write(), not the close() there. The driver expects the string passed to write() to be of the form "0=50\n". You are sending a string that is "0=50\n1=50\n". Really the driver should recognize that and treat it as two commands (in fact, it should also cope with you writing "0=" followed by "50\n" as two separate write() calls) but it doesn't at present.

I would expect this sort of approach to work, which would mean you only have to open() once, then just write() multiple times:

Code: Select all

sb=open("/dev/servoblaster","w")
some_loop_constuct:
    sb.write("0="+str(s1)+"\n")
    sb.write("1="+str(s2)+"\n")
    etc
sb.close

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 7:24 pm

Hi,

I did actually test with 2 seperate write commands and i received the same error. I didnt use a loop though, although i don't see that it would make a difference if your example?

I think python report sb.close() as the error as the file has already been closed by the driver, rather than being a python problem.

I can see this could pose a problem with large code but for the moment it's not really an issue....

My 2 servo's have now been dressed up as a leg with hip and knee joints. Now to try and make it walk! (sort of..)

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 7:34 pm

ok; I don't know enough python to comment further really :) If you wanted to investigate further, you should play with strace. I agree it's not really a big deal for your application though. good luck with your leg!

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 7:41 pm

Thanks.

Just one last one, if you did this from shell:

Code: Select all

#echo 0=110\n1=110 > /dev/servoblaster
if that the correct syntax? servoblaster reply's that it is an invalid argument. Just wondering if the driver can't handle more than 1 line at a time?

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 7:44 pm

@bulldog5046: The error could not be on the `close` line because you never *called* the method. You just referenced it without calling it. To call something means to write at least empty parenthesis behind the object you want to call.

Code: Select all

while not self.asleep():
    sheep += 1

rgh
Posts: 212
Joined: Fri Nov 25, 2011 3:53 pm

Re: Multitasking, how do i solve this one?

Mon Sep 03, 2012 9:23 pm

bulldog5046 wrote:Thanks.

Just one last one, if you did this from shell:

Code: Select all

#echo 0=110\n1=110 > /dev/servoblaster
if that the correct syntax? servoblaster reply's that it is an invalid argument. Just wondering if the driver can't handle more than 1 line at a time?
You'd need a -e and some quotes:

Code: Select all

# echo -e '0=110\n1=110' > /dev/servoblaster
but it wouldn't work because the driver would see one write of two commands. You'd have to make to two separate calls to echo, of one command each. Maybe I should fix that behaviour..

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 7:02 am

rgh wrote: but it wouldn't work because the driver would see one write of two commands. You'd have to make to two separate calls to echo, of one command each. Maybe I should fix that behaviour..
I think that makes sense doesn't it?

to update 8 servo's with a single call rather than 8 seperate calls... :ugeek:

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 9:23 am

I don't have a servo so can't test things, but a couple of points to note.

1. I see in your python code you are doing

Code: Select all

sb.close
. This does not
close the file handle. You need to do

Code: Select all

sb.close()
2. If the driver doesn't handle multiple writes, it may require a the device to be close() explicitly to push whatever it is in the buffer to low level code. So you it would be worth changing

Code: Select all

sb=open("/dev/servoblaster","w")
some_loop_constuct:
    sb.write("0="+str(s1)+"\n")
    sb.write("1="+str(s2)+"\n")
    etc
sb.close
to

Code: Select all

some_loop_constuct:
    sb=open("/dev/servoblaster","w")
    sb.write("0="+str(s1)+"\n")
    sb.close()
    sb=open("/dev/servoblaster","w")
    sb.write("1="+str(s2)+"\n")
    sb.close()
In addition there was an example of doing

Code: Select all

open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
. If an explicit close is required, you may not be getting the semantics you are expecting as the close may not actually occur until the program exits, or when the function/method goes out of scope and gc() is called at some point in the future.

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 10:54 am

Hi Tim,
timhoffman wrote:I don't have a servo so can't test things, but a couple of points to note.

1. I see in your python code you are doing

Code: Select all

sb.close
. This does not
close the file handle. You need to do

Code: Select all

sb.close()
2. If the driver doesn't handle multiple writes, it may require a the device to be close() explicitly to push whatever it is in the buffer to low level code. So you it would be worth changing
This was a typography error, i wrote the code free-hand retrospectivly and missed the () as you've spotted. The error was present even with the correct command.
timhoffman wrote:

Code: Select all

sb=open("/dev/servoblaster","w")
some_loop_constuct:
    sb.write("0="+str(s1)+"\n")
    sb.write("1="+str(s2)+"\n")
    etc
sb.close
to

Code: Select all

some_loop_constuct:
    sb=open("/dev/servoblaster","w")
    sb.write("0="+str(s1)+"\n")
    sb.close()
    sb=open("/dev/servoblaster","w")
    sb.write("1="+str(s2)+"\n")
    sb.close()
This is what i did before i reduced it below as i wanted to compress the code.
timhoffman wrote: In addition there was an example of doing

Code: Select all

open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
. If an explicit close is required, you may not be getting the semantics you are expecting as the close may not actually occur until the program exits, or when the function/method goes out of scope and gc() is called at some point in the future.
It appears to be closing the file. I believe that is expected funtionallity when opening/writing in a single command and not opening the file to a variable?

I tried appending .close() to the end but python didnt like it.

Thanks,

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 11:16 am

From the python docs http://docs.python.org/reference/datamodel.html
CPython implementation detail: CPython currently uses a reference-counting scheme with (optional) delayed detection of cyclically linked garbage, which collects most objects as soon as they become unreachable, but is not guaranteed to collect garbage containing circular references. See the documentation of the gc module for information on controlling the collection of cyclic garbage. Other implementations act differently and CPython may change. Do not depend on immediate finalization of objects when they become unreachable (ex: always close files).
So it's probably best to close - I would use @blackjack 's recommendation of

Code: Select all

with open('/dev/servoblaster') as servo_file:
    servo_file.write('%d=%d\n' % (servo_number, value))
Or just wrap the whole open/write/close in small function that you use.

Cheers

Tim

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 12:14 pm

bulldog5046 wrote:
timhoffman wrote: In addition there was an example of doing

Code: Select all

open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
open('/dev/servoblaster','w').write('0='+str(s1)+'\n')
. If an explicit close is required, you may not be getting the semantics you are expecting as the close may not actually occur until the program exits, or when the function/method goes out of scope and gc() is called at some point in the future.
It appears to be closing the file. I believe that is expected funtionallity when opening/writing in a single command and not opening the file to a variable?

I tried appending .close() to the end but python didnt like it.
There is no difference in behaviour wether you bind intermediate results to names or not. An expression is evaluated without any knowledge what happens with the result later.

Code: Select all

a = f()
b = a.g()

# <=>

b = f().g()

# Just like

a = x + y
b = a + z

# <=>

b = x + y + z
And of course you can not append ``.close()`` at the end because the `write()` call does not return the file object but `None`. And `None` does not have a `close` attribute → `AttributeError`.

Code: Select all

while not self.asleep():
    sheep += 1

bulldog5046
Posts: 36
Joined: Thu Jun 21, 2012 1:50 pm

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 12:34 pm

I've just given the 'with' statement a shot as below:

Code: Select all

        with open(dvr,'w') as f:
                f.write('%d=%d\n'%(0,s1))
                f.write('%d=%d\n'%(1,s2))

but it has the same effect as before. It accepts the first line and rejects the second. Seemingly because the driver has forced the file to closed?

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am
Contact: Website

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 12:39 pm

@bulldog5046: I thought it was already clear that the driver can not cope with more than one line per open/write/close cycle‽ If the driver closed the file you would not be guessing about it because that would lead to a clear error message: ``ValueError: I/O operation on closed file``.

Code: Select all

while not self.asleep():
    sheep += 1

Aydan
Posts: 704
Joined: Fri Apr 13, 2012 11:48 am
Location: Germany, near Lake Constance

Re: Multitasking, how do i solve this one?

Tue Sep 04, 2012 12:42 pm

I don't know why you'd want to close the file after every call, a flush() might be sufficient.
If you need to start writing from the start of the "file" again, you can use seek().

Return to “Python”