How to make a timer more efficient - python

I am quite new to coding and i am wondering how to make it more efficient. I am running it on a raspberry pi which will have other tasks so i want this to be as easy to run as possible. The code will use a magnetic sensor to record passes made by a magnet mounted on a wheel and from there determine the speed of the outside diameter of the wheel. It would be useful to implement something that takes the five last speed outputs and gives sort of an average but only if it does not affect the complexity of the code much. Would be really greatful for any help!
from gpiozero import Button
import time
global t0
t0 = time.clock()
raduis = 300
button = Button (21)
from signal import pause
def calculate_speed(radius):
global t0
t1 = time.clock
interval = t1 - t0
speed = radius/interval
print (speed, 'mm/sek')
y = True
while y == True:
button.when_pressed = calculate_speed(radius)
time.sleep(0.2)
#used to prevent one pass of the magnet from recording many passes

You should store five last speed outputs in an array (list) and then you can calculate the average speed
speed_records = []
def calculate_speed(radius):
global t0
t1 = time.clock
interval = t1 - t0
speed = radius/interval
print (speed, 'mm/sek')
speed_records.append(speed) # Adds one speed record to the list
if len(speed_records) >= 5: # checks if there are 5 five records available
last_five_records = speed_records[-5:] # Seperates five last records
average = sum(last_five_records) / 5
print('Average Speed:',average) # Prints out the average
if len(speed_records) > 10: # Free Up some memory
speed_records = list(set(speed_records) - set(speed_records[:5])) #removes the first five records

The following code uses modular arithmetic to iterate through a single list, adding and overwriting values, and printing the averaged speed. Adjust iterations to control how many passes to average over.
from gpiozero import Button
from signal import pause
import time
radius = 300
button = Button (21)
iterations = 5
speeds = [0] * iterations
speed_idx = 0
def calculate_speed(radius):
global speeds, speed_idx
t1 = time.time()
speeds[speed_idx] = radius / (t1- t0)
print (sum(speeds) / iterations, 'mm/sek')
speed_idx += 1
speed_idx %= iterations
t0 = time.time()
while True:
button.when_pressed = calculate_speed(radius)
time.sleep(0.2)
t0 = time.time()
Note this takes 5 measurements to "ramp up" in a sense. If you want- you could add an if statement to avoid printing out the first 4 recordings.
Additionally, if you wanted a smoother measurement of speeds, it occurred to me that you could use a single value to hold the sum of the speeds of the last N passes, and each time subtract off the average (assuming N sums), and add the new speed. It would a few extra passes to stabilize, but afterwards it should smooth the reported speeds a bit.

Related

Parallelize slow functions that needs to be run only every X iterations in order to not slow the loop

The project
I am conducting a project where I need to both detect faces (bounding boxes and landmarks) and perform face recognition (identify a face). The detection is really fast (it takes not even a few milliseconds on my laptop) but the recognition can be really slow (about 0.4 seconds on my laptop). I am using the face_recognition Python library to do so. After a few tests, I discovered that it is the embedding of the image that is slow.
Here is an example code to try it out for yourself :
# Source : https://pypi.org/project/face-recognition/
import face_recognition
known_image = face_recognition.load_image_file("biden.jpg")
biden_encoding = face_recognition.face_encodings(known_image)[0]
image = face_recognition.load_image_file("your_file.jpg")
face_locations = face_recognition.face_locations(image)
face_landmarks_list = face_recognition.face_landmarks(image)
unknown_encoding = face_recognition.face_encodings(image)[0]
results = face_recognition.compare_faces([biden_encoding], unknown_encoding)
The problem
What I need to do is to process a video (30 FPS), therefore 0.4s of computation is unacceptable. The idea that I have is that the recognition will only need to be run a few times and not every frame since from one frame to another, if there are no cuts in the video, a given head will be close to its previous position. Therefore, the first time the head appears, we run the recognition which is very slow but then for the next X frames, we won't have to since we'll detect that the position is close to the previous one, therefore it must be the same person that moved. Of course, this approach is not perfect but seems to be a good compromise and I would like to try it.
The only problem is that by doing so the video is smooth until a head appears, then the video freezes because of the recognition and then becomes smooth again. This is where I would like to introduce multiprocessing, I would like to be able to compute the recognition in parallel of looping through the frame of the video. If I manage to do so, I will then only have to process a few frames in advance so that when a face shows up it already computed its recognition a few seconds ago during several frames so that we did not see a reduced frame rate.
Simple formulation
Therefore here is what I have (in python pseudo code so that it is clearer):
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
person_name = slow_function(image)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
And I would like to do something like this :
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
DO slow_function(image) IN parallel WITH CALLBACK(person_name = result)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
The goal is to compute a slow function over several iterations of a loop.
What I have tried
I looked up multiprocessing and Ray but I did not find examples of what I wanted to do. Most of the time I found people using multiprocessing to compute at the same time the result of a function for different inputs. This is not what I want. I want to have in parallel a loop and a process that accepts data from the loop (a frame), do some computation, and returns a value to the loop without interrupting or slowing down the loop (or at least, spreading the slow down rather than having one really slow iteration and 9 fast ones).
I think I found pretty much how to do what I want. Here is an example:
from multiprocessing import Pool
import time
# This seems to me more precise than time.sleep()
def sleep(duration, get_now=time.perf_counter):
now = get_now()
end = now + duration
while now < end:
now = get_now()
def myfunc(x):
time.sleep(1)
return x
def mycallback(x):
print('Callback for i = {}'.format(x))
if __name__ == '__main__':
pool=Pool()
# Approx of 5s in total
# Without parallelization, this should take 15s
t0 = time.time()
titer = time.time()
for i in range(100):
if i% 10 == 0: pool.apply_async(myfunc, (i,), callback=mycallback)
sleep(0.05) # 50ms
print("- i =", i, "/ Time iteration:", 1000*(time.time()-titer), "ms")
titer = time.time()
print("\n\nTotal time:", (time.time()-t0), "s")
t0 = time.time()
for i in range(100):
sleep(0.05)
print("\n\nBenchmark sleep time time:", 10*(time.time()-t0), "ms")
Of course, I will need to add flags so that I do not write a value with the callback at the same time that I read it in the loop.

Python - How to Increment A Value As Time Passes [duplicate]

This question already has answers here:
How to create a python loop that allows other code to run as well
(3 answers)
Closed 1 year ago.
I'm trying to do something simple here, i want to increase my cost variable which is (100.00) by .01 every 5 minutes, so after every 5 minutes my new value/variable should be, 100.01, then 100.02, then 100.03 and so on..
but i can't figure out how to add it to my existing value.
Here is what i've tried so far, i've reduced the 300 seconds to 10 seconds to speed things up.
import time
import datetime
tracking = time.time()
def values():
global tracking
now = datetime.datetime.now()
cost = 100.00
increase = .01
newvalue = []
for x in range(1,1000):
print(x)
time.sleep(2)
if time.time() - 10 > tracking:
newvalue.append(float(increase))
print(newvalue)
print(now)
tracking = time.time()
values()
any help appreciated.
It seems like you want the += operator. This adds the right-hand value to the value to the value stored in the left-hand variable.
import time
import datetime
tracking = time.time()
def values():
global tracking
now = datetime.datetime.now()
cost = 100.00
increase = .01
for x in range(1,1000):
print(x)
time.sleep(2)
if time.time() - 10 > tracking:
cost += increase
print('Cost: {}'.format(cost))
print(now)
tracking = time.time()
values()
There are other issues to consider:
Floats are not a good way to store currency values. Because they store binary fractions, they cannot represent all decimal fractions. Consider using decimal.Decimal instead.
To track elapsed time, it's best to use time.monotonic. datetime.now() can be adjusted externally (e.g. by ntpdate), so you cannot assume a change in datetime.now represents elapsed time.
There doesn't seem to be a need to store tracking as a global.
In a single-threaded program, values will not exit until 1000 is reached, which means you can't have other program logic running while the cost is incrementing. Calculating the cost on-demand (as others have suggested) will allow you to run other program logic instead of just looping.
Try this
import time
def increase_value_evrey_t_sec(initail_value, interval, increase_by,stop_after = -1):
counter = 0
values = []
while counter < stop_after or stop_after == -1:
time.sleep(interval)
initail_value += increase_by
print(initail_value)
values.append(initail_value)
counter += 1
increase_value_evrey_t_sec(2,2,3,4)
increase_value_evrey_t_sec(2,2,3,4)

Forecast the remaining time of a loop

Good Evening,
I am trying to estimate the remaining time to the end of a loop; I've used:
start = datetime.now()
progress = 0
for i in range(1000):
#do a few calculations
progress += 1
stop = datetime.now()
execution_time = stop-start
remaining = execution_time * ( 1000 - progress )
print("Progress:", progress, "%, estimated", remaining, "time remaining")
But it does not seem to work properly, since it goes up to minutes, even though the loop would take 20 seconds in total, and decrease quickly when reaching the end.
How can I try to forecast the remaining time of a loop efficiently and correctly?
Simply use tqdm package:
from tqdm import tqdm
for i in tqdm(range(10000)):
dosomthing()
It will print everything for you:
76%|█████████████ | 7568/10000 [00:33<00:10, 229.00it/s]
Rather than using datetime.datetime.now() for this sort of thing you can use time.perf_counter(), which is available in Python 3.3+. From the docs:
Return the value (in fractional seconds) of a performance counter,
i.e. a clock with the highest available resolution to measure a short
duration. It does include time elapsed during sleep and is
system-wide. The reference point of the returned value is undefined,
so that only the difference between the results of consecutive calls
is valid.
Also, you can print using a carriage return instead of a newline so that the progress reports are printed on a single line. Here's a brief demo derived from your code.
from time import sleep, perf_counter
fmt = " Progress: {:>3}% estimated {:>3}s remaining"
num = 1000
start = perf_counter()
for i in range(1, num + 1):
# Simulate doing a few calculations
sleep(0.01)
stop = perf_counter()
remaining = round((stop - start) * (num / i - 1))
print(fmt.format(100 * i // num, remaining), end='\r')
print()
Depending on your terminal (and Python version) you may also need to add the flush=True keyword arg to the print call in order to get the progress reports to print as they are issued.
I think that in this line:
remaining = execution_time * ( 1000 - progress )
you should divide execution_time/progress, because you want to know how long it takes to complete one percent of progress.
remaining = execution_time/progress * ( 1000 - progress )
Your calculation for time remaining is wrong. If it takes execution_time for progress steps. Then how much does it take for 1000 steps ?
Simple cross multiply gives you the total time. Subtract it from the time already elapsed and that will give you the time remaining.
remaining_time = execution_time * 1000 / progress - execution_time
percent_complete = (progress / 1000) * 100 #You can simplify this if you like
print("Progress:", percent_complete , "%, Estimated", remaining_time, "time remaining")
Also your variable execution_time_1 is never defined

Python - creating a delay that takes in account the execution time

i am writing a function that reads N (800*600) frames from a bin file in one sec. the FPS is user controllable
import time
def getFrame(file,N): #read N frames per second, 800X600 pixels each from file
start = time.time()
delay = (1/float(N))
while N:
frame = file.read(480000)
time.sleep(delay)
N -= 1
print time.time()-start
return
the time.sleep() creates the delay, but without taking in account the execution time of the rest of the code.
so in total the running time is always bigger then 1 sec and it increments as i increase my N (fps) value.
is there a way to create a delay that takes into account the processing time ?
ummm .... you need basic math here
time.sleep(delay-(time.time()-start))

What's the proper way to write a game loop in Python?

I'm trying to write a python game loop that hopefully takes into account FPS. What is the correct way to call the loop? Some of the possibilities I've considered are below. I'm trying not to use a library like pygame.
1.
while True:
mainLoop()
2.
def mainLoop():
# run some game code
time.sleep(Interval)
mainLoop()
3.
def mainLoop():
# run some game code
threading.timer(Interval, mainLoop).start()
4.
Use sched.scheduler?
If I understood correctly you want to base your game logic on a time delta.
Try getting a time delta between every frame and then have your objects move with respect to that time delta.
import time
while True:
# dt is the time delta in seconds (float).
currentTime = time.time()
dt = currentTime - lastFrameTime
lastFrameTime = currentTime
game_logic(dt)
def game_logic(dt):
# Where speed might be a vector. E.g speed.x = 1 means
# you will move by 1 unit per second on x's direction.
plane.position += speed * dt;
If you also want to limit your frames per second, an easy way would be sleeping the appropriate amount of time after every update.
FPS = 60
while True:
sleepTime = 1./FPS - (currentTime - lastFrameTime)
if sleepTime > 0:
time.sleep(sleepTime)
Be aware thought that this will only work if your hardware is more than fast enough for your game. For more information about game loops check this.
PS) Sorry for the Javaish variable names... Just took a break from some Java coding.

Categories

Resources