gin66 / FastAccelStepper

A high speed stepper library for Atmega 168/328p (nano), Atmega32u4, Atmega 2560, ESP32, ESP32S2, ESP32S3, ESP32C3 and Atmel SAM Due
MIT License
286 stars 67 forks source link

Quickly changing target position or speed while stepper running causes RAMP generator loop #103

Closed DreiDe closed 2 years ago

DreiDe commented 2 years ago

Hi @gin66, for seek of syncing two stepper motors and adjusting their speed to measured load, I update their speed about 20 times a second while they are running in non continuous (position) mode. However after a minute or two they randomly stop. I debugged the RAMP queue and found out, that it has about 5-6 items in it (continuously switching). The getCurrentPosition() Output stays constant.

What is pretty weired to me, after approx 2-3 minutes, the stepper randomly starts again: The queue entrys increase and the position updates until the target position is reached. Than it continues normally.

I also checked the error flags of my TMC2130 Stepper drivers but they are running as expected. My board is an ESP32.

I didn't make a minimal example yet, but can if you wish. I just thought that you may already be having an idea what the cause could be. I'm pretty sure that the problem is caused by FastAccelStepper as I tried to eliminate all other possibilities.

gin66 commented 2 years ago

Thanks for reporting and sorry for the inconvenience..

I have not encountered this behavior, so I do not know the root cause yet.

Even it may not be relevant, but could you please check, if you use esp-idf.4.4 or earlier. As this is difficult to check, it can be tested, if a specific preprocessor variable is defined, like this:

#if defined(MCPWM_TIMER0_PHASE_DIRECTION_S)
    // output something 
#endif

A minimal example would be of great help, but I can try on my side, too.

For the time to restart: was it 2-3 minutes or eventually around 4-5 minutes ? 268 seconds is approx. one 32bit counter overflow at 16MHz, which would at least fit to the restart time.

gin66 commented 2 years ago

Both stepper stop at same time and restart at same time ? Or only one ?

DreiDe commented 2 years ago

Thanks for your answer gin66.

Actually the described issue seems to be my fault. I could not reproduce the issue in a minimal example. The cause propably was a faulty storage function, that blocked the program execution for some milliseconds. If it was called shortly before the stepper reached it's target position, it caused the stepper motor to run over the target position.

However maybe at this point there is an actual issue in the FastAccelStepper library. Once the storage function finished FastAccelStepper was not able to recover from this error. Altough my program continued executing normally, FastAccelStepper kept generating the same pulses for for about 2 minutes (I didn't measure the exact time), which caused the stepper motor to stand still, while still maintaining tension. Does that sound plausible?

Another thing i noticed is, that isMotorRunning occassionally returns 0 altough the motor is still running towards it's target position. As i implemented a queue which continues with the next command, after the previous finished (as indicated by isMotorRunning), that causes the stepper to stop randomly. Maybe a onMoveFinished hook would be a good feature.

gin66 commented 2 years ago

Hello Dreide,

sorry for not seeing your reply earlier.

I only have made the experience, that writing to SPIFFS causes hiccups of a running stepper motor. Cause is, that the FastAccelStepper function creating the low level stepper commands is running as a task on esp32, which is blocked by the SPIFFS writes. But after SPIFFS writes are completed, the stepper continues running.

That the stepper runs over the target position should actually not happen, unless interrupts are blocked for long time.

Your phenomenon of having stepper motor being in standstill with stepper queue running (only executing pauses) could in my opinion only happening, if pause_ticks_left contains a large value (or a negative value was stored in this uint32_t). One obvious issue of the current implementation, that this pause will execute unconditionally and uninterruptible. So if due to a combination of speed/acceleration of a command, a big pause value is stored, then any acceleration/speed updates/position command has to wait looong time. This is a bug.

That isMotorRunning() occasionally returns 0, while motor is running, I do not understand. Judging by the code, I cannot find any case, where this could happen. Can you please provide a minimal example to demonstrate this issue ?

I have always wondered, if FastAccelStepper should provide a high level command queue with position/speed/acceleration entries. Until now I think, that this would make FastAccelStepper way more complex and would lead to RAM/ROM resource and timing issues on AVR. While on application level, this could be easily done by polling isRampGeneratorActive() and calling the next move()/moveTo()on return of false. isRampGeneratorActive()is more appropriate, because then the queue can be filled continuously even while processing commands.

The onMoveFinished hook could be a possibility.

gin66 commented 2 years ago

Seems the test case is not sufficient. After reworking the forceStop machinery, the issue has surfaced on the emulator. Seems to hit just the right timing window and boom.

gin66 commented 2 years ago

Not sure, if this really the same issue. Anyway, it will be fixed in 0.26.1 on a branch