Pulse when dimming lights by setting duty cycle of PCA9685 - python

I have a AC current dimmer (MPDMv4.1) that takes a PWM signal to dim a halogen bulb. The PWM signal is coming from a PCA9685 servo HAT.
This works just perfectly with the following code:
from board import SCL, SDA
import busio
from adafruit_pca9685 import PCA9685
ic2_bus = busio.I2C(SCL, SDA)
pca = PCA9685(i2c_bus)
pca.frequency = 1000
while True:
value = input(„Enter a value from 0 to 100“)
duty_cycle = int(round((100 - int(val))/100 * 57000))
pca.channels[15].duty_cycle = duty_cycle
My problem occurs if I put the PCA9685 object initialization inside the endless loop, as such:
from board import SCL, SDA
import busio
from adafruit_pca9685 import PCA9685
ic2_bus = busio.I2C(SCL, SDA)
while True:
pca = PCA9685(i2c_bus)
pca.frequency = 1000
value = input(„Enter a value from 0 to 100“)
duty_cycle = int(round((100 - int(val))/100 * 57000))
pca.channels[15].duty_cycle = duty_cycle
It does of course not make sense to put the initialization inside the loop in this example. But my program is looping through different temperature sensors and based on the derived temperatures dims lights. The dimming is done in a separate process, because there is supposed to be a gradual change of the actual value to the target value to make the change smoother. I cannot wait for this to end with the main process, so I started a subprocess that takes care of this. But every time this process is started, the PCA9685 object has to be newly generated, and I get this pulse of bright light, which is not acceptable for my application. This pulse happens also when I‘m not actually changing the duty cycle, just by generating the object. It‘s only visible when the light is far from maximum power.
I tried to pickle the PCA9685 object and give it to the dimming process as an argument, but _io.FileIO objects cannot be pickled.
I also tried several different libraries, it’s always the same. Hence my question: Does anybody know how this is caused? Is this inevitable when establishing the connection? Can it potentially be circumvented?
Best and many thanks,
Gero

Related

Python: For Loop to change 1 property on multiple objects

The Python code below works perfectly to light 3 LEDs sequentially on a Rasberry Pico; however, I want to use a for loop with part of the object name plus the value of the for loop iterator. If LEDs were added to the board, all that would be needed is a change of the for range(1,x).
Something like the followning does not seem work (Python feels it is getting a value with no properites):
for loop_value in range(1,4):
#turn LED on, wait, turn LED off
LED_external_[loop_value].value(1)
utime.sleep(1)
LED_external_[loop_value].value(0)
Thanks so much for any input. I feel the function is a convoluted workaround. The for loop would be so much better !!!
########## start (works perfectly, Python coded in Thonny IDE) #######
from machine import Pin
import utime
# assign pin to green LED and initialize to off state
LED_external_1 = machine.Pin(14, machine.Pin.OUT)
LED_external_1.value(0)
# assign pin to yellow LED and initialize to off state
LED_external_2 = machine.Pin(15, machine.Pin.OUT)
LED_external_2.value(0)
# assign pin to red LED and initialize to off state
LED_external_3 = machine.Pin(11, machine.Pin.OUT)
LED_external_3.value(0)
def on_off(LED_Obj_Name):
#turn LED on, wait, turn LED off
LED_Obj_Name.value(1)
utime.sleep(1)
LED_Obj_Name.value(0)
while True:
on_off(LED_external_1)
on_off(LED_external_2)
on_off(LED_external_3)
############################# end ####################################
LED_external_[loop_value].value(1) here you're treating LED_external_ as a list. What you can do is add all LEDs to a list and iterate that list.
LED_list = [LED_external_1, LED_external_2, LED_external_3]
for item in LED_list:
#turn LED on, wait, turn LED off
item.value(1)
utime.sleep(1)
item.value(0)

Live 2D laser scanner data in rospy

I just got a Sick Tim 571 laser scanner. Since I'm new to ROS I wanted to test some stuff in an easy rospy implementation.
I thought that the code below will show me a live output of the laser measurements like it is possible in Rviz (Rviz works fine for me) - but in Python and with the possibility to use the measurements in my own code. Unfortunately, the output frame shows only one static measurement (from the time when the Python code was started for the first time) over and over again.
If I run Rviz parallel to this Python code, I get a dynamically updated representation of the measured area.
I thought that the .callback(data) function is called with a new set of laser scanner data each time. But it seems like that it works not as I imagined. So the error is possibly located in .laser_listener() where the callback function is called.
TL;DR
How can I use dynamically updated (live) laser scanner measurements in rospy?
import rospy
import cv2
import numpy as np
import math
from sensor_msgs.msg import LaserScan
def callback(data):
frame = np.zeros((500, 500,3), np.uint8)
angle = data.angle_min
for r in data.ranges:
#change infinite values to 0
if math.isinf(r) == True:
r = 0
#convert angle and radius to cartesian coordinates
x = math.trunc((r * 30.0)*math.cos(angle + (-90.0*3.1416/180.0)))
y = math.trunc((r * 30.0)*math.sin(angle + (-90.0*3.1416/180.0)))
#set the borders (all values outside the defined area should be 0)
if y > 0 or y < -35 or x<-40 or x>40:
x=0
y=0
cv2.line(frame,(250, 250),(x+250,y+250),(255,0,0),2)
angle= angle + data.angle_increment
cv2.circle(frame, (250, 250), 2, (255, 255, 0))
cv2.imshow('frame',frame)
cv2.waitKey(1)
def laser_listener():
rospy.init_node('laser_listener', anonymous=True)
rospy.Subscriber("/scan", LaserScan,callback)
rospy.spin()
if __name__ == '__main__':
laser_listener()
[EDIT_1]:
When I add print(data.ranges[405]) to the callback function I get this output. It changes slightly. But it's wrong. I covered the whole sensor in the middle of the measurement. The values still only fit for the time when the program was started.
1.47800004482
1.48000001907
1.48000001907
1.48000001907
1.48300004005
1.47899997234
1.48000001907
1.48099994659
1.47800004482
1.47899997234
1.48300004005
1.47800004482
1.48500001431
1.47599995136
1.47800004482
1.47800004482
1.47399997711
1.48199999332
1.48099994659
1.48000001907
1.48099994659
The same as above but the other way around. I started with a covered sensor and lifted the cover during the measurement.
0.0649999976158
0.0509999990463
0.0529999993742
0.0540000014007
0.0560000017285
0.0579999983311
0.0540000014007
0.0579999983311
0.0560000017285
0.0560000017285
0.0560000017285
0.0570000000298
[EDIT_2]:
Oh... if I comment out the whole cv2 part, I get the realtime print output! So cv2 slows it so much down that I get the 15Hz measurement shown at a much slower rate.
So my question is now: Is there an alternative to cv2 that is capable to refresh at a higher rate?
You Can Use Librviz But thats In C++ and i haven't seen python version for it.
You can Use OpenGL (PyOpenGL) But I Don't Recommend It Cause it makes what u intened to do Really Complex but it's fast.
Why Not Use the rviz for visualization Only and do Other things elsewhere?
I've even seen a whole framework Placed In rviz(check Moveit framework). Rviz is Completely Customizable You can write Your Own PlugIns for it and it will Handle The outputing whatever topic You want.
just move out cv2.circle cv2.imshow cv2.waitkey from the for loop,and the problem will be solved.

Raspberry pi servo doesn't stop

So I'm trying to use a servo (Doman s0306d) with the pi camera, tried running this script I found to test the motor, it starts running but doesn't stop unless I manually unplug it from the breadboard.
import RPi.GPIO as IO # calling for header file for GPIO’s of PI
import time # calling for time to provide delays in program
IO.setwarnings(False) # do not show any warnings
IO.setmode (IO.BCM) # programming the GPIO by BCM pin numbers. (like PIN29 as‘GPIO5’)
IO.setup(19,IO.OUT) # initialize GPIO19 as an output
p = IO.PWM(19,50) # GPIO19 as PWM output, with 50Hz frequency
p.start(7.5) # generate PWM signal with 7.5% duty cycle
time.sleep(4)
for x in range(0,5): # execute loop forever
p.ChangeDutyCycle(7.5) # change duty cycle for getting the servo position to 90º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(12.5) # change duty cycle for getting the servo position to 180º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(2.5) # change duty cycle for getting the servo position to 0º
time.sleep(1) # sleep for 1 second
p.ChangeDutyCycle(0)
p.stop()
IO.cleanup()
Any ideas ? Thank you.
[EDIT] The servo you are using is a "continuous" servo - so you need to give it the zero speed or "STOP" setting pulse width of 1500us (according to the website http://www.domanrchobby.com/content/?150.html). I haven't used PWM on the pi but if the percentages are of the 50Hz pulse rate (20ms interval) then that should be your 7.5% centre value. You need to make sure the servo gets that pulse before your code exits.
[Original] You are setting the duty cycle to 0 when you exit, which will probably mean the servo doesn't get any pulses. Some servos will stop sometime after they don't get pulses, but some servos (particularly digital servos, but not all) will continue to try achieve the setting from the last pulse they received. Suggest you leave the setting at the mid range 7.5 which you know the servo can achieve and delay for a little while before cleaning up.

Can't control any servo with my RaspberryPi 2

I am having the problem, that I am not able to control any of my servos I have. I have two servos, one is a normal servo used in model planes and the second one is a micro sized servo.
I wired both of them separately (The signal cable to a GPIO pin and the other two cables first directly to the board and after to a external power source).
When I try to run them via the similar python code
...
GPIO.setmode(GPIO.BCM)
GPIO.setup(11, GPIO.OUT)
pwm = GPIO.PWM(11, 50)
pwm.start(2)
timelib.sleep(2)
pwm.ChangeDutyCycle(3)
timelib.sleep(2)
...
the servos sometimes just turn for a bit but then stop on one side. After that you can still hear the servo making noises like it is trying to run further. After it turned to the end I can not make it work or make it turn in any way. It will just stay there whatever input I will make. It will just turn to the same end again if I am manually turn them back to the start position. I can not figure it out what I am doing wrong or where I need to change my way of doing it.
Does anyone have any tips or had a similar problem?
I'm thankful for every further tip and further step I will make.
Thanks in advance!
The servos position is controlled by the pulsewidth of a 50 Hz PWM signal. Hence, we need to turn the PWM sequence on at 50 Hz. Note that for a 50 Hz signal, the Period of the signal is 1/50=.02 seconds, or 20 milliseconds. Keep this Period in mind as we will come back to it later. We start by creating a PWM object on Pin 11 with a 50 Hz signal with the command:
pwm=GPIO.PWM(11,50)
We can now start the pwm sequence by giving a command to specify the DutyCycle of the signal. Before we do this, we need to talk a little bit about how servos work. A typical servo wants to see a frequency of 50 Hz on the control line. The position it moves to depends on the pulse width of the signal. Most servos behave roughly as such, but you will need to tweak these numbers for your particular servo. Typically, the servo will go to the full left position when it sees a pulse width of 1 millisecond, it will go the middle position when it sees a pulse width of 1.5 millisecond, and it will go to the full right position when it sees a pulse width of 2 millisecond. Note however, that on the Raspberry Pi we do not specify a pulse width, but we specify a DutyCycle. So, we can use the following relationship:
DutyCycle =PulseWidth/Period
Remember that Period = 1/frequency, so:
DutyCycle = PulseWidth/(1/frequency) = PulseWidth * frequency
The PulseWidth that will give us a full left position is 1 milllisecond. We now calculate the applied DutyCycle to give us the desired position:
DutyCycle = PulseWidth*frequency=.001 *50 = .05 = 5%
So, for a 50 Hz signal, if we set the DutyCycle to 5, then we should see the servo move to the full left position. Similarly, if we set DutyCycle to 7.5, we should get the middle position, and if we set it to 10 we should be in the full right position. You can get all the intermediate positions by linearly scaling between 5 and 10. Note that these values will vary between brands, and between individual servos, so play around with your servo to get it calibrated. We are now ready to apply a command to position the servo. If we want the servo in the full left position, we should set the DutyCycle to 5%. We do that with the command:
pwm.start(5)
This will start the PWM signal, and will set it at 5%. Remember, we already specified the 50 Hz signal when we created the pwm object in our earlier commands. Now if we want to change the position, we can change the DutyCycle. For example, if we want to go to the middle position, we want a DutyCycle of 7.5, which we can get with the command:
pwm.ChangeDutyCycle(7.5)
Now if we want the full right position, we want a duty cycle of 10, which we would get with the command:
pwm.ChangeDutyCycle(10)
Remember, it is not DutyCycle that actually controls servo position, it is PulseWidth. We are creating DutyCycles to give us the desired PulseWidth.
Now, play around with your particular servo and then find the specific DutyCycles that lead to full left and full right positions. For my servo, I find that full left is at DutyCycle=2, and full right is at DutyCycle=12. With these values, I can create a linear equation that will give me any angle I want between 0 and 180. This will make the Raspberry Pi behave much more like the simple and intuitive operation of the Arduino.
To do the linear equation I need two points. Well, I know that for a desired angle of 0, I should apply a DutyCycle of 2. This would be the point (0,2). Now I also know that for a desired angle of 180, I should apply a DutyCycle of 12. This would be the point (180,12). We now have two points and can calculate the equation of the line. (Remember, play with your servo . . . your numbers might be slightly different than mine, but the methodology below will work if you use your two points)
Remember slope of a line will be:
m=(y2-y1)/(x2-x1)=(12-2)/180-0)=10/180 = 1/18
We can now get the equation of the line using the point slope formula.
y-y1=m(x-x1)
y-2=1/18*(x-0)
y = 1/18*x + 2
Putting in our actual variables, we get
DutyCycle = 1/18* (DesiredAngle) + 2
Now to change to that position, we simply use the command:
pwm.ChangeDutyCycle(DutyCycle)
See more at: http://www.toptechboy.com/raspberry-pi/raspberry-pi-lesson-28-controlling-a-servo-on-raspberry-pi-with-python/#sthash.LRmf7708.dpuf
Alternately, you can use my library which hides most of the pwm and GPIO board complexity. Sample code:
from RaspberryMotors.motors import servos
s1 = servos.servo(11) #create the servo objects , connected to GPIO board pin #11
s1.setAngleAndWait(45) # move S1 position of 45 degrees
s1.shutdown() #will clean up the GPIO board as well`
You can view download the code or the library via any of the two links:
https://github.com/vikramdayal/RaspberryMotors or
https://pypi.org/project/RaspberryMotors/#description

Tkinter Canvas Freezes Program

For my class, I am creating a "Mandelbrot Explorer" program. There is one main issue: I lose control of the GUI (all written in Tkinter/Ttk, in Python 2.7) when actually drawing to the Canvas.
Here is my code:
# There is some code above and below, but only this is relevant
for real, imag in graph.PlaneIteration(self.graph.xMin, self.graph.xMax, resolution, self.graph.yMin, self.graph.yMax, resolution, master = self.graph, buffer_action = self.graph.flush):
# the above line iterates on the complex plane, updating the Canvas for every x value
c = complex(real, imag)
function, draw, z, current_iter = lambda z: z**2 + c, True, 0, 1
while current_iter <= iterations:
z = function(z)
if abs(z) > limit:
draw = False
break
current_iter += 1
self.progressbar.setValue(100 * (real + self.graph.xMax) / total)
color = self.scheme(c, current_iter, iterations, draw)
# returns a hex color value
self.graph.plot(c, color)
# self.graph is an instance of my custom class (ComplexGraph) which is a wrapper
# around the Canvas widget
# self.graph.plot just creates a line on the Canvas:
# self.create_line(xs,ys,xs+1,ys+1, fill=color)
My issue is that when run, the graphing takes a while - about 30 seconds. In this time, I cannot use the GUI. If I try to, the window freezes and only unfreezes once the drawing is done.
I tried using threading (I enclosed the entirety of the upper code in a function, thread_process):
thread.start_new_thread(thread_process, ())
However, the problem remains.
Is there a way to fix this? Thanks!
You can execute your loop "threaded" with Tkinter by implicitly returning to Tkinter's main loop execution after every point your draw. Do this by using widget.after to register the next function call:
plane = graph.PlaneIteration(...)
def plotNextPoint():
try:
real, imag = plane.next()
except StopIteration:
return
c = complex(real, imag)
...
self.graph.plot(c, color)
self.graph.after(0, plotNextPoint)
plotNextPoint()
This way, after each point you draw, the Tkinter mainloop will run again and update the display before calling your plotNextPoint function again. If this is too slow, try wrapping the body of plotNextPoint in a for _ in xrange(n) loop to draw n points between redraws.
You're right about the cause of the problem—the GUI event loop is not running while you're busy running this code.
And you're right about threading being a good solution. (The other major solution is to break the job up into smaller subtasks and have each one schedule the next. For a more detailed overview of the options and all of the wrinkles, see Why your GUI app freezes.)
But it's not quite as simple as putting the whole thing on a thread.
Unfortunately, Tkinter (like many GUI frameworks) is not free-threaded. You cannot call methods on any GUI objects from a background thread. If you do, different things happen on different platforms and versions, ranging from blocking the main thread to crashing the program to raising exceptions.
Also, remember that, even without Tkinter, you can't safely share mutable objects between threads without some kind of synchronization. And you're doing exactly that with the Tkinter objects, right?
The Tkinter wiki explains one way to get around both of these problems at once in Tkinter and Threads: Create a Queue, have the background thread put messages on it, and have the main thread check it every so often (e.g., by using after to schedule a nonblocking get every 100ms until the background thread is done).
If you don't want to come up with a "protocol" for passing data from the background thread to the main thread, remember that in Python, a bound method, or a tuple of a bound method and some arguments, it perfectly good, passable data. So, instead of calling self.graph.plot(c, color), you can just self.q.put((self.graph.plot, c, color)).
The library mtTkinter wraps this all up for you, making it look like Tkinter is free-threaded by using a Queue in the background. It isn't highly tested or frequently maintained, but even if it doesn't work in the future it still makes great sample code.

Categories

Resources