Calculating revolutions per minute in Python from an Arduino - python

I'm trying to calculate rpm's in python via a reed switch from the arduino.
i cant seem to get it right. can some one please help me get the code right.
my results are not consistent.
my problem is I'm trying to count the revolution per minute but I'm having issues with the minute part. the revolution counter works OK every time the reed switch is passed the count increases 1,2,3,4,5,6(etc). but, the rpm part witch is rev / ((time.time() - t)/60) gives me a result that keeps increases the value does not increase or decrease fast enough according to the speed of the revolution. so when when the reed switch is passed at 30 rev per minute it should reed out 30 RPM; when when the reed switch is passed at 90 rev per minute it should reed out 90 RPM. i know that i need to get a count of revolution for every minute, i guess its the ever minute part that i having a hard time programing.
thanks for helping me out this has been bothering me for months now.
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
t = time.time()
a = ser.read()
rev = 0
while (a == ser.read()):
rev += 1
print rev / ((time.time() - t)/60)
New problem, now I'm trying to add two reed switches. i can get both to work separately, but when i go to pass the second reed switch i get script terminated. i now why I'm getting the script termination( one if true other is false terminate), but I'm not sure how to code it properly. the b'1' and b'2' is what is coming in from the arduino
my goal is to get both reeds to read at the same time.
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
a = ser.read()
t0 = time.time()
while(a == ser.read()):
if a == b'2':
t1 = time.time()
try:
print(1 / ((t1-t0)/60))
except ZeroDivisionError:
pass
t0 = t1
if a == b'1':
t1 = time.time()
try:
print(1 / ((t1-t0)/60))
except ZeroDivisionError:
pass
t0 = t1
my none working code so you can get an idea of what I'm trying to do. if i could somehow break out the b'1' and b'2'. down below in the font object part you will see were m and r will need to go
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
import pygame, sys
from pygame.locals import *
t0 = time.time()
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('MPH!')
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLUE = (148, 228, 255)
BLACK = (0, 0, 0)
while True:
a = ser.read()
if a == b'2':
t1 = time.time()
try:
rpmm = (1 / ((t1-t0)/60))
m = str((7.065 *rpmm * 60)/5280).strip("\n,\r")
except ZeroDivisionError:
pass
t0 = t1
if a == b'1':
t1 = time.time()
try:
r = str((1 / ((t1-t0)/60))).strip("\n,\r")
except ZeroDivisionError:
pass
t0 = t1
fontObj = pygame.font.SysFont('digitaldream', 40)
textSurfaceObj = fontObj.render('MPH:'+m, True, BLUE, BLACK)
textRectObj = textSurfaceObj.get_rect()
textRectObj.center = (200, 150)
fontObj2 = pygame.font.SysFont('digitaldream', 40)
textSurfaceObj2 = fontObj2.render('RPM:'+r, True, BLUE, BLACK)
textRectObj2 = textSurfaceObj2.get_rect()
textRectObj2.center = (200, 200)
DISPLAYSURF.fill(BLACK)
DISPLAYSURF.blit(textSurfaceObj, textRectObj)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()

For now, your code will output the average RPM since the reading started. If ser.read() returns one time for every rotation, you can do something like this to get the instantaneous rotation speed:
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
a = ser.read()
t0 = time.time()
while(a == ser.read()):
t1 = time.time()
try:
print(1 / ((t1-t0)/60))
except ZeroDivisionError:
pass
t0 = t1
If you take a look at your new code, you should realize that the variable a is not assigned any new value during the while loop. You only check if ser.read() is equal to the value it had before you entered the for loop. This means that if ser.read() takes another value than what it had prior to entering the while loop, the statement a == ser.read() will be False.
This code will work for both values of ser.read().
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
t0 = time.time()
while True:
a = ser.read()
if a == b'2' or a == b'1':
t1 = time.time()
try:
print(1 / ((t1-t0)/60))
except ZeroDivisionError:
pass
t0 = t1
else:
break

You may get a division by zero if your first read is too fast.

Related

Lidar and Servo on Raspberry pi 4

I've two separate code bases for Tf-mini(Lidar) and servo motor. I want the both code to run at the same time. I tried to combine them in to a single code, But only one code runs at a time. Please help to rewrite or edit the code.
Code for servo motor here:
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.OUT)
p = GPIO.PWM(7, 100)
t = 0.007
r = 20
p.start(r)
while True:
for i in range(5,r):
p.ChangeDutyCycle(i)
print(i)
time.sleep(t)
for i in range(r,5,-1):
p.ChangeDutyCycle(i)
print(i)
time.sleep(t)`
Code for lidar here:
import time
import serial
ser = serial.Serial("/dev/ttyAMA0", 115200)
def read_data():
while True:
counter = ser.in_waiting
if counter > 8:
bytes_serial = ser.read(9)
ser.reset_input_buffer()
if bytes_serial[0] == 0x59 and bytes_serial[1] == 0x59: # this portion is for python3
print("Printing python3 portion")
distance = bytes_serial[2] + bytes_serial[3]*256 # multiplied by 256, because the binary
data is shifted by 8 to the left (equivalent to "<< 8").
# Dist_L, could simply be added resulting in 16-bit data of Dist_Total.
strength = bytes_serial[4] + bytes_serial[5]*256
temperature = bytes_serial[6] + bytes_serial[7]*256
temperature = (temperature/8) - 256
print("Distance:"+ str(distance))
print("Strength:" + str(strength))
if temperature != 0:
print("Temperature:" + str(temperature))
ser.reset_input_buffer()
if bytes_serial[0] == "Y" and bytes_serial[1] == "Y":
distL = int(bytes_serial[2].encode("hex"), 16)
distH = int(bytes_serial[3].encode("hex"), 16)
stL = int(bytes_serial[4].encode("hex"), 16)
stH = int(bytes_serial[5].encode("hex"), 16)
distance = distL + distH*256
strength = stL + stH*256
tempL = int(bytes_serial[6].encode("hex"), 16)
tempH = int(bytes_serial[7].encode("hex"), 16)
temperature = tempL + tempH*256
temperature = (temperature/8) - 256
print("Printing python2 portion")
print("Distance:"+ str(distance) + "\n")
print("Strength:" + str(strength) + "\n")
print("Temperature:" + str(temperature) + "\n")
ser.reset_input_buffer()
if __name__ == "__main__":
try:
if ser.isOpen() == False:
ser.open()
read_data()
except KeyboardInterrupt(): # ctrl + c in terminal.
if ser != None:
ser.close()
print("program interrupted by the user")
Please help me to combine these two code to run at the same time.
I think I see your problem. Both segments of code are sequentially driven and hence it will create problems. Here is what you need to do (pseudo code):
loop forever:
t1=start_time
change duty cycle #remove the wait code
poll the serial port #the ladar code
t2=end_time
if t2 - t1 < t:
wait (t - (t2-t1))
Being an old Arduino fan myself, I see the struggles of handling the servo motor. I have a simple library to do most of the dirty work for me. You are most welcome to use my library via any of the two links:
https://github.com/vikramdayal/RaspberryMotors or https://pypi.org/project/RaspberryMotors/#description

I am trying to sample at 100hz instead of just as quick as the program will run. How would I do that?

I have a program in which I am just printing to a csv and I want exactly 100 sample points every second but I have no clue where to start with this or how to do it!!! Please help!
from datetime import datetime
import pandas as pd
i = 0
data = []
filename = 'Data.csv'
hz = 0
count = 0
while True:
#start = process_time()
if i == 0:
Emptydf = pd.DataFrame([], columns = ['COUNT', 'TIME'])
(Emptydf).to_csv('Data.csv', index = False)
curr_time = datetime.now()
str_milli = curr_time.strftime("%f")[:2]
milliseconds = int(str_milli)
timestamp = curr_time.strftime("%H:%M:%S.%f")
datarow = {'Count': i, 'TIME' : timestamp}
#diff = curr_time - past time of 0.01 milli seconds
#if diff >= 0.01:
data.append(datarow)
#time.sleep(.006)
if i%10 == 0:
dataframe = pd.DataFrame(data)
(dataframe).to_csv('Data.csv', mode = 'a', header = False, index = False)
#print(dataframe)
data.clear()
i += 1
Here is an example that increments a counter 100 times per second:
import time
FREQ_HZ = 100.
count = 0
start_time = time.time()
try:
while True:
count += 1
time.sleep(count / FREQ_HZ - (time.time() - start_time))
except:
print("%.2f iter/second\n" % (count / (time.time() - start_time)))
To test, let it run for a bit and then hit ^C.
Basically, what you do is the following;
import time
cycletime = 0.01 # seconds
while True:
start = time.monotonic()
# << Do whatever you need to do here. >>
delta = time.monotonic() - start
if delta < cycletime: # Did we finish in time?
time.sleep(cycletime - delta) # Sleep the rest of the time.
else:
print('WARNING: cycle too long!')
Note that for such applications time.monotonic is preferred over time.time because the latter can decrease when the system clock is changed.

Calculating the amount of time left until completion

I am wondering how to calculate the amount of time it would take to example:
Complete a brute force word list.
I know how to use the time function and measure in time,
but the problem is i need to find out how long it would take in the program itself...
Here is the code i made this yesterday
import itertools, math
import os
Alphabet = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890") # Add or remove whatevs you think will be in the password you're cracking (example, [symbols])
counter = 1
CharLength = 1
range_num = int(raw_input("Enter range: "))
stopper = range_num + 1
filename = "bruteforce_%r.txt" % (range_num)
f = open(filename, 'a')
#n_1 = len(Alphabet)
#n_2 = n_1 - 1 # <-- total useless peice of garbage that could of been great in vurtual life
#n_3 = '0' * n_2
#n = '1' + n_3
x = range_num
y = len(Alphabet)
amount = math.pow(y, x)
total_items = math.pow(y, x)
for CharLength in range(range_num, stopper):
passwords = (itertools.product(Alphabet, repeat = CharLength))
for i in passwords:
counter += 1
percentage = (counter / total_items) * 100
amount -= 1
i = str(i)
i = i.replace("[", "")
i = i.replace("]", "")
i = i.replace("'", "")
i = i.replace(" ", "")
i = i.replace(",", "")
i = i.replace("(", "")
i = i.replace(")", "")
f.write(i)
f.write('\n')
print "Password: %r\tPercentage: %r/100\tAmount left: %r" % (i, int(percentage), amount)
if i == '0'* range_num:
print "*Done"
f.close()
exit(0)
else:
pass
This is my timer function i managed to make
#import winsound # Comment this out if your using linux
import os
import time
from sys import exit
print "This is the timer\nHit CTRL-C to stop the timer\nOtherwise just let it rip untill the time's up"
hours = int(raw_input('Enter the hours.\n>>> '))
os.system('clear') # Linux
#os.system('cls') # Windows
minutes = int(raw_input('Enter the minutes.\n>>> '))
os.system('clear') # linux
#os.system('cls') # Windows
seconds = int(raw_input('Enter the seconds.\n>>> '))
os.system('clear') # Linux
#os.system('cls') # Windows
stop_time = '%r:%r:%r' % (hours, minutes, seconds)
t_hours = 00
t_minutes = 00
t_seconds = 00
while t_seconds <= 60:
try:
os.system('clear') # Linux
#os.system('cls') # Windows
current_time = '%r:%r:%r' % (t_hours, t_minutes, t_seconds)
print current_time
time.sleep(1)
t_seconds+=1
if current_time == stop_time:
print "// Done"
#winsound.Beep(500,1000)
#winsound.Beep(400,1000)
break
elif t_seconds == 60:
t_minutes+=1
t_seconds=0
elif t_minutes == 60:
t_hours+=1
t_minutes = 00
except KeyboardInterrupt:
print "Stopped at: %r:%r:%r" % (t_hours, t_minutes, t_seconds)
raw_input("Hit enter to continue\nHit CTRL-C to end")
try:
pass
except KeyboardInterrupt:
exit(0)
Now i just cant figure out how to make this again but to calculate how long it will take rather than how long it is taking...
You cannot predict the time a script is going to take.
Firstly because two machines wouldn't run the script in the same time, and secondly, because the execution time on one machine can vary from on take to another.
What you can do, however, is compute the percentage of execution.
You need to figure out, for example, how many iterations your main loop will do, and calculate at each iteration the ratio current iteration count / total number of iterations.
Here is a minimalist example of what you can do:
n = 10000
for i in range(n):
print("Processing file {} ({}%)".format(i, 100*i//n))
process_file(i)
You can take it further and add the time as an additional info:
n = 10000
t0 = time.time()
for i in range(n):
t1 = time.time()
print("Processing file {} ({}%)".format(i, 100*i//n), end="")
process_file(i)
t2 = time.time()
print(" {}s (total: {}s)".format(t2-t1, t2-t0))
The output will look like this:
...
Processing file 2597 (25%) 0.2s (total: 519.4s)
Processing file 2598 (25%) 0.3s (total: 519.7s)
Processing file 2599 (25%) 0.1s (total: 519.8s)
Processing file 2600 (25%)
This is my implementation, which returns time elapsed, time left, and finish time in H:M:S format.
def calcProcessTime(starttime, cur_iter, max_iter):
telapsed = time.time() - starttime
testimated = (telapsed/cur_iter)*(max_iter)
finishtime = starttime + testimated
finishtime = dt.datetime.fromtimestamp(finishtime).strftime("%H:%M:%S") # in time
lefttime = testimated-telapsed # in seconds
return (int(telapsed), int(lefttime), finishtime)
Example:
import time
import datetime as dt
start = time.time()
cur_iter = 0
max_iter = 10
for i in range(max_iter):
time.sleep(5)
cur_iter += 1
prstime = calcProcessTime(start,cur_iter ,max_iter)
print("time elapsed: %s(s), time left: %s(s), estimated finish time: %s"%prstime)
Output:
time elapsed: 5(s), time left: 45(s), estimated finish time: 14:28:18
time elapsed: 10(s), time left: 40(s), estimated finish time: 14:28:18
time elapsed: 15(s), time left: 35(s), estimated finish time: 14:28:18
....
You will never ever be able to know exactly how long it is going to take to finish. The best you can do is calculate was percentage of the work you have finished and how long that has taken you and then project that out.
For example if you are doing some work on the range of numbers from 1 to 100 you could do something such as
start_time = get the current time
for i in range(1, 101):
# Do some work
current_time = get the current time
elapsed_time = current_time - start_time
time_left = 100 * elapsed_time / i - elapsed_time
print(time_left)
Please understand that the above is largely pseudo-code
The following function will calculate the remaining time:
last_times = []
def get_remaining_time(i, total, time):
last_times.append(time)
len_last_t = len(last_times)
if len_last_t > 5:
last_times.pop(0)
mean_t = sum(last_times) // len_last_t
remain_s_tot = mean_t * (total - i + 1)
remain_m = remain_s_tot // 60
remain_s = remain_s_tot % 60
return f"{remain_m}m{remain_s}s"
The parameters are:
i : The current iteration
total : the total number of iterations
time : the duration of the last iteration
It uses the average time taken by the last 5 iterations to calculate the remaining time. You can the use it in your code as follows:
last_t = 0
iterations = range(1,1000)
for i in iterations:
t = time.time()
# Do your task here
last_t = time.time() - t
get_remaining_time(i, len(iterations), last_t)

fps - how to divide count by time function to determine fps

I have a counter working that counts every frame. what I want to do is divide this by time to determine the FPS of my program. But I'm not sure how to perform operations on timing functions within python.
I've tried initializing time as
fps_time = time.time
fps_time = float(time.time)
fps_time = np.float(time.time)
fps_time = time()
Then for calculating the fps,
FPS = (counter / fps_time)
FPS = float(counter / fps_time)
FPS = float(counter (fps_time))
But errors I'm getting are object is not callable or unsupported operand for /: 'int' and 'buildin functions'
thanks in advance for the help!
Here is a very simple way to print your program's frame rate at each frame (no counter needed) :
import time
while True:
start_time = time.time() # start time of the loop
########################
# your fancy code here #
########################
print("FPS: ", 1.0 / (time.time() - start_time)) # FPS = 1 / time to process loop
If you want the average frame rate over x seconds, you can do like so (counter needed) :
import time
start_time = time.time()
x = 1 # displays the frame rate every 1 second
counter = 0
while True:
########################
# your fancy code here #
########################
counter+=1
if (time.time() - start_time) > x :
print("FPS: ", counter / (time.time() - start_time))
counter = 0
start_time = time.time()
Hope it helps!
Works like a charm
import time
import collections
class FPS:
def __init__(self,avarageof=50):
self.frametimestamps = collections.deque(maxlen=avarageof)
def __call__(self):
self.frametimestamps.append(time.time())
if(len(self.frametimestamps) > 1):
return len(self.frametimestamps)/(self.frametimestamps[-1]-self.frametimestamps[0])
else:
return 0.0
fps = FPS()
for i in range(100):
time.sleep(0.1)
print(fps())
Make sure fps is called once per frame
You might want to do something in this taste:
def program():
start_time = time.time() #record start time of program
frame_counter = 0
# random logic
for i in range(0, 100):
for j in range(0, 100):
# do stuff that renders a new frame
frame_counter += 1 # count frame
end_time = time.time() #record end time of program
fps = frame_counter / float(end_time - start_time)
Of course you don't have to wait the end of the program to compute end_time and fps, you can do it every now and then to report the FPS as the program runs. Re-initing start_time after reporting the current FPS estimation could also help with reporting a more precise FPS estimation.
This sample code of finding FPS. I have used it for pre, inference, and postprocessing. Hope it helps!
import time
...
dt, tt, num_im = [0.0, 0.0, 0.0], 0.0, 0
for image in images:
num_im += 1
t1 = time.time()
# task1....
t2 = time.time()
dt[0] += t2 - t1
# task2...
t3 = time.time()
dt[1] += t3 - t2
# task3...
dt[2] += time.time() - t3
tt += time.time() - t1
t = tuple(x / num_im * 1E3 for x in dt)
print(f'task1 {t[0]:.2f}ms, task2 {t[1]:.2f}ms, task3 {t[2]:.2f}ms, FPS {num_im / tt:.2f}')
from time import sleep,time
fps = 0
fps_count = 0
start_time = time()
while True:
if (time()-start_time) > 1:
fps = fps_count
fps_count = 1
start_time = time()
else:
fps_count += 1
print("FPS:",fps)
FPS = the number of cycles running per second

(Almost Done) Python program that closes stream when below audio threshold

I have already made a program that prints the max amplitude or volume, then I made a program that only prints the volume when the threshold is above 2300, now I want to make a program that closes the stream when the threshold is below 2300 for 2 seconds, so not right after it but in 2 seconds only if the threshold hasn't raised again within 2 seconds. Here is what I have:
import pyaudio
import struct
import audioop
import time
INITIAL_THRESHOLD = 0.010
FORMAT = pyaudio.paInt16
SHORT_NORMALIZE = (1.0/32768.0)
CHANNELS = 2
RATE = 44100
INPUT_BLOCK_TIME = 0.05
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME)
OVERSENSITIVE = 15.0/INPUT_BLOCK_TIME
UNDERSENSITIVE = 120.0/INPUT_BLOCK_TIME
MAX_BLOCKS = 0.15/INPUT_BLOCK_TIME
class TEST(object):
def __init__(self):
self.pa = pyaudio.PyAudio()
self.stream = self.open_mic_stream()
self.tap_threshold = INITIAL_THRESHOLD
self.noisycount = MAX_BLOCKS+1
self.quietcount = 0
self.errorcount = 0
def stop(self):
self.stream.close()
def find_input_device(self):
device_index = None
for i in range( self.pa.get_device_count() ):
devinfo = self.pa.get_device_info_by_index(i)
print( "Device %d: %s"%(i,devinfo["name"]) )
for keyword in ["mic","input"]:
if keyword in devinfo["name"].lower():
print( "Found an input: device %d - %s"%(i,devinfo["name"]) )
device_index = i
return device_index
if device_index == None:
print( "No preferred input found; using default input device." )
return device_index
def open_mic_stream( self ):
device_index = self.find_input_device()
stream = self.pa.open( format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
input_device_index = device_index,
frames_per_buffer = INPUT_FRAMES_PER_BLOCK)
return stream
def listen(self):
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
while True:
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
elif mx < 2300: #If the volume is below 2300:
time.sleep(2) #Wait two seconds
if mx > 2300: #If after two seconds the volume is back up, repeat
continue
elif mx < 2300: #If after two seconds the volume is still down, break the loop
break
print("You're Done")
self.stream.close()
print("Stream Closed")
if __name__ == "__main__":
tt = TEST()
for i in range(1000):
tt.listen()
All that does is either print the initial volume level, over and over forever, or it just closes the stream immediately. This depends on whether there is sound when I start the program or not.
EDIT:
While the methods posted in my answer outline ways to break your loop, the real reason your program is not working, is not because of the loop, its because the portion of code where you actually read the audio input is not even in your loop.
You need to read the input stream each time around the loop otherwise you will just keep making a check against the value when when the listen method is called.
def listen(self):
while True:
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
elif mx < 2300: #If the volume is below 2300:
time.sleep(2) #Wait two seconds
if mx > 2300: #If after two seconds the volume is back up, repeat
continue
You need to listen during those two seconds, instead of passively waiting.
start = -1
while True:
try:
chunk = self.stream.read(INPUT_FRAMES_PER_BLOCK)
except IOError, e:
self.errorcount += 1
print( "(%d) Error recording: %s"%(self.errorcount,e) )
self.noisycount = 1
return
mx = audioop.max(chunk, 2)
if mx > 2300: #print the volume level whenever the volume is above 2300
print(mx)
start = -1
elif mx < 2300: #If the volume is below 2300:
if start < 0: # INITIALIZE
start = time.time()
else:
if time.time() - start >= 2: #Wait two seconds
break
The problem is you aren't updating mx while (or after) you sleep. You should set a variable for when you last got a big sound, and break/restart based on new samples
last_sound = time.time() # time of last loud sound
while True:
mx = audioop.max(chunk, 2) # new sample
if mx > 2300:
print(mx)
last_sound = time.time() # reset the "timer" because we're loud
else: # I changed this to else
now = time.time()
if now - last_sound >= 2:
break # two seconds of silence!
else:
print(now-last_sound) # this should count up to 2

Categories

Resources