TeensyStep - How to switch from 1 speed to another

Status
Not open for further replies.

jingleby

Member
I'm stuck on figuring out how to actively choose a new speed and have my motors accel/decel from current to the new speed. The code below ramps up to the right speed, but on the new speed command it pops to Zero then starts the second speed. I suspect the delays are part of the issue, but still unsure how to resolve.

My goal will be to have 10x motors spinning at their own constant speed. When a new set of speeds are received, each motor will adjust to their new desired RPM.

thanks for any help!

Code:
#include <StepControl.h>

Stepper motor_1(10, 9);         // STEP pin: 10, DIR pin: 9
Stepper motor_2(5, 9);         // STEP pin: 5, DIR pin: 9
StepControl<> controller;    // Use default settings 

void setup(){
  motor_1
    .setPullInSpeed(800)
    .setAcceleration(40000); //lower = slow. ?won't go above max speed
}

void loop() {

  motor_1.setMaxSpeed(4000);
  controller.rotateAsync(motor_1);    // Do the move
  delay(10000); //how long to run for. at time, start decel
  
  motor_1.setMaxSpeed(400);
  controller.rotateAsync(motor_1, motor_2);    // Do the move
  delay(5000);
}
 
Sorry, currently this is not possible. The library was designed to efficiently move a bunch of motors synchronized to a target position. It precalculates all movement parameters before the run. Changing those parameters on the fly will generate a mess.

However, since this requirements pops up regularly I'm already working on a new controller which will allow synchronized rotation of up to 10 motors with on the fly adjustable speed.

My goal will be to have 10x motors spinning at their own constant speed.
Unfortunately the Teensies only have four 32bit timers. TeensyStep needs one of those timers for each independently moving group of motors.

motors.png

So, if you really need those 10 motors running independently on one Teensy, TeensyStep unfortunately is not the right tool.
 
Last edited:
Thanks for the quick and thorough reply.

Ok no problem on not being able to run 10, but is there any way to run for 1 motor to run at a constant speed, then accel/decel to a new constant speed? Apologies if im misreading what you wrote above, I wouldn't want to not use this amazing library if I simply have to buy more teensy's.

Or what about using a very very large target position, and instead of stopping at the target, a new position & slightly different maxSpeed is immediately chosen and the library switches instantly.

I suspect there would be a hiccup at the change but haven't tested yet. My motors are spinning constant speeds between1700-2000 PPS (500-600 micros/pulse).

Looking forward to future iterations of your library! Thanks for all of your hard work.
 
...but is there any way to run for 1 motor to run at a constant speed, then accel/decel to a new constant speed?
Currently not, but most probably this should be possible in a couple of days.

Or what about using a very very large target position, and instead of stopping at the target, a new position & slightly different maxSpeed is immediately chosen and the library switches instantly. I suspect there would be a hiccup at the change but haven't tested yet.
I'm afraid playing around with this is just wasting your time, the current design is simply not capable to do this. Anyway, I'd be happy if you could test the new controller as soon as it is ready.
 
Oh wow, sure thing. More than happy to test it out whenever you are ready. My coding skills aren't stellar but my application is pretty direct so should still be helpful.

Thanks for being direct about capabilities. I'm creating an installation for mid-January but plan to revise it for additional uses down the road. If I buy drivers that work for 5v and 3.3v I can switch between Arduino/teensy at any point.

Reach out anytime.
 
Great, the new controller works already. I need to clean up the code and optimize some parts but this makes good progress.
I'm creating an installation for mid-January but plan to revise it for additional uses down the road.
Just curious Is this going to be an art installation?

If I buy drivers that work for 5v and 3.3v I can switch between Arduino/teensy at any point.
Yesterday I ordered one of those cheap TB6600 "industrial" drivers for some tests. Pictures showing the inside of those drivers do not look very reassuring (as usual, you get what you pay for...) Anyway, I will have a look if they are really compatible to 3.3V as they state (doubt it...).
 
Awesome, excited to see it. Yep, its for an art installation ... 10-12x motors spinning at constant but precise RPMs to create an optical illusion.

I'm trying to get beyond the phase of ordering random pieces, but the TB6600 was one of them. Also have a DRV8825, A4988 Pololu, and Big Easy Driver (A4988).

I read in one of the comments that its not even a 6600 chip in some of these drivers (though I don't really understand chip-level details). I haven't opened mine up yet to see the guts, but the casing screws are so tightly screwed in the metal is a bit bent ... can't be a good sign for QC hah.

Might need to order another driver sample that properly accepts 3.3v logic. Reach out if I can help at all.
 
I am adapting my plan to work for TeensyStep and am looking to better understand the library and hardware limitations (FTM/PIT/Interval Timers).

Would the hardware limitations limit either Opt 1 or Opt 2?

Current (and working) plan is :
- set 10x motors max speed to slightly different speeds
- move/start all 10 motors
- spin at speed for 20s
- ramp down/pause
- slow reset to position zero

ALT Option 1:
- set 10x motors at same max speed/settings
- start each motor 1 second after the previous
- each motor repeats its move 4 times
- all motors stop when their move is finished

ALT Option 2: (*If WIP library-update allows it)
- set 10x motors to same max speed
- move/start all motors
- spin at max speed for 20s
- change 10x max speeds to slightly different speeds
- accel/decel to new speed
- spin at new max speeds
- ramp down to zero / pause
- slow reset to pos zero

I don't mind adding add'l teensy's, but am a bit intimidated to co-ordinate them starting in sync. If this is advised, any suggestions on posts/reads to look into?
Thanks.
 
Let me explain the constraints: Currently each controller uses its own PIT timer. Since there are only 4 PITs you are limited to 4 controllers. Each controller is able to move up to 10 (this can be increased easily) motors with a fixed speed ratio (Bresenham). Motors can be moved to independent fixed targets (move commands, speed ratio determined from the targets). Alternatively the motors can be continiously rotated with fixed speed ratios (rotate commands, speed ratio determined from the maxSpeed settings). Changing that ratio on the fly is not possible with the current design of TeensyStep.

I'm currently working on an improvement which allows for overriding the overall speed of the controller. I.e., there will be something like a controller.overrideSpeed(float factor). Motors will correctly accelerate / decelerate to the new speed and all motors connected to the controller will stay in sync.

Regarding your options:

Option 1:
each motor repeats its move 4 times
Don't understand that. Do you mean 4 revolutions? Anyway, this should be possible already if you use one controller for each motor. You'd need 3 Teensies for 12 motors.

Option 2:
- change 10x max speeds to slightly different speeds
If that means that the speed of all 10 motors will be changed by the same factor that should be possible with the new library.


I don't mind adding add'l teensy's, but am a bit intimidated to co-ordinate them starting in sync. If this is advised, any suggestions on posts/reads to look into?
Thanks.
That shouldn't be a big deal, if you only need to start them at the same time a digital line from a master Teensy to the slave motor controller Teensies would be enough. If you need more control you could also setup a serial communication from the master to the slaves.

12Steppers.png
 
I am running into issues compiling with 10 motors and can't figure out why. At the moment all are running at the same speed. For some reason the log says "Too many motors used", though I am only defining 10.

My goal is to run 4 sets of motors at different speeds. I tried defining 2 sets of motors but these also don't compile when using "controller.move(motorSet_A, motorSet_B);"

Where am i going wrong?

Code:
#include <Wire.h>
#include <StepControl.h>

unsigned long targetInterval = 5000; // read speed from master
unsigned long pulsePerSec = 1000000/targetInterval;

//Step, Dir
Stepper M0(6, 5), M1(15, 14), M2(7, 8), M3(17, 16), M4(10, 9), M5(1, 0), M6(4, 3), M7(21, 20), M8(23, 22), M9(12, 11);
StepControl<> controller;   // Use default settings 

constexpr int spr = 16*200; // 3200 steps per revolution
int acceleration = 1500;
int pullInSpeed = 100;
const byte numMotors = 9; //actually 10, but 0-9
int microSteps = 4; 
int PPR = 5000; //pulses per rotation wo microstepping

int targetDist[numMotors] = {20*spr}; //50x decent
//int S1MaxSpeed[numMotors] = {50000};
//int S1MaxSpeed[numMotors] = {1*PPR*microSteps};
int S1MaxSpeed[numMotors] = {8000};

int scene = 0;

// SCL/SDA Stuff
byte b[3];  //byte array
bool flag1 = LOW;

void setup() {
  delay(2000);
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  delay(4000);

  Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4, &M5, &M6, &M7, &M8, &M9};
  for (int i = 0; i <= numMotors; i++) {
    motorSet_A[i]->setPullInSpeed(pullInSpeed);
    motorSet_A[i]->setAcceleration(acceleration);
    motorSet_A[i]->setMaxSpeed(S1MaxSpeed[0]);
    motorSet_A[i]->setTargetRel(targetDist[0]);
  }
  
}

void loop() {
  Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4, &M5, &M6, &M7, &M8, &M9};
//  Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4};
//  Stepper* motorSet_B[] = {&M5, &M6, &M7, &M8, &M9};
  
  scene = 0;
//  if (scene != lastScene){  // only go through if scene changes
    switch (scene){
      case 0:                 
//        M0.setMaxSpeed(8000);
//        M1.setMaxSpeed(8000);
//        M2.setMaxSpeed(8000);
//        M3.setMaxSpeed(8000);
//        M4.setMaxSpeed(8000);
//        M5.setMaxSpeed(8000);
//        M6.setMaxSpeed(8000);
//        M7.setMaxSpeed(8000);
//        M8.setMaxSpeed(8000);
//        M9.setMaxSpeed(8000);
//        M0.setTargetRel(targetDist[0]);
//        M1.setTargetRel(targetDist[0]);
//        M2.setTargetRel(targetDist[0]);
//        M3.setTargetRel(targetDist[0]);
//        M4.setTargetRel(targetDist[0]);
//        M5.setTargetRel(targetDist[0]);
//        M6.setTargetRel(targetDist[0]);
//        M7.setTargetRel(targetDist[0]);
//        M8.setTargetRel(targetDist[0]);
//        M9.setTargetRel(targetDist[0]);
        
//        for (int i = 0; i <= numMotors; i++) {                    // loop through all motors in motorSet_A...
//          motorSet_A[i]->setTargetRel(targetDist[i]);                // ... set targets to 0...
//          motorSet_A[i]->setMaxSpeed(S1MaxSpeed[i]);
//          Serial.print(i); Serial.print(": MaxSpeed "); Serial.println(S1MaxSpeed[i]);
//          Serial.print(i); Serial.print(": targetDist "); Serial.println(targetDist[i]);
//        }
//        Serial.print("M1: MaxSpeed "); Serial.println(S1MaxSpeed[0]);
//        Serial.print("M1: TargetDist "); Serial.println(targetDist[0]);
        
        controller.move(motorSet_A);
//        controller.move(motorSet_B);
//          controller.move(motorSet_A, motorSet_B);
//        controller.move(M0, M1, M2, M3, M4, M5, M6, M7, M8, M9);
//        delay(5000);
        
        break;
//      default:
//        Serial.println("error");
//        break;
    }
//  lastScene = scene; 
//  }
  
    if (flag1 == HIGH){
      if (b[0] == '*')
      {
        targetInterval = (b[1] << 8) | b[2]; 
        pulsePerSec = 1000000/targetInterval; //OLD SPEED STUFF
//        Serial.print("Slave Speed: ");
//        Serial.println(targetInterval, DEC); //target speed
        flag1 = LOW;
      }
    }

  delay(5000);
}

void receiveEvent(int howMany){
  for (int i = 0; i < howMany; i++){
    b[i] = Wire.read();
  }
  flag1 = HIGH;
}
 
There is a known silly bug in stepcontrol.h line 8.

Code:
constexpr int MaxMotors = 10;

You need to change MaxMotors to n+1 if you need n motors. I.e. set it to 11 if you need up to 10 motors. To do a quick test of your code I commented out the I2C stuff, compiled and had a look at a few motors with the logic analyzer. Seems to work, at lease at a first glance.

Remarks:
You probably know, but just in case you intended to initialize your arrays with the following code:
Code:
int targetDist[numMotors] = {20*spr}; //50x decent
//int S1MaxSpeed[numMotors] = {50000};
//int S1MaxSpeed[numMotors] = {1*PPR*microSteps};
int S1MaxSpeed[numMotors] = {8000};

This will only initialize the first element of the arrays to the given value, the rest of the array will be initialized to zero.

Don't know if it is by purpose, but these arrays have only 9 elements. Your commented out code in loop suggests that you intended to have 10 not 9?
Code:
int targetDist[numMotors] = {20*spr}; //50x decent
//int S1MaxSpeed[numMotors] = {50000};
//int S1MaxSpeed[numMotors] = {1*PPR*microSteps};
int S1MaxSpeed[numMotors] = {8000};

Hope that helps
 
AMAZING! thank you for clarifying the bug and the array issue. I am sorry to ask so many questions, but really do appreciate your help.

I still can't get 2 sets of motors to move. I get this error:
no matching function for call to 'StepControl<>::move(Stepper* [5], Stepper* [5])'

The other issue of only 1 case working i'll look into more online. If i comment out either case it seems to work
(error: jump to case label [-fpermissive])

Here's a vid of the progress (thanks to your help :)


Main Loop:

Code:
  int scene = 1;
  
//  if (scene != lastScene){  // only go through if scene changes
    switch (scene){
//      case 0:                         
//        Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4, &M5, &M6, &M7, &M8, &M9};
//        
//        for (int i = 0; i < numMotors; i++) {                    // loop through all motors in motorSet_A...
//          motorSet_A[i]->setTargetRel(targetDist[i]);                // ... set targets to 0...
//          motorSet_A[i]->setMaxSpeed(S1MaxSpeed[i]);
//        }
//        
//        controller.move(motorSet_A);
////        controller.move(motorSet_B);
////          controller.move(motorSet_A, motorSet_B);        
//        break;
      case 1:
        Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4};
        Stepper* motorSet_B[] = {&M5, &M6, &M7, &M8, &M9};

        int distanceA = 20*spr;
        int distanceB = 20*spr;
        int speedA = 8000;
        int speedB = 7500;        
        
        for (int i = 0; i < 5; i++) {                    // 5 motors
          motorSet_A[i]->setTargetRel(distanceA);    
          motorSet_A[i]->setMaxSpeed(speedA);
          motorSet_B[i]->setTargetRel(distanceB);     
          motorSet_B[i]->setMaxSpeed(speedB);
        }

//          M0.setTargetRel(distanceA);
//          M0.setMaxSpeed(speedA);
//          M1.setTargetRel(distanceA);
//          M1.setMaxSpeed(speedA);
//          M2.setTargetRel(distanceA);
//          M2.setMaxSpeed(speedA);
//          M3.setTargetRel(distanceA);
//          M3.setMaxSpeed(speedA);
//          M4.setTargetRel(distanceA);
//          M4.setMaxSpeed(speedA);
//          M5.setTargetRel(distanceA);
//          M5.setMaxSpeed(speedA);
//          M6.setTargetRel(distanceA);
//          M6.setMaxSpeed(speedA);
//          M7.setTargetRel(distanceA);
//          M7.setMaxSpeed(speedA);
//          M8.setTargetRel(distanceA);
//          M8.setMaxSpeed(speedA);
//          M9.setTargetRel(distanceA);
//          M9.setMaxSpeed(speedA);
        
//        controller.move(M0, M1, M2, M3, M4, M5, M6, M7, M8, M9);
        controller.move(motorSet_A, motorSet_B);
        break;
//      default:
//        Serial.println("error");
//        break;
    }
 
I still can't get 2 sets of motors to move. I get this error:
no matching function for call to 'StepControl<>::move(Stepper* [5], Stepper* [5])'

Reason is that TeensyStep doesn't provide a method to pass in two arrays of steppers. I assume that your 4 motorSets are supposed to run independently? If so you need to setup a dedicated controller for each set. I.e. something like

Code:
StepControl<> controller1;
StepControl<> controller2;
StepControl<> controller3;
StepControl<> controller4;

Then, you can move a dedicated set of motors with each controller like this

Code:
Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3};
Stepper* motorSet_B[] = {&M5, &M6, &M7, &M8, &M9, &M4};

controller1.move(motorSet_A);
controller2.move(motorSet_B);

This would first move the motors from set A to their positions and then the motors from set B. If you want to run the motors from both sets in parallel you would do

Code:
Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3};
Stepper* motorSet_B[] = {&M5, &M6, &M7, &M8, &M9, &M4};

controller1.moveAsync(motorSet_A);
controller2.moveAsync(motorSet_B);

while(controller1.isRunning || controller2.isRunning) // optionally wait until movement is finished or continue with other tasks while the motors are running in the background
{
  delay(10);
}

The other issue of only 1 case working i'll look into more online. If i comment out either case it seems to work
(error: jump to case label [-fpermissive])


c++ does not allow to define variables in a naked case statement https://stackoverflow.com/questions/92396/why-cant-variables-be-declared-in-a-switch-statement . (motorSet_A[] in your case).

Code:
//      case 0:                         
//        Stepper* motorSet_A[] = {&M0, &M1, &M2, &M3, &M4, &M5, &M6, &M7, &M8, &M9};
//        
//        for (int i = 0; i < numMotors; i++) {                    // loop through all motors in motorSet_A...
//          motorSet_A[i]->setTargetRel(targetDist[i]);                // ... set targets to 0...
//          motorSet_A[i]->setMaxSpeed(S1MaxSpeed[i]);
//        }
//        
//        controller.move(motorSet_A);
////        controller.move(motorSet_B);
////          controller.move(motorSet_A, motorSet_B);        
//        break;

If you want to do that you have to wrap your case code in braces:

Code:
case 0:         
{ 
   ....
   break;
}
case 1: 
{ 
   ....
   break;
}


Had a look at your video, great work. Are you going to attach picture stripes on the faces to get a changing picture while moving? Anyway, looking forward to see TeensyStep moving 40 motors in parallel. I assume this has not been done before :)
 
Last edited:
Hi again Luni! Fantastic, I am happy to be able to change one's motor speed on the fly, but where can I download the new library? The link you provided does not seem to still work... In the mean time I would like to ask if this can be done:

1.jpg

For my winding application it would be great to fine-tune speeds on the fly while keeping accelereations / deccelerations in sync.

The following would allow me some interesting winding patterns:

2.jpg

Would this be possible with one controller? In case I need to use 2 controllers, I guess I would also need to pre-calculate the acceleration rates of the speed changes?
 
Nice coincident, I just came back from a Schumann /Schubert concert were I was wondering if they are already playing with your Teensyfied strings :)

but where can I download the new library
Sorry, the new version is still on the development branch. (Just select branch:develop then you can download / clone) as usual).

For my winding application it would be great to fine-tune speeds on the fly while keeping accelereations / deccelerations in sync.
That will definitely be possible.

...[pictures] would this be possible with one controller?
No, the motors moved by one controller will always rotate with a fixed speed ratio. But you can of course use two controller objects for that. However, what you have drawn seems to not only require an on-the-fly change of motor speed but also an on-the-fly change of acceleration? This currently is not possible, but shouldn't be a big deal to implement. Drop me a note if you really need this.

Alternatively you could use a simple regulation algorithm and use the speed of motor1 to generate the target speed of motor2 (multiply speed1 by some factor). You'd then regulate motor2 to that target and change the factor on the fly. A similar thing is shown here
https://forum.pjrc.com/threads/43491-New-Stepper-Motor-Library?p=198919&viewfull=1#post198919 and here https://forum.pjrc.com/threads/43491-New-Stepper-Motor-Library?p=198994&viewfull=1#post198994
 

Thanks for the links, great project!!! I will try to learn from these examples. I have to say: a big big thank to luni for his help and his library!!! He saves my project.

Nice coincident, I just came back from a Schumann /Schubert concert were I was wondering if they are already playing with your Teensyfied strings :)

Nice!!!! Yes, teensyfied and lunified indeed :cool: But as further I get, the farer the destination seems... I will keep you informed and will provide videos as soon as something is showable! For the moment I can show my recently completed PCBs, basically a board for the periphery of the well known teensy breakout-board:

IMG_20190210_152320.jpg

The ICs used are: ADS1231, MAX3490, LM335, MCP100-475D one each, and multiple 74HCT125, 74LVC245, ULN2803A.

what you have drawn seems to not only require an on-the-fly change of motor speed but also an on-the-fly change of acceleration?

Sorry, I didn't make this clear: I don't need changes of acceleration for the speed changes of a single motor, but as soon as both motors change speed I want them to be in sync. I updated my diagram accordingly:

3.jpg
 
For the moment I can show my recently completed PCBs, basically a board for the periphery of the well known teensy breakout-board:
They look good! So, you finally used a ECAD instead of Frizing? Looks like KiCad? One question: why do you have the T3.6 on the breakout and then the breakout on the board? Couldn't you design the T3.6 board directly into your board?

Implementing your speed profile might be demanding. Therefore, of course, I'd like give it a try :).

However, I need some more information:
1) First acceleration phase (t0-t1) This movement should be synced. I assume that the speed ratio of the motors and the acceleration are given to get the correct winding pitch. Right?
2) At some time t2 you want to accelerate the red motor to a given, higher speed. Is the acceleration the same as at the beginning or is it arbitrary?
3) At t3 you start decelerating in sync to new target speeds. Should the speed ratio of both motors be the same as it was in the first phase? Or are the target speeds given and the speed ratio follows?
4) Same question for the last phase

Given your winding application I thought that you would need a constant speed ratio all the time which would determine the target speeds of one of the motors, but I'm not sure. Especially the phase between 3) and 4) will definitely have a different ratio, looks like you want some larger pitch at the end of the winding?
 
Looks like KiCad? One question: why do you have the T3.6 on the breakout and then the breakout on the board? Couldn't you design the T3.6 board directly into your board?

Yes, KiCad. I wanted to be able to replace it in case I again burn it so I used some boards I already had in hand which have socketed teensys. Also it seemed easier to not have to worry about the pogo pins needed for some of the inner teensy pads...

Implementing your speed profile might be demanding. Therefore, of course, I'd like give it a try :)

Many thanks!!!! I will try to explain: here a short video showing a test winding (with recycled i.e. crude and dirty material). The wires are fed towards the core in a way shown in the following diagram:

wire.jpg

What I do during winding is to pull the winding wire slightly backwards to make the winding tighter: this is marked (b) on the above diagram. At the moment I have to stop the process, move the feeder a little bit backwards, start again and continue to wind, often with a tad narrower pitch. It would be amazing if I could adjust that on the fly. Also many winding wires are not perfectly even: so if the wire becomes thinner, I would reduce the speed of the feeder a little bit, ending up with a narrower pitch. There are many similar situations where I adjust the ratio, and I always have to stop, change the pitch and start again... This is doable, but... :p

1) First acceleration phase (t0-t1) This movement should be synced. I assume that the speed ratio of the motors and the acceleration are given to get the correct winding pitch. Right?

Correct!

2) At some time t2 you want to accelerate the red motor to a given, higher speed. Is the acceleration the same as at the beginning or is it arbitrary?

Arbitrary!

3) At t3 you start decelerating in sync to new target speeds. Should the speed ratio of both motors be the same as it was in the first phase? Or are the target speeds given and the speed ratio follows?

The latter. From t2 to the end I wanted to keep the ratio.

4) Same question for the last phase

Same answer, only that the target speed is zero. To be precise: I don't want the ratio to change during acceleration or deceleration. As long as I change only the speed of one of the motors all future speed changes shall have the new ratio until I again change the ratio by changing the speed of one of the motors.

I can say that if you read my diagram in #17 backwards it happends exactly what I tried to describe: I start with a slower speed (t4), go higher as soon as I feel comfortable (t3 to t2), then lower the red speed (between t2 and t1: i.e. for pulling back the winding wire), and finally stop at t0.
 
Thanks for the explanation and the video. The speed and precision of the winding is quite impressive. I think I understand now what you want to achieve.

To see what is possible I did a quick experiment with two controllers. I.e, I do not use the usual synced motion (which is not possible since you need to change the speed ratio on the fly). Instead I try to sync the motors by precalculating the acceleration of the red motor. (The following is only showing the first phase of your speed profile, I only wanted to test the accuracy of the method)

Settings:
The green motor is set to v=15000 / a=10000 the red motor to v=8000. Acceleration of the red motor is set to a=10000*(8000/15000) to reach the target speed at the same time as the green motor (theoretically). I print out time, motor positions, motor speeds and speed ratios every 20ms. Here the code if you want to experiment.

Code:
#include "Arduino.h"
#include "TeensyStep.h"

Stepper greenMotor(0, 1), redMotor(1, 2);
RotateControl greenCtrl(5,10000), redCtrl(5,10000); //needed to increase the acc_update period to get required accuracy

int greenSpeed = 15000;
int greenAcc = 10000;

int redSpeed = 8000;
int redAcc =  greenAcc * redSpeed / greenSpeed;

void setup()
{
  while(!Serial);
  
  greenMotor
      .setMaxSpeed(greenSpeed)
      .setAcceleration(greenAcc);

  redMotor
      .setMaxSpeed(redSpeed)
      .setAcceleration(redAcc);

  redCtrl.rotateAsync(redMotor);
  greenCtrl.rotateAsync(greenMotor);
}

elapsedMillis stopwatch = 0;

void loop()
{
  if (stopwatch > 20)
  {
    stopwatch = 0;

    // printout position, speed and ratio for the motors
    // tab seperated for import to spreadsheet
    int greenPos = greenMotor.getPosition();
    int greenSpeed = greenCtrl.getCurrentSpeed();
    int redPos = redMotor.getPosition();
    int redSpeed = redCtrl.getCurrentSpeed();
    float ratio = (float)greenSpeed /redSpeed;
    
    Serial.printf("%d\t%i\t%i\t%i\t%i\t%.4f\n", millis(), greenPos, redPos, greenSpeed, redSpeed, ratio);
  }
}

While this works in principle, the accuracy of the method is somehow borderline. The reason is that the acceleration algorithm is integer based so that you don't get a perfect speed ratio during acceleration. To improve the situation I needed to increase the acceleration update time to 10ms.

Below a plot of the motor speeds(red,green, left axis, Hz) and ratio (yellow, right axis) over time (ms):

ratio1.PNG

All in all it might work, but I don't like it. A true synchronization would be much more elegant. I'll think of something better...
 
Many thanks for the explanation and the code!!! I tried to play with your code and managed to run it, but observed some details I don't fully understand...

Here is a version of your code with just the last 2 lines added / modded:

Code:
#include "Arduino.h"
#include "TeensyStep.h"

Stepper greenMotor(22, 41), redMotor(43, 42);
RotateControl greenCtrl(5, 10000), redCtrl(5, 10000); //needed to increase the acc_update period to get required accuracy

int greenSpeed = 15000;
int greenAcc = 10000;

int redSpeed = 8000;
int redAcc =  greenAcc * redSpeed / greenSpeed;

void setup(){
  while (!Serial);

  greenMotor
  .setMaxSpeed(greenSpeed)
  .setAcceleration(greenAcc);

  redMotor
  .setMaxSpeed(redSpeed)
  .setAcceleration(redAcc);

  redCtrl.rotateAsync(redMotor);
  greenCtrl.rotateAsync(greenMotor);
}

elapsedMillis stopwatch = 0;

void loop(){
  if (stopwatch > 20){
    stopwatch = 0;

    // printout position, speed and ratio for the motors
    // tab seperated for import to spreadsheet
    int greenPos = greenMotor.getPosition();
    int greenSpeed = greenCtrl.getCurrentSpeed();
    int redPos = redMotor.getPosition();
    int redSpeed = redCtrl.getCurrentSpeed();
    float ratio = (float)greenSpeed / redSpeed;
    float ratioPos = (float)greenPos / redPos;

    Serial.printf("%d\t%i\t%i\t%i\t%i\t%.5f\t%.5f\n", millis(), greenPos, redPos, greenSpeed, redSpeed, ratio, ratioPos);
  }
}

The serial output gives me a constant speed-ratio but a differing position-ratio which starts a little to high and slowly decreases until it equals the speed-ratio. I tried to verify that with a one-controller version:

Code:
#include "Arduino.h"
#include "TeensyStep.h"

Stepper greenMotor(22, 41), redMotor(43, 42);
RotateControl Motors(5, 10000);

int greenSpeed = 15000;
int greenAcc = 10000;

int redSpeed = 8000;
int redAcc =  greenAcc * redSpeed / greenSpeed;

void setup(){
  while (!Serial);

  greenMotor
  .setMaxSpeed(greenSpeed)
  .setAcceleration(greenAcc);

  redMotor
  .setMaxSpeed(redSpeed)
  .setAcceleration(redAcc);

  Motors.rotateAsync(redMotor, greenMotor);
}

elapsedMillis stopwatch = 0;

void loop(){
  if (stopwatch > 20){
    stopwatch = 0;

    // printout position, speed and ratio for the motors
    // tab seperated for import to spreadsheet
    int greenPos = greenMotor.getPosition();
    int redPos = redMotor.getPosition();
    float ratioPos = (float)greenPos / redPos;

    Serial.printf("%i\t%i\t%.5f\n", greenPos, redPos, ratioPos);
  }
}

Same observation. Is my understanding wrong...? I am not totally sure about this but I think I saw something similar during winding: the ratio slowly catches up after start acceleration... I though this was a drift because of wire unevenness, but now there is hope my wires are fine...? :rolleyes:

Another observation:

Code:
#include "TeensyStep.h"

Stepper motor(22, 41);
RotateControl controller(5, 1000);

void setup() {
  delay(1000);

  motor
  .setMaxSpeed(-124)
  .setAcceleration(198);

  controller.rotateAsync(motor);     // not moving at all...
  delay(2000);
  controller.stop();

  motor
  .setMaxSpeed(124)
  .setAcceleration(198);

  controller.rotateAsync(motor);
  delay(2000);
  controller.stop();                 // not able to stop...
}

void loop() {}

Here it seems the motor doesn't move at all in the negative direction at very slow speeds, and in the positive direction it starts but I can't stop it. Both worked with an older pre speedOverride() version and pullin-speed set to 50.

(BTW I could only compile after removing the line "arduino.h" in stepper.h, and after renaming the folder "accelerators" to "Accelerators")
 
I would say your observed effect is simply an effect of discrete mathematics:
Assume that the second motor runs at say 1/3 of the speed of the first motor. I.e, after 3 steps of the first motor the second motor makes one step (see table below). If you calculate the position ratio out of that you see the effect you observed. For small total step numbers the calculation "error" is large. Of course, the higher the step numbers get the smaller the relative "error" and you approach the expected value 0.333 for the ratio.

Code:
pos 1	pos2	pos2/pos1
1	1	1.000
2	1	0.500
3	1	0.333
4	2	0.500
5	2	0.400
6	2	0.333
7	3	0.429
8	3	0.375
9	3	0.333
10	4	0.400
11	4	0.364
12	4	0.333
13	5	0.385
14	5	0.357
15	5	0.333
16	6	0.375
17	6	0.353
18	6	0.333

Here it seems the motor doesn't move at all in the negative direction at very slow speeds, and in the positive direction it starts but I can't stop it. Both worked with an older pre speedOverride() version and pullin-speed set to 50.
I can reproduce that, looks like you found a bug for small speeds. I'll have a look into it.


I also did some work on your original problem. Below you see the resulting motor speeds (red, green, using the left y-axis). The yellow curve is the speed ratio (using the right y-axis). Don't worry about the small wiggles in the curves, these again are only calculation effects.

ratio2.PNG

As you see, both motors run synchronized all the time, i.e. the speed ratio stays constant, i.e. the red motor precisely follows the green one with the given ratio. Besides the usual speed override, the code enables you now to change the speed ratio on the fly. It starts with a ratio of 0.6 (green maxSpeed = 15000, red maxSpeed= 9000) At t=6s I changed the ratio to 0.8. I didn't bother to slowly change the ratio from the old value to the new value. I assume that you will only use that to slightly trim the pitch of your winder, so in reality the changes will be small enough to avoid step losses due to the sudden change in speed.

The current code is quite messy and a proof of principle only. I'll clean it up and post it later today.
 
I would say your observed effect is simply an effect of discrete mathematics:
Assume that the second motor runs at say 1/3 of the speed of the first motor. I.e, after 3 steps of the first motor the second motor makes one step (see table below). If you calculate the position ratio out of that you see the effect you observed. For small total step numbers the calculation "error" is large. Of course, the higher the step numbers get the smaller the relative "error" and you approach the expected value 0.333 for the ratio.

Thanks for clarification!

I also did some work on your original problem. Below you see the resulting motor speeds (red, green, using the left y-axis). The yellow curve is the speed ratio (using the right y-axis). Don't worry about the small wiggles in the curves, these again are only calculation effects.

This is very promising, I can't wait to test it...

I assume that you will only use that to slightly trim the pitch of your winder, so in reality the changes will be small enough to avoid step losses due to the sudden change in speed.

Yes exactly, and I use these motors, so I am not too worried about step losses.

I observed also something else which I did not test until now:

Code:
#include "TeensyStep.h"

Stepper motor(22, 41);
RotateControl controller;    // Use default settings

void setup() {
  pinMode(7, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(7), ISR, CHANGE);

  motor
  .setMaxSpeed(10000)
  .setAcceleration(1000);
  controller.rotateAsync(motor);
}

bool estop_flag;

void ISR() {
  estop_flag = 1;
}

void loop() {
  if (estop_flag == 1) {
    controller.emergencyStop();
    estop_flag = 0;
  }
}

In this code I can stop the motor only after acceleration is completed, so I can't use pin-interrupts for my limit switches. Is this intended?
 
Just a small update: I noticed that the low speed bug is fixed. I implemented speed override and now can change the speed during winding which is really useful for me. Thanks a lot! :cool:

Regarding my limit switch / homing question: as advised I tried to design a homing routine (i.e. push the switches to keep track of the absolute positions). During acceleration/deceleration the limit switches would not fire: I tried that by polling the pin and also with pin interrupts without success. But with the motors driving slowly and without acceleration it was possible.

After homing is done I can use the motor.setTargetAbs(precalculated) and controller.move() commands safely, but I can't get overrideSpeed() to work in StepControl mode. And in RotateControl mode I could not use the setTarget() commands. I would love to make use of the override commands but I am reluctant to give up on the position tracking...
 
I'm currently traveling and can only answer briefly from my mobile.
Yes, I fixed the acceleration bug and added an override acceleration feature. Both are only implemented for the rotation controller (currently). Anyway, all controllers keep track of position. You do not need to stick with one controller and can switch to another after a move. Position Will be correct.

I could reproduce the estop bug, but it is not yet fixed.

Redid the speed profile experiments with the the override acceleration which works great. Stay tuned...
BTW: can you add some typical numbers to the speed profile (time and velocities)?

I don't understand the issue with homing. Can you do a quick sketch showing the problem?
 
Status
Not open for further replies.
Back
Top