4

I'm running a Python script that controls a robot, but I'm a bit confounded as to how to multithread the motor control function.

The issue is that the hardware is designed such that the motors won't move unless there are several sleeps in the motor-control function, because the hardware requires time to send the electrical signals to the motors. Because of these sleeps in the motor-control function, the entire program halts and stops reading in sensor data.

What I would like to do is know how to multithread/multiprocess the motor-control function once it is called, but once the program hits the call again in the following iteration of the loop, it checks if the motor-control is still running (i.e. it isn't done with the sleep). If it is still running, it simply skips over the motor-control call and continues on with looping, reading sensor data, and then checking again if the motor-control function is still running. Of course, if the motor-control function is no longer running, I would like for it to once again be called.

Basically, the whole program only requires two threads: one that runs the main program, and one that branches off and continuously re-runs one instance of the motor-control function every time the motor-control function has completed its execution.

I had tried using the concurrent.futures import but got messages saying it was not supported and I couldn't find any usages particular to the way I intend to use it.

Gabriel Garrett
  • 2,087
  • 6
  • 27
  • 45
  • 1
    If you have a function which does a little processing and a lot of sleeping, then I should think that `threading.Thread` should suffice. – quamrana Aug 15 '15 at 17:49
  • Which version of python are you using? And which versions could you use? – quamrana Aug 15 '15 at 17:52
  • But how do I ensure that only one instance of that function ever exists? The function is called at the end of each loop iteration, meaning if I use the threading as it is, it would create several threads (I think?) of that function. Furthermore, if a second call to the motor-control function occurs while another motor-control call is in progress, it will circumvent the original's sleep while the signal is being sent to the motors and the robot won't move at all. The Python version is 2.7. I might be able to use 3.3 but I know this robotics framework only definitely supports 2.7. – Gabriel Garrett Aug 15 '15 at 17:57
  • 1
    @zavtra You can check if the current motor control thread is still running using `Thread.is_alive()`. – augurar Aug 15 '15 at 18:17
  • You need to consider that you create a thread which should call your function, which itself should loop round forever. Different threads each call different functions. – quamrana Aug 15 '15 at 18:18
  • "The function is called at the end of each loop iteration" -- You can have a loop and call function inside a thread... – Patrick Maupin Aug 16 '15 at 03:58

1 Answers1

1

I think you don't need threading, but I may misunderstand your requirements so I will present 2 alternatives.

  1. Without threading and sleeps

Assuming your current program flows like that :

while True:
    data = read_sensors()
    command = process(data)
    motor_do(command)
    time.sleep(delay)

Then you could just remove the sleep, and only call motor_do if the last call was at least delay seconds ago.

last_call_time = -delay # to be sure that motor_do can be called the first time
# "On Windows, [time.clock] returns wall-clock seconds elapsed since the first call to this
# function, as a floating point number, based on the Win32 function QueryPerformanceCounter()
# The resolution is typically better than one microsecond." (python 2.7 doc)
# i.e. close to 0 on first call of time.clock()

while True:
    data = read_sensors()
    command = process(data)
    motor_try(command)

def motor_try(command):
    global last_call_time

    current_time = time.clock()
    # on win that works, on unix... you may want to take a look at the NB

    elapsed = current_time - last_call_time
    if elapsed >= delay:
        motor_do(command)
        last_call_time = current_time
  1. With threading (this is an example, I have no experience in threading/async with python 2.7 so there may be better ways to do this)

Assuming your current program flows like that :

while True:
    data = read_sensors()
    command = process(data)
    motor_do(command) // this function sleeps and you CANNOT change it

Then you have to launch 1 thread that will only push the commands to the motor, asynchronously.

import thread

command = None
command_lock = thread.allocate_lock()

def main():
    thread.start_new_thread(motor_thread)

    global command
    while True:
        data = read_sensors()
        with command_lock:
            command = process(data)

def motor_thread():
    while True:
        while not command: # just to be sure
            time.sleep(0.01)
            # small delay here (for instance, the time consumed by read_sensors + process)
        with command_lock:
            motor_do(command)
            command = None
        time.sleep(delay)

NB : on Unix, time.clock() returns processor time (= without the idling time), so it would be better to use time.time()... unless the system clock is changed : "While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls." (python 2.7 doc)

I don't know how time.sleep() react to system clock changes.

see How do I get monotonic time durations in python? for precise time delays on unix/py2.7 (and Understanding time.perf_counter() and time.process_time() can be useful)

Python3 : just use time.monotonic()... or time.perf_counter().

Community
  • 1
  • 1
artemisart
  • 190
  • 1
  • 7