I'm on Raspberry Pi, using Python.
I would like to receive continuous input from a distance sensor and have the distance value determine whether or not to send output.
I have a output function, gogo, and a sense function, sense, which updates the distance value.
I'd like to have the output start when below 20 distance and the output stop when the distance reaches threshold of 20, but the only solution that I can see is another loop.
My code is not working and I'm thinking that there's a nice solution, but I am not well-versed in loops.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
distance = 40.0
TRIG = 4
ECHO = 18
GPIO.setup(TRIG,GPIO.OUT)
GPIO.setup(ECHO,GPIO.IN)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)
GPIO.setup(25, GPIO.OUT)
def gogo():
Motor1A = 23
Motor1B = 24
Motor1E = 25
GPIO.setup(Motor1A,GPIO.OUT)
GPIO.setup(Motor1B,GPIO.OUT)
GPIO.setup(Motor1E,GPIO.OUT)
print "Turning motor on"
GPIO.output(Motor1A,GPIO.HIGH)
GPIO.output(Motor1B,GPIO.LOW)
GPIO.output(Motor1E,GPIO.HIGH)
def sense():
GPIO.output(TRIG, True)
time.sleep(0.00001)
GPIO.output(TRIG, False)
while GPIO.input(ECHO) == False:
start = time.time()
while GPIO.input(ECHO) == True:
end = time.time()
sig_time = end-start
#CM:
distance = sig_time / 0.000058
#inches:
#distance = sig_time / 0.000148
print('Distance: {} centimeters'.format(distance))
while distance > 20.0:
print (distance)
sense()
else:
print (distance)
gogo()
sense()
GPIO.cleanup()
The problem is scope; distance is constantly in your 40 main code. It gets updated only in sense()
First, let's edit sense to return the value of distance
def sense():
GPIO.output(TRIG, True)
time.sleep(0.00001)
GPIO.output(TRIG, False)
while GPIO.input(ECHO) == False:
start = time.time()
while GPIO.input(ECHO) == True:
end = time.time()
sig_time = end-start
#CM:
distance = sig_time / 0.000058
#inches:
#distance = sig_time / 0.000148
print('Distance: {} centimeters'.format(distance))
return distance
you should also probably create a function that turns the motor off
you need to define distance, also let's make a loop that runs forever. right now, your loop only runs until distance< 20
distance = sense()
while True:
if distance > 20:
<call motor off function here>
print (distance)
else:
print (distance)
gogo()
distance = sense() #now we're checking distance every cycle
Related
I have a problem with the Jsn-sr04t water proof ultrasonic sensor, knowing that the connection in the raspberry pi zero W1 is well made becouse it is activated but the readings of this are totally wrong, the pins were entered correctly but this one seems to have a data entry error. I tried several programs in python that were used on this same sensor model
PD: I'm aware that this works, because I tested it on an arduino mega and the sensor worked correctly
import RPi.GPIO as GPIO
import os
import time
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO_TRIGGER = 10
GPIO_ECHO = 8
TRIGGER_TIME = 0.00001
MAX_TIME = 0.004 # max time waiting for response in case something is missed
GPIO.setup(GPIO_TRIGGER, GPIO.OUT) # Trigger
GPIO.setup(GPIO_ECHO, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Echo
GPIO.output(GPIO_TRIGGER, False)
def measure():
# Pulse the trigger/echo line to initiate a measurement
GPIO.output(GPIO_TRIGGER, True)
time.sleep(TRIGGER_TIME)
GPIO.output(GPIO_TRIGGER, False)
# ensure start time is set in case of very quick return
start = time.time()
timeout = start + MAX_TIME
while GPIO.input(GPIO_ECHO) == 0 and start <= timeout:
start = time.time()
if(start > timeout):
return -1
stop = time.time()
timeout = stop + MAX_TIME
# Wait for end of echo response
while GPIO.input(GPIO_ECHO) == 1 and stop <= timeout:
stop = time.time()
if(stop <= timeout):
elapsed = stop-start
distance = float(elapsed * 34300)/2.0
else:
return -1
return distance
if __name__ == '__main__':
try:
while True:
distance = measure()
if(distance > -1):
print("Measured Distance = %.1f cm" % distance)
else:
print("#")
time.sleep(0.5)
except KeyboardInterrupt:
print("Measurement stopped by User")
GPIO.cleanup()
constantly OUTPUT: "#"
Seems like an issue with the GPIO settings or a timing issue as the same setup works with an Arduino.
Try with higher trigger on time (maybe 20us)
After elapsed calculation, print the elapsed time on the screen and check the actual value
If it still doesn't work, then you can also checkout the alternate output options like serial UART, PWM, etc. You can get more details here -
https://tutorials.probots.co.in/communicating-with-a-waterproof-ultrasonic-sensor-aj-sr04m-jsn-sr04t/
I have a class for a DC-motor. It has attributes that can e.g. change the set rotational speed.
In order to control the actual speed I would like to run a thread that guards the acceleration. So if a user sets a new speed the thread will slowly increase the actual speed to match the set speed.
My approach:
import time
import threading
import RPi.GPIO as GPIO
class motorController:
#constructors
def __init__(self,maxPower,powerPin,directionPin,direction=True):
self.maxPower = abs(maxPower)
self.power = 0 #initially off
self.setPower = 0
#set pins motors are connected to
self.powerPin = powerPin
self.directionPin = directionPin
self.direction = direction
#initialize PWM
GPIO.setmode(GPIO.BCM)
#initialize powerPin
GPIO.setup(self.powerPin, GPIO.OUT) # set pin to output
self.powerPwm = GPIO.PWM(self.powerPin, 100) # Initialize PWM on pwmPin 100Hz frequency, max is 8kHz, min is 10Hz
self.powerPwm.start(self.power) # Start PWM with 0% duty cycle
#initialize directionPin
GPIO.setup(self.directionPin, GPIO.OUT,initial=self.direction) # set pin to output
#initialize controll deamon
self.__lastCall = time.time()
self.setPointIsNotReached = threading.Condition() #currently unused
self.deamonTimeLock = threading.Lock()
self.controllThread = threading.Thread(target=self.__powerControllerDeamon())
self.controllThread.setDaemon(True)
self.controllThread.start()
def setTargetPower(self,setPower):
setPower = min(setPower,self.maxPower)
self.setPower = max(setPower,-self.maxPower)
with self.deamonTimeLock:
self.__lastDeamonCall = t
def __setPower(self,power):
#limit power to maxPower
self.power = min(abs(power),self.maxPower)
self.powerPwm.ChangeDutyCycle(self.power)
#set rotation direction
if power < 0:
self.direction = False
else:
self.direction = True
GPIO.output(self.directionPin, self.direction) #set to 3.3V if direction is true, or 0V if direction is false
def __powerControllerDeamon(self):
#private method that controls the power of the motor
t = time.time()
dt = t-self.__lastCall
if self.power > self.setPower:
m = -50 #ramp from 100 to 0 in minimal 2 sec
else:
m = 20 # ramp from 0 to 100 in minimal 5sec
newPower = self.power + m * dt
#detect if power is reched through change of sign
dP = self.setPower - self.power
dP2 = self.setPower - newPower
if dP*dP2 <= 0: #change of sign or one is zero
newPower = self.setPower #set point reached
else:
with self.setPointIsNotReached
self.setPointIsNotReached.notify()#set point NOT reached
print(newPower)
self.__setPower(newPower)
with self.deamonTimeLock:
self.__lastDeamonCall = t
and
from motorController import motorController as mC
import time
try:
m1 = mC(100,18,17)
m1.setTargetPower(100)
time.sleep(10) #should have powered up by now
except BaseException as e:
import RPi.GPIO as GPIO
GPIO.cleanup()
raise(e)
In this example, the thread function is called once and then never again.
I'm new to threading and python. My questions:
Can I create a thread from a constructor and have it running until the main program ends (that's why i set deamon true)?
If the answer is yes, what's my mistake?
Your __powerControllerDeamon method should contain a loop that runs until it reaches the desired speed or until it is told to stop.
Since it doesn't contain a loop, it will run once and exit.
Note that this loop should also contain a small amount of sleep, to prevent it from hogging the CPU.
I have a attached a rotary encoder to a DC motor shaft in hopes of creating a python script on a PI4 in which one could set a desired angle, motor will move CW at a 10 - duty cycle, and motor will stop and hold position on desired angle that once set angle is read back into code from rotary encoder (# 800 pulses per revolution - reading out 0.45 deg increments).
The PI controller will ideally then hold the set angle (set in code but later GUI) and will not move regardless of outside force on the shaft.
Lastly i am using if statments after my PI control signal 'PI' into a duty cycle ouput thus controlling the speed of the motor as it reaches the set point (either slowing down or speeding up tp get to the set point) with error PI polarity governing directional output....if this could be done better i'd appreciate any suggestions
I am only getting 1 error i cant figure out and cant find a solution online...but i feel i am close to getting this code right. Any criticism is welcome. The error is regarding the "PI" output of the Pi equation:
"Value of type "int" is not indexable"
from RPi import GPIO
import time
import threading
#Encoder GPIO Pins
clk = 15 # GRN/YLW Z+
dt = 11 # BLU/GRN Z-
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(clk, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(dt, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
#Motor GPIO Pins
PWMPin = 18 # PWM Pin connected to ENA.
Motor1 = 16 # Connected to Input 1.
Motor2 = 18 # Connected to Input 2.
GPIO.setup(PWMPin, GPIO.OUT) # We have set our pin mode to output
GPIO.setup(Motor1, GPIO.OUT)
GPIO.setup(Motor2, GPIO.OUT)
GPIO.output(Motor1, GPIO.LOW)# When program start then all Pins will be LOW.
GPIO.output(Motor2, GPIO.LOW)
def MotorClockwise():
GPIO.output(Motor1, GPIO.LOW) # Motor will move in clockwise direction.
GPIO.output(Motor2, GPIO.HIGH)
def MotorAntiClockwise():
GPIO.output(Motor1, GPIO.HIGH) # Motor will move in anti-clockwise direction.
GPIO.output(Motor2, GPIO.LOW)
#Sim Values
duty_c = 10 #duty Cycle
PwmValue = GPIO.PWM(PWMPin, 10000) # We have set our PWM frequency to 5000.
PwmValue.start(duty_c) # That's the maximum value 100 %.
error = 0
Set_point = 1.8
encoder_counter = 0
encoder_angle = 0
Kp = 2
Ki = 1
PI = 0
#Sim Parameters
Ts = .1 #sampling time
Tstop = 200 #end simulation time
N = int(Tstop/Ts)#simulation length
def ReadEncoder():
global encoder_counter
global encoder_angle
clkLastState = GPIO.input(clk)
while True:
clkState = GPIO.input(clk)
dtState = GPIO.input(dt)
encoder_angle = float(encoder_counter/800)*360
if clkState != clkLastState:
if dtState != clkState:
encoder_counter += 1
else:
encoder_counter -= 1
#print("encoder_counter", encoder_counter )
print("encoder_angle", encoder_angle )
time.sleep(1)
clkState = clkLastState
# some delay needed in order to prevent too much CPU load,
# however this will limit the max rotation speed detected
time.sleep(.001)
def PI_function():
global PI
global duty_c
global error
global Kp
global Ki
global encoder_angle
for k in range(N+1):
error = int(Set_point) - encoder_angle
print (f"{error} Error Detected")
PI = PI[k-1] + Kp*(error[k] - error[k-1]) + (Kp/Ki)*error[k] #PI equation
print (f"{PI} PI value")
if (PI > 0):
MotorClockwise()
else:
MotorAntiClockwise()
if ((PI > duty_c) & (duty_c <100)):
duty_c += 1
if ((PI < duty_c) & (duty_c > 10)):
duty_c -= 1
return()
def main_function():
if (N != 0):
encoder_thread = threading.Thread( target=ReadEncoder)
encoder_thread.start()
encoder_thread.join()
PI_thread = threading.Thread( target=PI_function)
PI_thread.start()
PI_thread.join()
PwmValue.ChangeDutyCycle(duty_c)
else:
PwmValue.ChangeDutyCycle(0)
print(f"Motor Stopped & Holding at {encoder_angle} Degrees")
So basically we're using raspberry pi for a project and a part of it includes the usage of ultrasonic sensors. We have three, and we've been able to get readings from all of them (I actually came from multi-threading but decided to move to multiprocessing). Here is my code:
#Libraries
from multiprocessing import Process, Lock
import RPi.GPIO as GPIO
import time
#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#set GPIO Pins
GPIO_TRIGGER1 = 23
GPIO_ECHO1 = 24
GPIO_TRIGGER2 = 27
GPIO_ECHO2 = 22
GPIO_TRIGGER3 = 25
GPIO_ECHO3 = 17
#set GPIO direction (IN / OUT)
GPIO.setup(GPIO_TRIGGER1, GPIO.OUT)
GPIO.setup(GPIO_ECHO1, GPIO.IN)
GPIO.setup(GPIO_TRIGGER2, GPIO.OUT)
GPIO.setup(GPIO_ECHO2, GPIO.IN)
GPIO.setup(GPIO_TRIGGER3, GPIO.OUT)
GPIO.setup(GPIO_ECHO3, GPIO.IN)
def sense_distance(lock, processName):
#lock.acquire()
gpio_echo_var = ''
gpio_trigger_var = ''
if processName == "Sensor-1":
gpio_echo_var = GPIO_ECHO1
gpio_trigger_var = GPIO_TRIGGER1
elif processName == "Sensor-2":
gpio_echo_var = GPIO_ECHO2
gpio_trigger_var = GPIO_TRIGGER2
elif processName == "Sensor-3":
gpio_echo_var = GPIO_ECHO3
gpio_trigger_var = GPIO_TRIGGER3
print "%s process created." % (processName)
try:
while True:
# set Trigger to HIGH
GPIO.output(gpio_trigger_var, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(gpio_trigger_var, False)
StartTime = time.time()
StopTime = time.time()
# save StartTime
while GPIO.input(gpio_echo_var) == 0:
StartTime = time.time()
# save time of arrival
while GPIO.input(gpio_echo_var) == 1:
StopTime = time.time()
# time difference between start and arrival
TimeElapsed = StopTime - StartTime
# multiply with the sonic speed (34300 cm/s)
# and divide by 2, because there and back
distance = (TimeElapsed * 34300) / 2
if distance <= 10:
print "%s has read less than 10 cm." % (processName)
else:
pass
# Reset by pressing CTRL + C
except KeyboardInterrupt:
print("Measurement stopped by User")
GPIO.cleanup()
#lock.release()
if __name__ == '__main__':
lock = Lock()
Process(target=sense_distance, args=(lock, "Sensor-1")).start()
Process(target=sense_distance, args=(lock, "Sensor-2")).start()
Process(target=sense_distance, args=(lock, "Sensor-3")).start()
It successfully reads the input and prints out the text when input goes smaller than 10 cm. However, after some time, they stop. I have run out of ideas and have searched all over only to come up short. Any sort of help will be appreciated.
I have set up 3 HC-SR04 ultrasonic sensors to operate continuously with raspberry pi. Two of the sensors work perfectly while one is being very inconsistent. I have replaced the wires, sensor, and breadboard multiple times, as well as changing the trig and echo pins associated with the sensor. I have debugged the code and have isolated the problem sensor in the code below:
import RPi.GPIO as GPIO
import time
TRIG1 = 13
ECHO1 = 15
##TRIG2 = 22
##ECHO2 = 18
##
##TRIG3 = 37
##ECHO3 = 40
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(TRIG1, GPIO.OUT)
GPIO.output(TRIG1, 0)
##GPIO.setup(TRIG2, GPIO.OUT)
##GPIO.output(TRIG2, 0)
##
##GPIO.setup(TRIG3, GPIO.OUT)
##GPIO.output(TRIG3, 0)
GPIO.setup(ECHO1, GPIO.IN)
##GPIO.setup(ECHO2, GPIO.IN)
##GPIO.setup(ECHO3, GPIO.IN)
while True:
time.sleep(0.1)
GPIO.output(TRIG1, 1)
time.sleep(0.00001)
GPIO.output(TRIG1, 0)
print("anything")
while GPIO.input(ECHO1) == 0:
print("Works")
time.sleep(1)
start1 = time.time()
while GPIO.input(ECHO1) ==1:
stop1 = time.time()
print("sensor 1:")
print (stop1-start1) * 17000
## time.sleep(0.1)
##
## GPIO.output(TRIG2, 1)
## time.sleep(0.00001)
## GPIO.output(TRIG2, 0)
##
## while GPIO.input(ECHO2) == 0:
## start2 = time.time()
##
## while GPIO.input(ECHO2) == 1:
## stop2 = time.time()
## print("sensor 2:")
## print (stop2-start2) * 17000
##
## time.sleep(0.1)
##
## GPIO.output(TRIG3, 1)
## time.sleep(0.00001)
## GPIO.output(TRIG3, 0)
##
## while GPIO.input(ECHO3) == 0:
## start3 = time.time()
##
## while GPIO.input(ECHO3) == 1:
## stop3 = time.time()
## print("sensor 3:")
## print (stop3-start3) * 17000
GPIO.cleanup()
The line that reads "print("anything")" allows the code to compile, but take no distance reading and will continue to print "Works" indicating that it is running continuously in the first while loop. Unexpectedly when the "print("anything")" line is removed, the error:
print (stop1-start1) * 17000
NameError: name 'start1' is not defined
prints to the terminal. The code that is commented out above, which is identical to the code for the functioning sensors works without error when isolated from the code that is currently uncommented. Any thoughts or suggestions would be greatly appreciated.
Thanks.
I think you have a timing issue, that causes your code to run differently depending on "small" timing changes.
Your code fails due to not having initialized start1.
When you have a delay in your code (print "anything") is a significant delay, the while loop will run and initialize start1
When you do not have the delay,
GPIO.input(ECHO1)
will not equal zero, the while loop will not run and you get the error.
I think you should initialie your variables and you should check if there are timing requirements you are not aware of.
Also please note running python on a raspberry pi, will not be a real time system, so please be aware of real time constraints and be careful of making code that is timing sensitive.
I think you have declaration issue. At first you need to declare then declare global. I changed line number 6,7,40 and 44. you see
import RPi.GPIO as GPIO
import time
TRIG1 = 13
ECHO1 = 15
start1=0.00
stop1=0.00
##TRIG2 = 22
##ECHO2 = 18
##
##TRIG3 = 37
##ECHO3 = 40
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(TRIG1, GPIO.OUT)
GPIO.output(TRIG1, 0)
##GPIO.setup(TRIG2, GPIO.OUT)
##GPIO.output(TRIG2, 0)
##
##GPIO.setup(TRIG3, GPIO.OUT)
##GPIO.output(TRIG3, 0)
GPIO.setup(ECHO1, GPIO.IN)
##GPIO.setup(ECHO2, GPIO.IN)
##GPIO.setup(ECHO3, GPIO.IN)
while True:
time.sleep(0.1)
GPIO.output(TRIG1, 1)
time.sleep(0.00001)
GPIO.output(TRIG1, 0)
print("anything")
while GPIO.input(ECHO1) == 0:
print("Works")
time.sleep(1)
global start1
start1 = time.time()
while GPIO.input(ECHO1) == 1:
global stop1
stop1 = time.time()
print("sensor 1:")
print (stop1 - start1) * 17000
## time.sleep(0.1)
##
## GPIO.output(TRIG2, 1)
## time.sleep(0.00001)
## GPIO.output(TRIG2, 0)
##
## while GPIO.input(ECHO2) == 0:
## start2 = time.time()
##
## while GPIO.input(ECHO2) == 1:
## stop2 = time.time()
## print("sensor 2:")
## print (stop2-start2) * 17000
##
## time.sleep(0.1)
##
## GPIO.output(TRIG3, 1)
## time.sleep(0.00001)
## GPIO.output(TRIG3, 0)
##
## while GPIO.input(ECHO3) == 0:
## start3 = time.time()
##
## while GPIO.input(ECHO3) == 1:
## stop3 = time.time()
## print("sensor 3:")
## print (stop3-start3) * 17000
GPIO.cleanup()