PsychoPy playing sounds with irregular delay in an EEG experiment - python

I'm using PsychoPy to prepare an oddball paradigm in which I preset a picture on the screen for 7 seconds and during the picture presentation 14 consecutive sounds are displayed every half a second. For every sound onset I send a trigger to the parallel port. I then ran timing tests to see whether the sounds are played at the same time as the triggers.
While the triggers seem to be fine and arrive every half a second, this is not the case for the sounds. The sounds are played with a delay of 100ms-200ms after the trigger and the difference between consecutive sounds' onsets is not half a second. The size of the sound file doesn't make a difference and the delays seem to be the case both for very short sounds (10ms) and longer ones (250ms). I tried to add ISI at the very beginning of each trial and load both a picture and the sounds during that period but it didn't help.
I also tried to add the ISI before every sound and load the sounds during these shorts ISIs but it didn't help either. Do you have any idea how the problem could be solved? Is there any way ISI could help? Am I supposed to have one ISI at the beginning of the trial to load all the stimuli for that trial or multiple ISIs per trial, one per stimulus?
The delay for the visual stimulus (i.e. the picture) is small and constant across trials.
The code below plays only two sounds but demonstrates the issue well.
from __future__ import absolute_import, division
from psychopy import locale_setup, sound, gui, visual, core, data, event, logging, parallel
from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED,
STOPPED, FINISHED, PRESSED, RELEASED, FOREVER)
# Setup the Window
win = visual.Window(
size=(1920, 1080), fullscr=True, screen=0,
allowGUI=True, allowStencil=False,
monitor='testMonitor', color=[-1, -1, -1], colorSpace='rgb',
blendMode='avg', useFBO=True)
snd_2_tr = 'sound1.wav'
snd_3_tr = 'sound2.wav'
picture_tr = 'pic.jpg'
win.setRecordFrameIntervals(True)
win._refreshThreshold = 1 / 60.0 + 0.004
image_tr = visual.ImageStim(
win=win, name='image_tr',
image='sin', mask=None,
ori=0, pos=(0, 0), size=None,
color=[1,1,1], colorSpace='rgb', opacity=1,
flipHoriz=False, flipVert=False,
texRes=128, interpolate=True, depth=0.0)
sound_2_tr = sound.Sound('A', secs=-1)
sound_2_tr.setVolume(1)
sound_3_tr = sound.Sound('A', secs=-1)
sound_3_tr.setVolume(1)
image_port_tr = parallel.ParallelPort(address='0xD010')
sound_2_port_tr = parallel.ParallelPort(address='0xD010')
sound_3_port_tr = parallel.ParallelPort(address='0xD010')
ISI_17 = core.StaticPeriod(win=win, screenHz=60, name='ISI_17')
# ------Prepare to start Routine "main_tr"-------
main_trClock = core.Clock()
t = 0
main_trClock.reset() # clock
frameN = -1
continueRoutine = True
# keep track of which components have finished
main_trComponents = [image_tr, sound_2_tr, sound_3_tr, image_port_tr, sound_2_port_tr, sound_3_port_tr, ISI_17]
for thisComponent in main_trComponents:
if hasattr(thisComponent, 'status'):
thisComponent.status = NOT_STARTED
# -------Start Routine "main_tr"-------
while continueRoutine:
# get current time
t = main_trClock.getTime()
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
# update/draw components on each frame
# *image_tr* updates
if frameN >= 60 and image_tr.status == NOT_STARTED:
# keep track of start time/frame for later
image_tr.tStart = t
image_tr.frameNStart = frameN # exact frame index
image_tr.setAutoDraw(True)
if image_tr.status == STARTED and frameN >= (image_tr.frameNStart + 420):
image_tr.setAutoDraw(False)
# start/stop sound_2_tr
if frameN >= 90 and sound_2_tr.status == NOT_STARTED:
# keep track of start time/frame for later
sound_2_tr.tStart = t
sound_2_tr.frameNStart = frameN # exact frame index
sound_2_tr.play() # start the sound (it finishes automatically)
if sound_2_tr.status == STARTED and t >= (sound_2_tr.tStart + 0.25):
sound_2_tr.stop() # stop the sound (if longer than duration)
# start/stop sound_3_tr
if frameN >= 120 and sound_3_tr.status == NOT_STARTED:
# keep track of start time/frame for later
sound_3_tr.tStart = t
sound_3_tr.frameNStart = frameN # exact frame index
sound_3_tr.play() # start the sound (it finishes automatically)
if sound_3_tr.status == STARTED and t >= (sound_3_tr.tStart + 0.25):
sound_3_tr.stop() # stop the sound (if longer than duration)
# *sound_1_port_tr* updates
if frameN >= 60 and image_port_tr.status == NOT_STARTED:
# keep track of start time/frame for later
image_port_tr.tStart = t
image_port_tr.frameNStart = frameN # exact frame index
image_port_tr.status = STARTED
win.callOnFlip(image_port_tr.setData, int(triggers_image_tr))
if image_port_tr.status == STARTED and frameN >= (image_port_tr.frameNStart + 15):
image_port_tr.status = STOPPED
win.callOnFlip(image_port_tr.setData, int(0))
# *sound_2_port_tr* updates
if frameN >= 90 and sound_2_port_tr.status == NOT_STARTED:
# keep track of start time/frame for later
sound_2_port_tr.tStart = t
sound_2_port_tr.frameNStart = frameN # exact frame index
sound_2_port_tr.status = STARTED
win.callOnFlip(sound_2_port_tr.setData, int(triggers_sound_2_tr))
if sound_2_port_tr.status == STARTED and frameN >= (sound_2_port_tr.frameNStart + 15):
sound_2_port_tr.status = STOPPED
win.callOnFlip(sound_2_port_tr.setData, int(0))
# *sound_3_port_tr* updates
if frameN >= 120 and sound_3_port_tr.status == NOT_STARTED:
# keep track of start time/frame for later
sound_3_port_tr.tStart = t
sound_3_port_tr.frameNStart = frameN # exact frame index
sound_3_port_tr.status = STARTED
win.callOnFlip(sound_3_port_tr.setData, int(triggers_sound_3_tr))
if sound_3_port_tr.status == STARTED and frameN >= (sound_3_port_tr.frameNStart + 15):
sound_3_port_tr.status = STOPPED
win.callOnFlip(sound_3_port_tr.setData, int(0))
# *ISI_17* period
if frameN >= 0 and ISI_17.status == NOT_STARTED:
# keep track of start time/frame for later
ISI_17.tStart = t
ISI_17.frameNStart = frameN # exact frame index
ISI_17.start(60 * frameDur)
elif ISI_17.status == STARTED: # one frame should pass before updating params and completing
# updating other components during *ISI_17*
image_tr.setImage(picture_tr)
sound_2_tr.setSound(snd_2_tr, secs=0.25)
sound_3_tr.setSound(snd_3_tr, secs=0.25)
# component updates done
ISI_17.complete() # finish the static period
# check if all components have finished
if not continueRoutine: # a component has requested a forced-end of Routine
break
continueRoutine = False # will revert to True if at least one component still running
for thisComponent in main_trComponents:
if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
continueRoutine = True
break # at least one component has not yet finished
# check for quit (the Esc key)
if endExpNow or event.getKeys(keyList=["escape"]):
core.quit()
# refresh the screen
if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
win.flip()

Related

How to call a function once in a while loop

I have a robotic code, that does the following:
camera starts processing and taking images
Mounting Holes (hough transform) function detection is activated
The holes are drawn on the image
approachcirlce function moves robot towards one of the set coordinates
I have two issues :
The mounting holes keep getting called even after detecting the coordinates once.
The robot in the approachcircle function cant move to one coordinates then onto the other. It keeps going back and forth as the x and y aren't specifically set to finish the first set of coordinates first. i.e : between two circles it does not reach either centers as expected. it never reaches the center of a detected circle if its more than one
I want the code to call the mountingholes function once and have the robot to move to each recorded coordinates, after the intial set of coordinates is done. I will have the robot move to another area and start doing the process again. I'm assuming the problem is that the functions are in the camera processing loop which is run indefinitely
The code is below:
##Def:
def approachcircle (r,t,z):
move = robot.Pose()*transl(r,t,z)
robot.MoveL(move)
def approacharea (z):
move = robot.Pose()*transl(0,0,z)
robot.MoveL(move)
def MountingHoles(img,thresh,r):
minR = r
CannyHighT = 50
min_points = 15 #param2
img_1= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
#img3 = cv2.inRange(img_1, thresh, 255)
circles = cv.HoughCircles(img_1,cv.HOUGH_GRADIENT, 1, 2*minR, param1=CannyHighT,
param2=min_points, minRadius=minR, maxRadius=220)
return circles
#Installation
from robolink import * # RoboDK API
from robodk import * # Robot toolbox
RDK = Robolink()
pose = eye()
ITEM_TYPE_ROBOT
RDK = robolink.Robolink()
robot = RDK.Item('TM12X')
import_install('cv2', 'opencv-python')
import cv2 as cv
import numpy as np
import numpy
#----------------------------------
# Settings
PROCESS_COUNT = -1 # How many frames to process before exiting. -1 means indefinitely.
CAM_NAME = "Camera"
DISPLAY_SETTINGS = True
WDW_NAME_PARAMS = 'RoboDK - Blob detection parameters'
DISPLAY_RESULT = True
WDW_NAME_RESULTS = 'RoboDK - Blob detections1'
# Calculate absolute XYZ position clicked from the camera in absolute coordinates:
cam_item = RDK.Item(CAM_NAME, ITEM_TYPE_CAMERA)
if not cam_item.Valid():
raise Exception("Camera not found! %s" % CAM_NAME)
cam_item.setParam('Open', 1) # Force the camera view to open
#----------------------------------------------
# Create an resizable result window
if DISPLAY_RESULT:
cv.namedWindow(WDW_NAME_RESULTS) #, cv.WINDOW_NORMAL)
#----------------------------------------------
# capture = cv.VideoCapture(0)
# retval, image = capture.read()
#----------------------------------------------
# Process camera frames
count = 0
while count < PROCESS_COUNT or PROCESS_COUNT < 0:
print("=============================================")
print("Processing image %i" % count)
count += 1
#----------------------------------------------
# Get the image from RoboDK
bytes_img = RDK.Cam2D_Snapshot("", cam_item)
if bytes_img == b'':
raise
# Image from RoboDK are BGR, uchar, (h,w,3)
nparr = np.frombuffer(bytes_img, np.uint8)
img = cv.imdecode(nparr, cv.IMREAD_UNCHANGED)
if img is None or img.shape == ():
raise
#----------------------------------------------
# Detect blobs
keypoints = MountingHoles(img,250,50)
i = 0
#----------------------------------------------
# Display the detection to the user (reference image and camera image side by side, with detection results)
if DISPLAY_RESULT:
# Draw detected blobs and their id
i = 0
for keypoint in keypoints[0,:]:
cv.putText(img, str(i), (int(keypoint[0]), int(keypoint[1])), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv.LINE_AA)
cv.circle(img, (int(keypoint[0]), int(keypoint[1])), int(keypoint[2]), (0, 0, 255), 15)
#
i += 1
# Resize the image, so that it fits your screen
img = cv.resize(img, (int(img.shape[1] * .75), int(img.shape[0] * .75)))#
cv.imshow(WDW_NAME_RESULTS, img)
key = cv.waitKey(500)
if key == 27:
break # User pressed ESC, exit
if cv.getWindowProperty(WDW_NAME_RESULTS, cv.WND_PROP_VISIBLE) < 1:
break # User killed the window, exit
#--------------------------------------------------------------------------------------------
# Movement functions
r=0
t=0
i=0
#approacharea(200)
for keypoint in keypoints[0,:]:
#print("id:%i coord=(%0.0f, %0.0f)" % (i, X, Y))
X= int(keypoint[0])-320
Y=int(keypoint[1])-240
r=int(keypoint[2])
print("id:%i coord=(%0.0f, %0.0f)" % (i, X, Y))
if X!= 0 or Y!=0 :
r=X*0.1
t=Y*0.1
approachcircle(r,t,0)
i+=1

Tektronix MSO56 Oscilloscope: SEVERE Memory Error

I am collecting over 100k FastFrame images (100 frames, with 15k points each), with summary mode and collecting them via python pyvisa using ni-visa.
The error is as follows:
SEVERE
The system is low on memory. Some results may be incomplete. To remedy, reduce record length or remove one or more analytical features such as math, measurements, bus decode or search.
After that, I can disconnect, connect again, send commands which update the window, but cannot query anything.
I suspect it is something to do with a memory leak on MSO56 RAM, or communication queue.
Commands like *RST, CLEAR, LCS, and FACTORY do not fix the error.
import pyvisa
import time
if __name__ == '__main__':
## DEV Signal
rm = pyvisa.ResourceManager()
ll = rm.list_resources()
print('\n\n\n----------------------\nAvailable Resources\n----------------------')
for i in range(len(ll)):
print(F'Resource ID {i}: {ll[i]}')
#i = int(input(F"\n\nPlease select 'Resource ID' from above: "))
i=0;
inst = rm.open_resource(ll[i])
inst.timeout = 10000
reset = inst.write("*RST")
ind = inst.query("*IDN?")
print(F"\nResource {i}: {ind}")
inst.write('C1:OUTP ON')
inst.write('C2:OUTP ON')
# Wave signal
Ch = 1; # channel 1 || 2
wave_name = 'UT1'
Frq = 500000; #Hz
Peri = 1/Frq;# Length of waveform
print(F"Period: {Peri}")
# trigger on channel 2
inst.write(F'C2:BSWV WVTP,SQUARE,FRQ,{Frq},AMP,1,OFST,0,DUTY,1')
# signal on channel 1
inst.write(F'C1:BSWV WVTP,SQUARE,FRQ,{Frq},AMP,1,OFST,0,DUTY,10')
inst = []
scope_ip = '192.168.0.10';
rm = pyvisa.ResourceManager()
ll = rm.list_resources()
print(ll)
for l in ll:
if scope_ip in l:
vScope = rm.open_resource(l)
#vScope.clear()
#vScope.close()
vScope.timeout = 2000
## attempts to fix Memory Error
vScope.write_raw('FPANEL:PRESS RESTART')
vScope.write_raw('*PSC ON')
vScope.write_raw('*CLS')
vScope.write_raw('FACTORY\n')
vScope.write_raw('*RST')
vScope.write_raw('*CLEAR')
vScope.write_raw('*ESE 1')
vScope.write_raw('*SRE 0')
vScope.write_raw('DESE 1')
print('\nESR')
print(vScope.query('*ESR?'))
#print('\nEVMSG?')
#print(vScope.query('*EVMsg?'))
#print(vScope.query('*ESE ?'))
# Display Wave Forms
vScope.write_raw('DISPLAY:WAVEVIEW1:CH1:STATE 1')
vScope.write_raw('DISPLAY:WAVEVIEW1:CH2:STATE 1')
# Vertical Command Groups.
vScope.write_raw('CH1:Coupling DC')
vScope.write_raw('CH2:Coupling DC')
vScope.write_raw('CH1:SCALE .5') # *10 for the range
vScope.write_raw('CH2:SCALE .5')
vScope.write_raw('CH1:Position 0')
vScope.write_raw('CH2:Position 0')
vScope.write_raw('TRIGGER:A:TYPE EDGE')
vScope.write_raw('TRIGGER:A:EDGE:SOURCE CH2')
vScope.write_raw('TRIGger:A:LEVEL:CH2 0')
vScope.write_raw('TRIGger:A:EDGE:SLOpe RISE')
vScope.write_raw('Horizontal:Position 10')
vScope.write_raw('Horizontal:MODE MANUAL')
vScope.write_raw('Horizontal:Samplerate 25000000000')
vScope.write_raw('HORIZONTAL:MODE:RECORDLENGTH 25000')
vScope.write_raw('DATA:SOURCE CH1')
vScope.write_raw('ACQUIRE:STOPAFTER SEQUENCE')## triggers re-read
nframes = 100;
vScope.write_raw(F"HORIZONTAL:FASTFRAME:COUNT {nframes}")
if int(1):
vScope.write_raw(F"DATA:FRAMESTART {1+nframes}")
else:
vScope.write_raw('DATA:FRAMESTART 1')
vScope.write_raw(F"DATA:FRAMESTOP {1+nframes}")
vScope.write_raw('HORIZONTAL:FASTFRAME:STATE OFF')
vScope.write_raw('HORIZONTAL:FASTFRAME:STATE ON')
vScope.write_raw('HORIZONTAL:FASTFRAME:SUMFRAME:STATE ON')
vScope.write_raw(F"HORIZONTAL:FASTFRAME:SELECTED {1+nframes}")
t0 = time.time()
for i in range(1000000):
vScope.write_raw('ACQUIRE:STATE 1') ## triggers re-read
vScope.query('*opc?')
vScope.write_raw(b'WFMOUTPRE?')
wfmo = vScope.read_raw()
vScope.write_raw('CURVE?')
curve = vScope.read_raw()
if i%100 ==0:
print(F"Iteration {i}")
print(F"Time Delta: {time.time()-t0}")
t0=time.time()
Poor Solution.
Restarting the Scope with the power button works.
I should have added that in the question, but takes ~ 2 minutes and is not an elegant solution.

Completion time of "_invalidate_internal" in Matplotlib increases linearly over extended periods. What is this function doing?

I am running a Python Tkinter GUI application for multiple days (ideally it will run continuously without needing to be restarted). After a few days, the program typically crashes with no explanation or error messages. I have used cProfile to profile the animation functionality of our Matplotlib graphs and search for possible slow-down points. The results clearly showed that the only function which increased in execution time over the program run period was this one:
/usr/lib/python3/dist-packages/matplotlib/transforms.py:134(_invalidate_internal)
I've looked at the Matplotlib source code (https://github.com/matplotlib/matplotlib/blob/main/lib/matplotlib/transforms.py), but I haven't been able to figure out what this function is doing. What does _invalidate_internal do, and is there anything I can do to prevent it from taking so long to execute?
For more context, our program has several animated matplotlib plots which graph inputs from sensor data over time. We plot the n most recent data points on each frame so that it gives the effect of a scrolling graph. Here is the animation code:
def animate(ii):
while True:
most_recent_time_graphed = param_dict[param_list[0]] #first, pulls up first plot
most_recent = reader.query_by_num(table="SensorData", num=1)
reader.commit() #if identical, do not animate
#then checks that plot's time list
if (len(most_recent) == 0):
break
#time_reader = datetime.strptime(most_recent[0][0], "%m/%d/%Y %H:%M:%S")
time_reader = datetime.datetime.fromtimestamp(most_recent[0][0])
if (len(most_recent_time_graphed.tList) != 0) and (time_reader == most_recent_time_graphed.tList[0]):
for i, param in enumerate(param_list, 1):
current_text = live_dict[param]
current_text.label.config(text=most_recent[0][i], fg="black", bg="white")
break #checks if the timestamp is exactly the same as prior, i.e. no new data points have been logged in this frame
#do I have to add an else?
else:
config_settings = csv_read()
c0, c1, c2 = config_dict['enable_text'], config_dict['num_config'], config_dict['provider_config']
c3, c4, c5 = config_dict['email_config'], config_dict['upper_config'], config_dict['lower_config']
for i, key in enumerate(param_dict, 1):
current_plot = param_dict[key]
current_param_val = float(most_recent[0][i])
current_text = live_dict[key] #update to live text data summary
if current_param_val > float(config_settings[c4][i-1]) or current_param_val < float(config_settings[c5][i-1]):
#only send text if enable_text is True
if config_settings[c0] == [str(True)]:
###sends text if new problem arises or every 5 minutes
if allIsGood[key] and Minute[key] == None:
print('new problem')
Minute[key] = datetime.now().minute
minuta[key] = Minute[key]
pCheck(float(config_settings[c4][i-1]),float(config_settings[c5][i-1]),key,current_param_val,config_settings[c1],config_settings[c2]) #uncomment to test emergency texts
elif allIsGood[key] == False and abs(Minute[key] - datetime.now().minute) % 5 == 0 and not (minuta[key] == datetime.now().minute):
print('same problem')
minuta[key] = datetime.now().minute
pCheck(float(config_settings[c4][i-1]),float(config_settings[c5][i-1]),key,current_param_val,config_settings[c1],config_settings[c2]) #uncomment to test emergency texts
#pass
#setting the parameter to not ok
allIsGood[key] = False
current_text.label.config(text=most_recent[0][i], fg="red", bg="white")
current_plot.plot_color = 'r'
else:
current_text.label.config(text=most_recent[0][i], fg="black", bg="white")
current_plot.plot_color = 'g'
###setting the parameter back to true and sending "ok" text
if allIsGood[key] == False:
Minute[key] = None
allOk(key,config_settings[c1],config_settings[c2])
pass
allIsGood[key] = True
data_stream = current_plot.incoming_data
time_stream = current_plot.tList
data_stream.insert(0, most_recent[0][i])
#time_f = datetime.strptime(most_recent[0][0], "%m/%d/%Y %H:%M:%S")
time_f = datetime.datetime.fromtimestamp(most_recent[0][0])
time_stream.insert(0, time_f)
if len(data_stream) < 20: #graph updates, growing to show 20 points
current_plot.make_plot()
else: #there are 20 points and more available, so animation occurs
data_stream.pop()
time_stream.pop()
current_plot.make_plot()
break

Move a stepper motor to an exact position

My setup: An Arduino that reads numbers from serial(steps), then it moves the stepper motor that many steps. It uses a4988 driver, a 12 volt power supply, 1.5 amp Nema 17. All of that is fine, my issue is on the Python side.
In Python, I have tried to create a function in a variety of ways.
I used tkinter to get the mouse position on the screen, it moves in the x axis rotating the stepper. The stepper has a flashlight just to shine it at anything I point at with my mouse.
Say there is a camera below the stepper, if I move my mouse over to an object it would move there, within a tolerance of 5 steps either way. +5, -5
The function I have tried to create should work like this.
while True:
#I change direction by writing 400, clockwise, or 401, anti clockwise to serial.
step(10)#move only 10 steps
step(10)#it should do nothing as it has already moved 10 steps.
step(15)#it should move 5 steps as it has moved 10 already
step(5)#change direction and move back 10 steps
I am somewhat decent at python(or so I think)
here is my most promising code so far:
#I have direction changes done by writing a 400 for clockwise or 401 for anti clockwise to serial.
import tkinter as tk
import serial
ser = serial.Serial("COM8", 9600)
root = tk.Tk()
wi = root.winfo_screenwidth() / 2
width = root.winfo_screenwidth()
hi = root.winfo_screenheight() / 2
height = root.winfo_screenheight()
sere = '\r\n'
sender = sere.encode('utf_8')
pps = height / 200
# p.p.s pixels per step divide that by the same thing to get correct.
#how many pixels are for 1 step
print(pps)
#number of pixels difference divided by pps to get steps, then round for ease of use
past = 0
print(height, width, wi) # height divided by pps should always = max steps in my case 200
def send(steps):
global past
global current
sere = '\r\n'
current = steps
neg = past - 5
pos = past + 5
if current != past:
if current > pos or current < neg:
if steps > 1:
sender = sere.encode('utf_8')
e = str(steps).encode('utf_8')
ser.write(e + sender)
past = steps
while True:
pos = root.winfo_pointerx() - wi
steps = round(pos / pps)
print(pos, steps) # shows cursor position
#direction code is not in here as I have no need for it atm.For one simple reason, IT DOESN'T WORK!
Please explain answers if possible, I am a long time lurker, and only made a account just now for this.Thank you all for any assistance.
Edit:
The question is how to make a function that moves a stepper motor to a certain number of steps.If you command it to do 10 steps twice, it only moves 10 steps.
step(10) stepper moves 10 steps, do it again the motor does nothing as it is already at 10 steps.
If I do step(15) it would only move another 5 as it is at 10 steps.
Edit2 Clarification:
By function I mean
def step(steps):
#logic that makes it work
For anyone else who has this problem and people seem to not help a ton, heres my new code to drive it,
past = 0
sender = sere.encode('utf_8')
dir1 = str(400).encode('utf_8')
dir2 = str(401).encode('utf_8')
def send(steps):
global past
global current
global sender
global dir1
global dir2
global stepstaken
sere = '\r\n'
current = steps
neg = past - 5 # tolerance both are unused atm
pos = past + 5 # tolerance
needed = current - past
print(needed, steps)
if current != past:
if needed > 0:
serialsteps = str(needed).encode('utf_8')
ser.write(dir1 + sender)
ser.write(serialsteps + sender)
if needed < 0:
needed = needed * -1
serialstepss = str(needed).encode('utf_8')
ser.write(dir2 + sender)
ser.write(serialstepss + sender)
past = steps
Here is my full code for mouse to stepper motor,
import tkinter as tk
root = tk.Tk()
import serial
import struct
import time
stepstaken = 0
ardunioport = "COM" + str(input("Ardunio Port Number:")) # port number only
ser = serial.Serial(ardunioport, 9600)
wi = root.winfo_screenwidth() / 2
width = root.winfo_screenwidth()
hi = root.winfo_screenheight() / 2
height = root.winfo_screenheight()
sere = '\r\n' #still don't understand why but it does not work without this.
sender = sere.encode('utf_8')
pps = width / 200 #how many pixels are in 1 step
past = 0
sender = sere.encode('utf_8')
dir1 = str(400).encode('utf_8')
dir2 = str(401).encode('utf_8')
def send(steps):
global past
global current
global sender
global dir1
global dir2
global stepstaken
#need overcome overstepping
sere = '\r\n'
current = steps
neg = past - 5 # tolerance
pos = past + 5 # tolerance
needed = current - past
print(needed, steps)
if current != past:
if needed > 0:
serialsteps = str(needed).encode('utf_8')
ser.write(dir1 + sender)
ser.write(serialsteps + sender)
if needed < 0:
needed = needed * -1
serialstepss = str(needed).encode('utf_8')
ser.write(dir2 + sender)
ser.write(serialstepss + sender)
past = steps
while True:
pos = root.winfo_pointerx() - wi
steps = round(pos / pps)
#print("Mouse Position " + str(pos) + " Steps Needed" + str(steps)) # shows cursor position
send(steps)
For me it goes to a Arduino, the Arduino is not programmed to accept negative numbers. Instead it removes the - sign, multiply negative by negative = positive. It sends the opposite direction code/number then the steps needed to get there. Good luck everyone. This code works for me, no promise it will work for you.If it does not I am sorry.

Stepper motor quits working after a short while, even though the code still works. (Raspberry Pi)

I am running the following code on my Raspberry Pi 4B, using Python 3.7.3:
from time import sleep
import RPi.GPIO as GPIO
import math
from watchgod import watch
g=open("/home/pi/Desktop/Int2/DesHeight.txt","r")
DesHeight = g.readline()
DesHeight1=float(DesHeight)
print(DesHeight1)
GPIO.cleanup()
DIR = 20
STEP = 21
CW = 0
CCW = 1
TX_ENC = 15
SPR = 200 # Steps per Rev [CONSTANT]
delay = .001 #Seconds per stepper pulse [CONSTANT]
ratio=24 #gear ratio [CONSTANT]
f = open("height.txt", "r")
y0 = f.readline()
y0 = float(y0)
d=1.25
r=d/2
theta0=y0/(2*math.pi*r)
GPIO.setmode(GPIO.BCM) # GPIO numbering
GPIO.setup(12,GPIO.OUT)
GPIO.output(12,1) # Turning on the "Enable Input"
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, CW) # Setting CW Direction
GPIO.setup(TX_ENC, GPIO.IN) # Encoder input setup
GPIO.add_event_detect(TX_ENC, GPIO.BOTH)
Tx = 0
MODE = (16,17,18,19) # GPIO 16 is the standby input. It needs to be high for anything to move
GPIO.setup(MODE,GPIO.OUT)
RESOLUTION = {'Standby':(0,0,0,0),
'Full':(1,0,0,0),
'Half':(1,1,0,0),
'1/4':(1,0,1,0),
'1/8':(1,1,1,0),
'1/16':(1,0,0,1),
'1/32':(1,1,0,1),
'1/64':(1,0,1,1),
'1/128':(1,1,1,1)}
GPIO.output(MODE,RESOLUTION['Full'])
ass = (0,0,0,0)
pp = list(ass)
pp[0] = GPIO.input(MODE[0])
pp[1] = GPIO.input(MODE[1])
pp[2] = GPIO.input(MODE[2])
pp[3] = GPIO.input(MODE[3])
ass = tuple(pp)
if(ass == RESOLUTION['Standby']):
res = 0
elif(ass == RESOLUTION['Full']):
res = 200
elif(ass == RESOLUTION['Half']):
res = 400
elif(ass == RESOLUTION['1/4']):
res = 800
elif(ass == RESOLUTION['1/8']):
res = 1600
elif(ass == RESOLUTION['1/16']):
res = 3200
elif(ass == RESOLUTION['1/32']):
res = 6400
elif(ass == RESOLUTION['1/64']):
res=12800
elif(ass == RESOLUTION['1/128']):
res=25600
else:
print("Whoops lol")
while(True):
for changes in watch('/home/pi/Desktop/Int2/DesHeight.txt'):
g=open("/home/pi/Desktop/Int2/DesHeight.txt","r")
DesHeight = g.readline()
DesHeight1=float(DesHeight)
f = open("height.txt", "r")
y0 = f.readline()
y0=float(y0)
while(abs(y0-DesHeight1)>.001):
if(y0 < DesHeight1):
while(y0 < DesHeight1):
GPIO.output(DIR,CCW)
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
Tx = Tx + 1
theta0=theta0+1/res*1/ratio#*1/gearratio
y0 = y0+2*5/4*14/15*.9944*math.pi*(1/res*1/ratio)*r
else:
while(y0 > DesHeight1):
if(y0>0):
GPIO.output(DIR,CW)
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
Tx = Tx - 1
theta0=theta0-1/res*1/ratio#*1/gearratio
y0 = y0-2*5/4*14/15*.9944*math.pi*(1/res*1/ratio)*r
y0 = str(y0)
print(y0)
f.close()
f = open('height.txt', 'w')
f.write(y0)
f.close()
Essentially, what I am trying to do is read the height of a machine from a text file, then compare it with the desired height, as written in a separate text file. When the code detects a change in the desired height, it checks to make sure that the actual height and the desired height are within 1/1000 of an inch of each other, and if not, it moves a NEMA-17 motor until this condition is met.
The problem I am encountering is that if this code is left to run for a little bit (usually around 40 seconds) the stepper motor ceases to run when I change the desired height. The code itself runs, taking as long as expected to "move" the motor and also calculating the height and returning to the top of the while loop, but the motor itself remains stagnant. This does not occur if new changes to the desired height file are implemented immediately. I am at a loss as to what this could be and could use some help.
Okay, so following the advice of RootTwo, and learning how to use an oscilloscope, I was able to isolate part of the problem and determine a solution. Either the pi or the driver quits supplying a voltage to the motor after about 90 seconds of inactivity. I was not able to find a way to continuously keep the motor at holding torque, but I was able to make it so that the code will reinitialize after a long break by moving the while(True): and watchdog command to the beginning of the code. My problem is technically solved, but my question is yet unanswered: Why does my pi stop giving a signal to the driver board?

Categories