pootle / pisteppers

Python app to drive stepper motors from a raspberry pi with good performance and minimal extra hardware. A simple built in web interface provides remote control
The Unlicense
25 stars 2 forks source link

stepwavetest abort with error #4

Closed LinuksGuru closed 4 years ago

LinuksGuru commented 4 years ago

Hi !

1) stepwavetest called from python script aborts with error (attached). I have "clif.exit()" at the end of my python script.

2) Default "pulseontime" must have been increased from 2 to 10 in order to work with DM556. This is not error in code, just a suggestion to put somewhere in comments.

Thanks in advance.

root@raspberrypi:/home/pi/LinW# python3 1test.py 
stepper control.RA: action >setup< started: .
stepper control.RA: action >pins mode set< complete: output.
stepper control.RA: action >enable< complete: disable.
stepper control.DEC: action >setup< started: .
stepper control.DEC: action >pins mode set< complete: output.
stepper control.DEC: action >enable< complete: disable.
stepper control: action >run agent< started: wave agent: wave limits, pulses per wave: 12000, seconds per wave: 1800.000000, control blocks per wave: 25016.
stepper control.RA: action >enable< complete: enable.
stepper control.RA: action >direction< complete: forward.
stepper control.RA: action >micro step level< complete: 1/1.
stepper control: action >wavepulsemaker< started: ready to start waves with 1 motors.
stepper control: action >run agent< failed: "done" cannot finish - wavepulsemaker.
stepper control.RA: action >pulsegen< progress: ramp up complete at 10.97 in 2401 ticks with 18238 to do at full speed.
stepper control.RA: action >pulsegen< progress: starting ramp down.
wrapped call got exception ><class 'RuntimeError'><
stepper control.RA: action >pulsegen< complete: pulse train complete in 23040 ticks.
wrappedCall: <class 'RuntimeError'> exception in called code - 
generator raised StopIteration
  File "/home/pi/LinW/stepwavetest.py", line 113, in wrappedRunMethod
    meth(**kwargs)
  File "/home/pi/LinW/stepwavetest.py", line 441, in checkPending
    self.morewaves = self.prepareWave()
  File "/home/pi/LinW/stepwavetest.py", line 469, in prepareWave
    pulseparams = tuple(self.wpgen.__next__() for i in range(groupleft))

^CException ignored in: <module 'threading' from '/usr/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 1281, in _shutdown
    t.join()
  File "/usr/lib/python3.7/threading.py", line 1032, in join
    self._wait_for_tstate_lock()
  File "/usr/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt
LinuksGuru commented 4 years ago

Tried default example, straight from stepwavetest without any mods, same error. Could it be Python 3.5 -> 3.7 issues?

LinuksGuru commented 4 years ago

Seems like this is related to "PEP 479 -- Change StopIteration handling inside generators" https://www.python.org/dev/peps/pep-0479/#consequences-for-existing-code

Can you please correct this ? Thanks in advance.

pootle commented 4 years ago

Haven't looked in detail, but the stopiteration change looks likely to be the problem. - especially since I use nested generators. I'll have to have a think about this!

LinuksGuru commented 4 years ago

Yes, switching to Python 3.6 fixed problem with "StopIteration handling inside generators". However, this script (from sample) never finishes after motor stop and requires keyboard interrupt to quit. Or I'm missing something there?

#!/usr/bin/python3.6

import sys
sys.path.append('/home/pi/LinW')
import stepwavetest as st

clif=st.makeagents()

# warp : microstepset stepping mode - 1,2,4,8 or 16
m1p={'motor':'RA', 'startsr':100, 'maxsr':1200, 'overreach':1.1, 'ramp':1, 'totalsteps':1*16*48*30, 'pulseontime':10, 'forward':True,'warp':1}
m2p={'motor':'DEC', 'startsr':300, 'maxsr':850, 'overreach':1.1, 'ramp':.5, 'totalsteps':1*16*48*95, 'pulseontime':5, 'forward':True,'warp':2}

clif.runmotors((m1p,))
clif.exit()
pootle commented 4 years ago

No it's not meant to exit - it just waits for another command, it needs an app wrapped round it really.

LinuksGuru commented 4 years ago

OK, thanks, I see, it some kind of threaded event loop. Which command is used to end it, apart from brute force? del clif? Maybe it makes sense to make sa global variable in an example file, so it would be convenient to check sa.activeaction if motors are running before destroying object? I can upload extended example but I need better understanding of the underlying logic of this really great piece of software. Till now I'm not aware of any other comparable library which does this kind of acceleration, deceleration, and very stable run at high RPM.

pootle commented 4 years ago

Yes it needs some more examples, I'm just busy with something else, but I'll try and add some more info over the next couple of days

LinuksGuru commented 4 years ago

OK, thanks. From my side I'll try simple GUI, hopefully it will work as expected.

LinuksGuru commented 4 years ago

It would be nice to have possibility in this method to specify "steplevels" as "0" or "-1", if driver has dip switches only for step level settings, otherwise one had to reserve one IO pin as dummy.

sa = onewave(inq=cmdq, outq=respq, motors=(
          {'name':'RA',  'loglevel':7, 'pins':{'enable':21, 'direction': 12, 'step': 13, 'steplevels': (20,19,16)}}
        , {'name':'DEC', 'loglevel':7, 'pins':{'enable':25, 'direction': 18, 'step': 27, 'steplevels': (24,23,22)}}))
pootle commented 4 years ago

I'm working on a big update so I can put a web front end on this. I've done the -1 for no pin bit as well. I've just got some basic testing to finish and I'll update it (without the web front end - that will follow)

LinuksGuru commented 4 years ago

Ok, great, thanks. As I understood ramp an empirical value rather then precise timing, please correct if I'm wrong here. If there is a need to sync 2 step motors, a time interval (e.g. 2sec) would be more convenient.

ramp = 0.1, ramp up complete at  1.02 in 927 ticks with 21186 to do at full speed.
ramp = 1, ramp up complete at 10.17 in 9271 ticks with 4498 to do at full speed.
ramp = 5, ramp up complete at 20.17 in 11520 ticks with 0 to do at full speed.

BTW, on Rasp3, with DM556, NEMA 23 motor, 24V I have reached 4500 pulses/sec (maxsr), at 5000 motor stalled.

LinuksGuru commented 4 years ago

Hi! Can you please commit new version? Thanks in advance.

pootle commented 4 years ago

I've pushed the current working version, I'm still doing final testing and tidying up parts of this though....

I'm not quite sure I understand what you want by sync motors, but if you set 2 motors with identical params, they should be exactly in sync when run.

LinuksGuru commented 4 years ago

Sync 2 motors I meant both have same acceleration time but different RPM. For example lathe chuck (high RPM) and feedshaft (very low RPM). "rampintvl" in json config - is this ramp time in sec? If so it becomes very simple.

pootle commented 4 years ago

Ah! I understand. The ramp works like this: set initial interval, use that interval until rampintvl time expires, then divide interval by rampfact. repeat until calcuation passes minstep, at which point lock to minstep and run constant speed.

So if you set initial intervals and min intervals to the required ratio, with all other numbers the same, then I think you will get right answer.

LinuksGuru commented 4 years ago

Hi! Did you updated utils after recent pisteppers update? After today's git pull got this:

Traceback (most recent call last):
  File "./app.py", line 7, in <module>
    webserv.runmain()
  File "/usr/local/lib/python3.7/dist-packages/pootlestuff/webserv.py", line 37, in runmain
    configmodule=importlib.import_module(configpath.stem)
  File "/usr/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/pi/LinW/pisteppers/config_full.py", line 10, in <module>
    from pootlestuff.watchables import loadsettings
ImportError: cannot import name 'loadsettings' from 'pootlestuff.watchables' (/usr/local/lib/python3.7/dist-packages/pootlestuff/watchables.py)
pootle commented 4 years ago

oh yes, meant to add it to the readme. pull the utils again AND rerun setup in pootles_utils.

LinuksGuru commented 4 years ago

pootles_utils updated 10 days ago, looks like its not most recent version. Please upload newest one. Thanks.

pootle commented 4 years ago

Oops yes - it's there now

LinuksGuru commented 4 years ago

Ok, so far it runs. There are number of issues: 1) Microstep level set in json (in my case to 1) displayed correctly only in first (fast) setting, in the other 2 (slow, smartfast) its for whatever reason 2. Not critical, can be fixed manually with popup menu. 2) What is current analog of old "maxsr"? Can't find it anywhere. 3) All params default, go to 100000 - no acceleration/deceleration. Could be maxsr to high for so short time? 4) All params default, run, motor spinning, then stop, nothing happens. Close (abrupt stop) works. 5) Did I figured out correctly how to send data to pisteppers web server? It is quite convinient to link pisteppers with another app via HHTP. Keywords can be taken out of "simplestepperweb.py". Is this something like this? http://localhost:8000/index.html?usercmd=goto&userdir=fwd&userpos=1000

pootle commented 4 years ago
  1. I'll have a look at that
  2. rather than steps per second, it's now minimum step interval (using max step level as base - it scales it) it is now minstep - float number in seconds
  3. that's strange - should have sensible values in from the json file. I'll do a clean install and see if I can reproduce
  4. run should run forever - goto should stop when target reached, but if other params have strange values, it may be confused.
  5. Yes, once initial page is setup everything happens by updates with responses - check the js code the web page uses to get a basic idea. All the fields are identified by id, but that could change if the page layout or software changes. I have half a solution to using fixed id's - I'll think about that.

I'm just finishing a doc which explains the json file...