Make Python multiprocessing.Pipe between two functions - python

I use a OneWire sensor (ds18b20) to read out a temperature and use it in a PI-algorithm to controll a SSR relay. I want to use a Pipe between the two functions to to send the temperature and make te "Reg" function to run as fast as possible. If I don't use a Pipe the Reg-function waits for the temperature-function (uses 0.75 seconds) and the output gets wrong...
Can anyone please show me how to use the Pipe function.??
The code:
import time
import RPi.GPIO as GPIO
import os
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(22, GPIO.OUT)
def temperatur(self):
while True:
tfile = open("/sys/bus/w1/devices/28-00000433f810/w1_slave")
text = tfile.read()
tfile.close()
secondline = text.split("\n")[1]
temperaturedata = secondline.split(" ")[9]
temp2 = float(temperaturedata[2:])
self.temp = temp2 / 1000
print self.temp
def reg(self):
while True:
ek = self.ref - self.temp
P_del = self.Kp * ek
I_del = ((self.Kp * self.Syklustid) / self.Ti) * ek
Paadrag = P_del + I_del
if Paadrag > 100:
Paadrag = 100
if Paadrag < 0:
Paadrag = 0
print "Paadrag: ", Paadrag, " Temperatur: ", self.temp
duty = Paadrag / 100.0
on_time = self.Syklustid * (duty)
off_time = self.Syklustid * (1.0-duty)
print "On_time: ", on_time, " Off_time: ", off_time
GPIO.output(22, GPIO.HIGH)
time.sleep(on_time)
GPIO.output(22, GPIO.LOW)
time.sleep(off_time
if __name__ == '__main__':

This is straight from the python documentation:
http://docs.python.org/2/library/multiprocessing.html
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print parent_conn.recv() # prints "[42, None, 'hello']"
p.join()
I've had better results using shared state. Especially for simple data like temperature (a number I assume - not a complex custom object or whatever) Here is a wee example (again you will find more in the python docs)
#import stuff
from multiprocessing import Process, Manager
# Create a shared dictionary of paremeters for both processes to use
manager = Manager()
global_props = manager.dict()
# SuperImportant - initialise all parameters first!!
global_props.update({'temp': 21.3})
def functionOne(global_props):
# Do some stuff read temperature
global_props['temp'] = newVal
def functionTwo(global_props):
temp = global_props['temp']
# Do some stuff with the new value
# assign listeners to the two processes, passing in the properties dictionary
handlerOne = functionOne # This can also be a class in which case it calls __init__()
handlerTwo = functionTwo
processOne = Process(target=handlerOne,
args=(global_props))
processTwo = Process(target=handlerTwo,
args=(global_props))
# Start the processes running...
processOne.start()
processTwo.start()
processOne.join()
processTwo.join()

Related

Less Than 1 Frame Switching Between Multiple Cameras in Python OpenCV2

Does anyone have better idea on how to quickly switch between an array of web cameras controlled by a python open CV2 program. I would like it to have no noticeable pause between switching cameras. I am willing to go to other languages, maybe C++?
What I have so far:
#%% import the opencv library
import cv2
from multiprocessing import Queue, Process, Manager
import time
import keyboard
import glob
import threading
import signal
import sys
#%%
baseVidList = glob.glob("/dev/video*")
baseVidList = baseVidList[:-2]
#%%
manager = Manager()
selectedCam = manager.Value('i', 0)
endSignal = manager.Value('i', 0)
qqq = Queue()
vidDict = {}
# define a video capture object
for device in baseVidList:
vidDict[device] = lambda: cv2.VideoCapture(f"{device}")
#%%
ejections = []
for device, capDevice in vidDict.items():
vidDict[device] = capDevice()
if vidDict[device].isOpened():
print(f"{device} Sucess")
vidDict[device].set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
else:
print(f"{device} Failed, Ejecting")
vidDict[device].release()
ejections.append(device)
for eject in ejections:
vidDict.pop(eject)
#%%
font = cv2.FONT_HERSHEY_SIMPLEX
#### Functions ####
# Thread class
class VideoCaptureThread(threading.Thread):
def __init__(self, device, camID, endSignal):
super().__init__()
self.camID = camID
self.device = device
self.stopped = False
self.fps = 10
self.endSignal = endSignal
def run(self):
print("RUNNNN", self.endSignal != 1)
while self.endSignal != 1:
start_time = time.time()
print("S cam is:", selectedCam.value)
ret, frame = self.device.read()
if ret and selectedCam.value == self.camID:
# print(self.camID, xW, yH)
cv2.putText(frame,f"{self.camID}",(100,100),font,1,(255,0,0),1)
qqq.put(frame)
# Control the frame rate
sleep_time = 1/self.fps - (time.time() - start_time)
if sleep_time > 0:
time.sleep(sleep_time)
def stop(self):
self.stopped = True
self.device.release()
# Multiprocessing thread launch
def runCamera(device, camID):
# assert cap.isOpened()
# Usage
capture_thread = VideoCaptureThread(device, camID, endSignal) # src can be a filepath or a device index
capture_thread.start()
# # To stop the thread
# capture_thread.stop()
# Display Process
def runViewer():
while endSignal != 1:
# print("Qlen: ", qqq.qsize())
if qqq.empty() != True:
frame = qqq.get()
fy, fx, _ = frame.shape
frame = cv2.resize(frame,(int(2*fx),int(2*fy)))
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
while qqq.qsize() > 0: # Empty the Q
qqq.get()
# Signal Handler
def signal_handler(sig, arg2):
print("Exiting...")
endSignal.value = 1
sys.exit(0)
#### MAIN ####
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
processes = []
idx = 0
# Setup the cameras
for device, capDevice in vidDict.items():
if capDevice.isOpened():
processes.append(Process(target=runCamera, args=(capDevice,idx)))
idx = idx + 1
else:
print(f"Ejecting: {device}")
numProcesses = len(processes)
print(f"Processes: {numProcesses}")
processes.append(Process(target=runViewer))
# Launch the processes
for process in processes:
process.start()
# Keyboard control
while endSignal != 1:
if keyboard.is_pressed('d'): # if key 'q' is pressed
print('You Pressed d Key!')
selectedCam.value = (selectedCam.value + 1) % numProcesses
time.sleep(250/1000)
if keyboard.is_pressed('a'): # if key 'q' is pressed
print('You Pressed a Key!')
selectedCam.value = (selectedCam.value - 1) % numProcesses
time.sleep(250/1000)
# Graceful shutdown?
print("START END")
for process in processes:
process.join()
for device, capDevice in vidDict.items():
capDevice.release()
# Destroy all the windows
cv2.destroyAllWindows()
This code seems to support up to 5 cameras without issue. Extending to 10 sort of works but some of the cameras seem not to send data. The combination of threading and multiprocessing is an attempt to have the next frame ready for immediate addition to the queue and thereby there is no pause in switching cameras as would happen if we had to go through the whole cap setup and read process.
Any thoughts appreciated.
Run each camera capture in a separate thread by using multi-threading. In this method, while the other threads are capturing the following frame, the main thread can continue to process the currently shown frame.

Use generator to iterate through data from a multiprocess

I would like to perform the following below using multiprocess, instead of subprocess.Popen. This is because I cannot pass objects using popen. I know my simple example below does not use/pass objects, but that is what I want to do.
Sample code is:
main.py
import subprocess
class ProcReader():
def __init__(self, python_file):
self.proc = subprocess.Popen(['python', python_file], stdout=subprocess.PIPE)
def __iter__(self):
return self
def __next__(self):
while True:
line = self.proc.stdout.readline()
if not line:
raise StopIteration
return line
if __name__ == "__main__":
r1 = ProcReader("test1.py")
r2 = ProcReader("test2.py")
r3 = ProcReader("test3.py")
for l1, l2, l3 in zip(r1, r2, r3):
d1 = l1.decode('utf-8').strip().split(",")
d2 = l2.decode('utf-8').strip().split(",")
d3 = l3.decode('utf-8').strip().split(",")
print(f"{d1[0]}:{d1[1]},{d2[0]}:{d2[1]},{d3[1]}:{d3[1]}")
test#.py
for x in range(10):
print("test1,{}".format(x))
My sample code is in python3, but I would like an equivalent, using multiprocess, in python2.7. Should the equivalent also read from stdout? Or should it utilize the queue and just have a worker reading from the queue?
Update---------
My example using multiprocessing:
import time
from multiprocessing import Process, Queue
def writer1(queue):
for x in range(10):
time.sleep(1)
queue.put("test1,{}".format(x))
def writer2(queue):
for x in range(10):
time.sleep(2)
queue.put("test2,{}".format(x))
def writer3(queue):
for x in range(10):
queue.put("test3,{}".format(x))
if __name__=='__main__':
q1 = Queue()
q2 = Queue()
q3 = Queue()
writer_1 = Process(target=writer1, args=((q1),))
writer_1.daemon = True
writer_1.start()
writer_2 = Process(target=writer2, args=((q2),))
writer_2.daemon = True
writer_2.start()
writer_3 = Process(target=writer3, args=((q3),))
writer_3.daemon = True
writer_3.start()
while True:
msg1 = q1.get()
msg2 = q2.get()
msg3 = q3.get()
if msg1 and msg2 and msg3:
d1 = msg1.strip().split(",")
d2 = msg2.strip().split(",")
d3 = msg3.strip().split(",")
print("{}:{},{}:{},{}:{}".format(d1[0],d1[1],
d2[0],d2[1],
d3[0],d3[1]))
else:
break
Didnt realize q1.get() waits until something is there, I added sleep to verify this. Also, how do I check that the process is done writing? Seems to be just waiting at the end
To adapt your second example for my comment about sentinel objects, maybe you're looking for something like
import os
import time
from multiprocessing import Process, Queue
def writer(queue):
value = os.getpid()
for x in range(10):
time.sleep(0.1)
queue.put("{},{}".format(value, x))
queue.put(None)
def spawn_process():
q = Queue()
p = Process(target=writer, args=(q,))
p.daemon = True
p.start()
return (p, q)
if __name__ == "__main__":
processes_and_queues = [spawn_process() for x in range(3)]
processes, queues = zip(*processes_and_queues)
live_queues = list(queues)
while live_queues:
messages = []
for queue in live_queues:
message = queue.get()
if message is None:
live_queues.remove(queue)
messages.append(message)
if len(messages) == len(processes):
print(messages)
It outputs (e.g.)
['51748,0', '51749,0', '51750,0']
['51748,1', '51749,1', '51750,1']
['51748,2', '51749,2', '51750,2']
['51748,3', '51749,3', '51750,3']
['51748,4', '51749,4', '51750,4']
['51748,5', '51749,5', '51750,5']
['51748,6', '51749,6', '51750,6']
['51748,7', '51749,7', '51750,7']
['51748,8', '51749,8', '51750,8']
['51748,9', '51749,9', '51750,9']

how to get a realtime return value from an infinite loop thread in Python 3

I am trying to make a thread that constantly reads from my robot's sensors, so I can use the outputed "steering" value in different run cases for the motors, as to be able to run the motors on rotations, on time, etc. I have found something similar (Python returning values from infinite loop thread), but this did not help, as it only printed the values when I broke the loop (ie : disconnecting the sensor). Here is my code :
from sensor_and_motor_startup import *
import threading
import queue
DEFAULT_SPEED = 60
# PID Values --These are subjective and need to be tuned to the robot and mat
# Kp must be augmented or decreased until the robot follows the line smoothly --Higher Kp = Stronger corrections
# Same with Ki, after Kp is done --- note, Ki is not used in this case (error accumulation)
# Kd to 1, and move up or done until smooth, after Kp and Ki
# This process can take a VERY long time to fine-tune
K_PROPORTIONAL = 0.2
K_INTEGRAL = 0
K_DERIVATIVE = 0
class OneSensorLineFollower:
target = 24
error = 0
last_error = 0
derivative = 0
integral = 0
def __init__(self, color_sensor):
self.__color_sensor = color_sensor
def follower(self, side_of_line=None, kp=K_PROPORTIONAL):
if side_of_line is None:
side_of_line = self.SideOfLine.left
else:
side_of_line = self.SideOfLine.right
self.error = self.target - (self.__color_sensor.value(3) / 2)
self.integral = self.error + self.integral
self.derivative = self.error - self.last_error
motor_steering = ((self.error * kp) + (self.integral * K_INTEGRAL) + (self.derivative * K_DERIVATIVE
)) * float(side_of_line)
self.last_error = self.error
return motor_steering
class SideOfLine:
left = 1
right = -1
def hisp_center_corrector(out_que):
while True:
follow = OneSensorLineFollower(left_side_sensor)
steering = follow.follower(kp=0.15)
out_que.put(steering)
sleep(0.1)
def low_speed_follower(speed=DEFAULT_SPEED, rotations=5):
follower = OneSensorLineFollower(center_sensor)
steer_pair.on_for_rotations(follower.follower(kp=0.3), speed, rotations)
que = queue.Queue()
t = threading.Thread(target=hisp_center_corrector, args=(que,))
t.start()
t.join()
while True:
value = que.get()
print(value)
You are calling .join() which makes your main thread to wait for that thread to finish.
Start your thread as a deamon and don't join it:
threading.Thread(target=hisp_center_corrector, args=(que,), daemon=True).start()
Otherwise your value = que.get() code won't run.

Python - How can I run two dependant funtions at the same time?

I have two funtions, one runs increasing by one a number, and the other needs to capture the number that is running the first function every 5 seconds, so that when I capture the first number is 0, the sencond one is 10, the next one is 15, and so on... this is simulating the first function as a sensor. My code is as follows:
import time
import threading
from threading import Thread
def numero(i=0):
while True:
i = i + 1
time.sleep(1)
#print i
def capturar():
while True:
posicion = numero()
time.sleep(5)
print posicion
if __name__ == '__main__':
Thread(target = capturar()).start()
Thread(target = numero()).start()
When I run this code, it keeps in the first function, how can I get this run correctly and obtain the capture of the series of numbers every 5 seconds?
There are several mistakes in your code:
i is not in scope for capturar
you execute capturar and numero instead of handing the functions themselves to Thread
As Matt Clark commented, you call numero from capturar, which never returns, so capturar can't even do one full loop.
To help with the first one, the easiest solution is to wrap the functions into a class, and make i into an instance state (as an attribute). The second one merely requires you to remove the extra parentheses. The third one is resolved with the first one, as there is no function call required any more to fetch the counter state.
This should do what you want:
import time
from threading import Thread
class Contador:
def __init__(self, comienzo = 0):
self.numero = comienzo
def producir(self):
while True:
self.numero += 1
time.sleep(1)
def capturar(self):
while True:
posicion = self.numero
time.sleep(5)
print posicion
if __name__ == '__main__':
c = Contador()
Thread(target = c.capturar).start()
Thread(target = c.producir).start()
Thank you Amadan, the soution that you share is working perfectlym but when I tried to pull it into the real sensor (GPS), I am getting only "0"
import gps
import itertools
import json
import os
import portdetect
import time
import serial
from threading import Thread
(puerto1, puerto2) = portdetect.detectar_puerto()
print puerto1, puerto2
port = serial.Serial(puerto1, 115200, timeout=1)
os.system('sudo systemctl stop gpsd.socket')
time.sleep(2)
os.system('sudo systemctl disable gpsd.socket')
time.sleep(2)
comando = 'sudo gpsd ' + puerto2 + ' -F /var/run/gpsd.sock'
os.system(str(comando))
#print str(comando)
# Listen on port 2947 (gpsd) of localhost
session = gps.gps("localhost", "2947")
session.stream(gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE)
class Contador:
def __init__(self, comienzo = 0):
self.raw_datos = comienzo
def leergps(self):
while True:
try:
report = session.next()
# Wait for a 'TPV' report and display the current time
# To see all report data, uncomment the line below
# print report
if report['class'] == 'TPV':
if hasattr(report, 'time') or hasattr(report, 'lat') or hasattr(report, 'lon') or hasattr(report,
'speed'):
datos = [report.time, report.lat, report.lon, report.speed]
clave = ['tiempo', 'lat', 'lon', 'speed']
diccionario = dict(itertools.izip(clave, datos))
self.raw_datos = (json.dumps(diccionario))
# print raw_datos
return self.raw_datos
else:
return report['class']
except:
print "Error de coordenadas"
def capturar(self):
while True:
posicion = self.raw_datos
time.sleep(5)
print posicion
if __name__ == '__main__':
c = Contador()
Thread(target = c.capturar).start()
Thread(target = c.leergps()).start()
I am trying only to pull the "raw data obtained every 5 seconds, but I am not achieving this"

How to quit the program in while loop using push-button in PyQt

I have the following code which will start after clicking the 'Start' button in PyQt:
def Start(self):
import time
import os
import RPi.GPIO as GPIO
import datetime
GPIO.setmode(GPIO.BCM)
DEBUG = 1
os.system('clear')
# SPI port on GPIO
SPICLK = 18
SPIMISO = 23
SPICS = 25
# set up the SPI interface pins
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)
GPIO.output(SPICS, True)
GPIO.output(SPICS, False) # bring CS low
while True:
adcout = 0
read_adc = 0
#s=time.clock()
for i in range(25):
GPIO.output(SPICLK, True)
GPIO.output(SPICLK, False)
adcout <<= 1
if (GPIO.input(SPIMISO)==1):
adcout |= 0x1
time.sleep(0.085)
if (GPIO.input(SPIMISO)==0):
read_adc = adcout
millivolts = read_adc * ( 2500.0 /(pow(2,22)))
read_adc = "%d" % read_adc
millivolts = "%d" % millivolts
if DEBUG:
print millivolts, "mV (ADC)"
The above program is for ADC reading and it will start after clicking the pushbutton called 'Start' as : self.pushButton.clicked.connect( self.Start)
And I have another pushButton_2 called 'Stop' and by clicking this the above process should stop.Please suggest, so I can able to do that.
There is no need to do anything other than what I suggested in your other question on this topic: just use processEvents. As long as you can call it frequently enough (but not too frequently), it should do exactly what you want. Using your second example, the following works fine for me:
def Start(self):
if not self.started:
self.started = True
self.StartLoop()
def Stop(self):
if self.started:
self.started = False
def StartLoop(self):
DEBUG = 1
while self.started:
print "LED on "
time.sleep(0.05)
print "LED off "
time.sleep(0.085)
QtGui.qApp.processEvents()
This question is useful: tkinter loop and serial write It could be copied over with two changes: master.update becomes QtGui.qApp.processEvents and master.after becomes QTimer.singleShot.
Here is a sketch of how to do what you ask for with guiLoop:
from guiLoop import guiLoop, stopLoop
# ... means fill in your code
class ...:
started = False
def Start(self):
if not self.started:
# you can also use threads here, see the first link
self.started = self.StartLoop()
def Stop(self):
if self.started:
stopLoop(self.started)
self.started = False
#guiLoop
def StartLoop(self):
# This is your Start function
# ...
while True:
# ...
yield 0.085 # time.sleep(0.085) equivalent
# ...
Since I do not know what your code look like, here is a working example using PyQT4 and guiLoop:
from PyQt4 import QtGui
import sys
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951
#guiLoop
def led_blink(argument):
while 1:
print("LED on " + argument)
yield 0.5 # time to wait
print("LED off " + argument)
yield 0.5
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
led_blink(w, 'shiny!')
sys.exit(app.exec_())
guiLoop uses QTimer.singleShot(time, function) to make the loop continue.
You can also stop the loop with stopLoop() of guiLoop.

Categories