Improvements to AccelStepper...

1,881 views
Skip to first unread message

Ray Jones

unread,
Jul 5, 2014, 7:22:21 PM7/5/14
to accels...@googlegroups.com
I'm a new member of the group, having just started to play with AccelStepper for a stepper driven rotary table conversion project I'm working on.

First off the library is fantastic. I tried to read the paper the design is based upon, but found it a hard read :-)

I've started looking at the V1.41 code to understand what is going on for an improvement I'll discuss further on.
But the first thing I noticed was setAcceleration() whilst checking for zero, does not guard for a negative value?
The simple fix is  

  if (acceleration <= 0.0)
  {
      return;
  }

instead of

  if (acceleration == 0.0)
  {
      return;
  }

The big modification I'm contemplating to attempt is modifying the code so that instead of needing to call AccelStepper::Run frequently, instead use a 16 bit timer interrupt (timer1 on a UNO) and let the stepping happen in the background.
I have already done this with a simple linear speed algorithm using the "FlexiTimer2" library and a callback function.
I found the smoothness of drive is superb whereas it becomes lumpy using "co-operative multitasking" via the usual delay routines, or needing to frequently call into AccelStepper::Run (ala micros).

My thoughts are to re-program the timer compare value when _stepInterval is re-calculated (in the ISR), and the ISR becomes much like calling Run except it always steps if speed / position error !=0.
Obviously this would make the code more single motor based, unless we just use a simple callback to just call Run on a much more regular basis.
It would also usurp being able to use the "Servo" library (not required for my needs).
The big advantage being no need to keep on calling Run from "user code".

Before I dig a big hole, I'm wondering if any prior work on using interrupts to run the AccelStepper show has been undertaken?

Cheers,
Ray Jones

Mike McCauley

unread,
Jul 5, 2014, 7:36:41 PM7/5/14
to accels...@googlegroups.com
Hi Ra,

no, there is no prior work here on using interrupts to run AccelStepper.

Cheers.

On Saturday, July 05, 2014 04:22:21 PM Ray Jones wrote:
> I'm a new member of the group, having just started to play with
> AccelStepper for a stepper driven rotary table conversion project I'm
> working on.
>
> First off the library is fantastic. I tried to read the paper the design is
> based upon, but found it a hard read :-)
>
> I've started looking at the V1.41 code to understand what is going on for
> an improvement I'll discuss further on.
> But the first thing I noticed was setAcceleration() whilst checking for
> zero, does not guard for a negative value?
> The simple fix is
>
> if (acceleration *<= *0.0)
> {
--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com
Phone +61 7 5598-7474

Sandy Noble

unread,
Jul 6, 2014, 3:35:08 AM7/6/14
to accels...@googlegroups.com
Hallo Ray, I haven't used interrupt-based timing with Arduino, but I do know that almost all of the CNC machine type firmwares I've seen (Marlin etc for 3d printers) use interrupt-based stepping. Not with accelstepper, but with their own movement planning engines. They mostly seem to use FrequencyTimer2.

I used Accelstepper with the IntervalTimer library on a Teensy-3 based machine I built earlier this year (http://www.polargraph.co.uk/2014/04/polargraph-at-the-edinburgh-science-festival/) that needed to read from an encoder and move motors in response. IntervalTimer is an interrupt-based timing library - the teensy 3 has loads of interrupts spare so there is no cost to using them, but the same principles should apply.

Im my application I needed to continuously process input from the serial port, while also stepping regularly. It all worked sweetly (the teensy is fast too), but there were quite a few cases where I wanted to disengage it for one reason or another, where I explicitly wanted to control the stepping synchronously, so I ended up not using the IntervalTimer in the final version. What I _did_ do was use the IntervalTimer to run the communications subsection, so that I could afford to "live" in the motor control loop, and call run() whenever I want, and didn't have to worry about checking comms all the time. Horses for courses.

I don't see much benefit in integrating the ISR code directly into accelstepper though - would it not be more simple just to have your ISR function in the arduino code, calling run() there?


sn
Sandy Noble
Message has been deleted

Ray Jones

unread,
Jul 6, 2014, 4:35:32 AM7/6/14
to accels...@googlegroups.com
Hi Sandy,

the reason why I was thinking of the ISR within the library was to reprogram the compare register in the timer with the new step interval.
Thus when the interrupt next fires it is precisely timed for the next step pulse, and if the step interval is re-adjusted the timer is reconfigured, hence time to next IRQ so on and so on.

As the intimate knowledge of the step interval is a private member variable of the class, the ISR needs to be buried in there.

The other option is as you say, use a regular ISR to call into Run.
The benefit there is multiple motor control is not so hard, but the finesse of the steady interval changes is probably lost due to the rigid time steps of the fixed period ISR.

The other option is run as it is now, but hope you don't need to dwell too long in certain tasks.
Everything certainly works well when all the Arduino does is motor control, but feature creep with a user interface soon will upset that.

The reason I went ISR based in the fixed speed version I first played with was the LCD updates I was performing skewed the step timing all over the place.
Putting the stepping behind and ISR set the stepping intervals rock solid.

So for my single motor application I figured using ISRs to set the nice steady acceleration without undue influence from LCD updates is the best solution for my purposes.



On Sunday, July 6, 2014 6:08:58 PM UTC+10, Ray Jones wrote:

Ray Jones

unread,
Jul 6, 2014, 4:44:12 AM7/6/14
to accels...@googlegroups.com
I should also add that my reasoning behind using a 16 bit timer (timer1 on UNO) is that the longer intervals at the start / end of the acceleration can be readily accommodated without playing prescaler games as you'd need to do with an 8 bit timer.

Cheers,
Ray Jones

Kim Øyhus

unread,
Jul 6, 2014, 1:20:55 PM7/6/14
to accels...@googlegroups.com


On Sunday, July 6, 2014 1:22:21 AM UTC+2, Ray Jones wrote:
The big modification I'm contemplating to attempt is modifying the code so that instead of needing to call AccelStepper::Run frequently, instead use a 16 bit timer interrupt (timer1 on a UNO) and let the stepping happen in the background.
I have already done this with a simple linear speed algorithm using the "FlexiTimer2" library and a callback function.
I found the smoothness of drive is superb whereas it becomes lumpy using "co-operative multitasking" via the usual delay routines, or needing to frequently call into AccelStepper::Run (ala micros).

Interesting. I also get tugging on the movements.
Knowing cybernetics, I considered adding tugs in the opposite direction for counteraction.
While this would certainly help, writing my own totally cybernetic interrupt driven motor driver would be more productive.

The code in AccelStepper is some of the most beautiful I have encountered. Intertwined dependent simplicity.

jimc

unread,
Jul 6, 2014, 2:47:20 PM7/6/14
to accels...@googlegroups.com
For what its worth, I ended up using a home-made ISR that only calls run() to avoid having to worry about delays when doing other things on an Arduino Uno R3. I know that AccelStepper is not safe in this context, but it seems to be fine with a single stepper motor and all I have the motor do is to maintain the height of a platform as items are placed on it or removed from it - ie, move up or down until the top clears an optical path. It sure makes programming the rest simpler - flashing the height leds and reading back optical resistors, flashing leds to indicate status, monitoring a switch to raise the platform to the limit switch, testing for the bottom and top limits, etc.

For this I use a geared stepper on a slider (lifting up to 3.5 kgs), Timer 1 which is 16-bits wide, and aiming at interrupts something like this:
ATmega328 with a 16MHz clock, clk/8, timer resolution = 8 * 1/16,000,000 = 8 * 6.25e-8 secs
(# timer counts + 1) = (target time) / (timer resolution)
                                =     .0001s      /   6.25e-8 s  * 8
                                =   200 timer counts
Interrupts every 0.1ms seems to work smoothly, 1ms was a bit rough. Of course, too many interrupts takes time away from the other things, not enough and you miss servicing accelstepper events in a timely fashion.

I am curious, and haven't looked into the accelstepper code - how hard would it be to make it safe for servicing on an ISR like this?

James Conway

Mike McCauley

unread,
Jul 6, 2014, 4:50:58 PM7/6/14
to accels...@googlegroups.com
Hi,


On Sunday, July 06, 2014 11:47:19 AM jimc wrote:
> For what its worth, I ended up using a home-made ISR that only calls run()
> to avoid having to worry about delays when doing other things on an Arduino
> Uno R3.

Perhaps you can post some sample code so we can make an example?


> I know that AccelStepper is not safe in this context, but it seems
> to be fine with a single stepper motor and all I have the motor do is to
> maintain the height of a platform as items are placed on it or removed from
> it - ie, move up or down until the top clears an optical path. It sure
> makes programming the rest simpler - flashing the height leds and reading
> back optical resistors, flashing leds to indicate status, monitoring a
> switch to raise the platform to the limit switch, testing for the bottom
> and top limits, etc.
>
> For this I use a geared stepper on a slider (lifting up to 3.5 kgs), Timer
> 1 which is 16-bits wide, and aiming at interrupts something like this:
> ATmega328 with a 16MHz clock, clk/8, timer resolution = 8 * 1/16,000,000 =
> 8 * 6.25e-8 secs
> (# timer counts + 1) = (target time) / (timer resolution)
> = .0001s / 6.25e-8 s * 8
> = 200 timer counts
> Interrupts every 0.1ms seems to work smoothly, 1ms was a bit rough. Of
> course, too many interrupts takes time away from the other things, not
> enough and you miss servicing accelstepper events in a timely fashion.
>
> I am curious, and haven't looked into the accelstepper code - how hard
> would it be to make it safe for servicing on an ISR like this?

The only thing I think might be unsafe is the delay() used in the case of
interface type of DRIVER.

Cheers.

>
> James Conway

jimc

unread,
Jul 6, 2014, 5:30:22 PM7/6/14
to accels...@googlegroups.com
Mike,

On Sunday, July 6, 2014 4:50:58 PM UTC-4, mikem wrote:
Hi, 

On Sunday, July 06, 2014 11:47:19 AM jimc wrote: 
> For what its worth, I ended up using a home-made ISR that only calls run() 
> to avoid having to worry about delays when doing other things on an Arduino 
> Uno R3. 

Perhaps you can post some sample code so we can make an example? 

The whole source of my project is attached - not very elaborate, mainly reading two light-sensitive resistors to test if the light path to them is open or blocked, and turning the motor accordingly so that the top of a platform maintains a constant height. The purpose is to receive electron micrographs in metal frames after they have been scanned on a Nikon inverted microscope - dropping them into a box causes them to jam, so I am preparing this new system where the robot arm places them on top of each other, always at the same height. There is a 2-min test movie of the whole scan process with the discard at the end - the original drop-it-in-the-box method:
A number of other "passive" methods have been tried, but all jam up, so I am hoping the drop-them-on-a-platform-at-constant-height method will work better.

The key bits for the ISR routine are:

1. Include the AVR library files (I think this is part of the Arduino IDE - I did this some months back and forget the details) and the AccelStepper file:
          #include <AccelStepper.h>
          #include <avr/io.h>
          #include <avr/interrupt.h>

2. In Setup, call SetUpInterrupts() - at the bottom of the source file - with an interval in microsecs

3. The ISR is defined as:
          ISR(TIMER1_COMPA_vect)
          {
            myStepper.run();
          }

That's about it. A link to a tutorial on the interrupt stuff is in the source file - its:
I used some of the example code, but added in changes to get a good value for the clock scaler according to the timer interval desired.

I have a mock-up made at home from wood and using thick cardboard "slides" so it works fine, and the workshop now has a proper fancy-shmancy rig that needs a bit of time to install and test. The Arduino has been great for a small well-defined task like this (you may notice another one in the movie controlling the slide release box at the start) and the AccelStepper library is great for moving 50 slides, which are heavy enough that turning on the motor without acceleration is not so successful.

Hope that the code is useful. Sorry I don't have time to slim it down.

James Conway
 
ArduinoCode-v2.txt

Ray Jones

unread,
Jul 12, 2014, 6:21:58 PM7/12/14
to accels...@googlegroups.com
For what it's worth I have managed to modify AccelStepper via a subclass (and making some variables in AccelStepper protected) by using timer1's compare interrupt. The timer compare interval is set by the latest step interval calculations.
The interrupt only runs whilst the motor runs, there is no need to call run regularly.

I also had a look into optimising some of the code to get a faster step rate as I find my driver works best in x8 microstep mode.
I found much time was being wasted in the "step" output routines (I'm using step1).
Whilst possibly convenient for some to be able to change polarity it certainly slows things up considerably.
I can now get about 4.5kHz step rate from my UNO.
This is done by implementing a function override on step / step1 which are already virtual functions in AccelStepper :-).

I also added a minimum speed function as I found my motor/driver got a bit cranky in the very first/last steps.

The code is a bit messy right at the moment. Once I tidy it up I'll post it here...

Cheers,
Ray Jones

Sandy Noble

unread,
Jul 12, 2014, 6:40:27 PM7/12/14
to accels...@googlegroups.com
This sounds really nice - the benefit of being able to kick-off moves synchronously, but also able to run in the background once it's moving. Keen to see :)

sn

Jose Manuel Gonzalez Martinez

unread,
Aug 3, 2014, 12:10:12 PM8/3/14
to accels...@googlegroups.com
Hi everyone!!

I´m trying to figure which might be the best option for my next project. I'm trying to make a kinetic painting using 25 steppers at the same time. I would like to be able to synchronice all of them in terms of leaving the machine working for a few hours not having problems of accuracy. I read that chipKit is the best option, but I want to be sure about which is the best purchase, chipKit vs. Arduino. I have worked with Arduino Mega before in this prototype (VIDEO), using the cheapest steppers in the market. The problem I had with them is that because of the mechanism inside has some plastic pieces, the precision is not so good at the begining and at the end of every rotation.

Any advice or recommendation about which stepper, which driver or microcontroller would be appreciated.

Thanks for the great library.

JM

http://cargocollective.com/geometriaemocional



Date: Sat, 12 Jul 2014 23:40:26 +0100
Subject: Re: [accelstepper] Re: Improvements to AccelStepper...
From: sandy...@gmail.com
To: accels...@googlegroups.com


This sounds really nice - the benefit of being able to kick-off moves synchronously, but also able to run in the background once it's moving. Keen to see :)

sn

--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Brian Schmalz

unread,
Aug 3, 2014, 9:22:02 PM8/3/14
to accels...@googlegroups.com
Jose,

As far as running lots of steppers at the same time using Accelstepper, chipKIT boards (especially the 80MHz ones) will get you much further than Arduino (not sure how the new 32 bit Arduinos stack up).

Depending on what form factor you want, the Fubarino SD and chipKIT MAX32 have the same CPUs (the highest end PIC32).

In the near-ish future, I'll be releasing the Fubarino SDZ, which uses a PIC32MZ part on it running at 200MHz (rather than 80MHz of the current generation) and should be able to do even more steppers at the same time.

Remember that Accelstepper does not coordinate any of it's steppers - they're all 'independent' of one another. So if you need to do many moves where they all start/stop at the same time, you'll need to synchronize them yourself in your code.

*Brian

From: accels...@googlegroups.com [accels...@googlegroups.com] on behalf of Jose Manuel Gonzalez Martinez [nexu...@hotmail.com]
Sent: Sunday, August 03, 2014 11:10 AM
To: accels...@googlegroups.com
Subject: RE: [accelstepper] Re: Improvements to AccelStepper...

Jose Manuel Gonzalez Martinez

unread,
Aug 11, 2014, 3:25:52 PM8/11/14
to accels...@googlegroups.com
Many thanks for your recommendation. I'll probable buy a Fubarino SD, using easydrivers and Nema17. I'll inform you about my progress. Do you know code to fix the problem about synchronicity? The solution for my prototype in the video was a light sensor. When one motor triggers the sensor all the motors stop at the same time in the right position. I'm not very happy with the accuracy. I need some help more than anything in the code.

Thank you Brian.

JM


To: accels...@googlegroups.com
Subject: RE: [accelstepper] Re: Improvements to AccelStepper...
Date: Mon, 4 Aug 2014 01:21:58 +0000

Brian Schmalz

unread,
Aug 11, 2014, 3:29:11 PM8/11/14
to accels...@googlegroups.com

Jose,

 

At this point, AccelStepper is not synchronized between axis. The library wasn’t written for that, and to retrofit would be a very big job. Maybe somebody will have time to do it, but at this point nobody has figured it out.

 

*Brian

Brian Schmalz  //  Principal Embedded Systems Engineer | product development services

Logic PD
T // 612.436.5134

NOTICE: Important disclaimers and limitations apply to this email.

Please see this web page for our disclaimers and limitations:

http://logicpd.com/email-disclaimer/

David Garrison

unread,
Aug 12, 2014, 12:07:43 PM8/12/14
to accels...@googlegroups.com
I also would like to see and most likely use Ray's code !!!


David


On Saturday, July 12, 2014 6:40:27 PM UTC-4, Sandy Noble wrote:
Reply all
Reply to author
Forward
0 new messages