Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultipleSteppers without Acceleration #139

Open
BlueGene00 opened this issue Jul 21, 2022 · 37 comments
Open

MultipleSteppers without Acceleration #139

BlueGene00 opened this issue Jul 21, 2022 · 37 comments

Comments

@BlueGene00
Copy link

As far as I understood, the MultipleSteppers example only works with an integrated acceleration/declaration.

For my application I want to run different segments one after another (Polyline to interpolate a spline curve).

So the acceleration is already taken care of, I just need a code which runs all steppers synchronous between two points, but without the acceleration...

Of course I could set a very high acceleration, then it would akt a linear movement. But there is still the calculation in the background which might slow down the movement and prevent the motor from reaching very high speeds.

What would be the easiest way to get rid of the acceleration in the code and just perform a linear movement between several motors?

@luni64
Copy link
Owner

luni64 commented Jul 21, 2022

You can plug in your own accelerator, no need to change the lib. Since the accelerator doesn't need to calculate anything, this should be quite simple. Here some untested code:

#include "Arduino.h"
#include "TeensyStep.h"

class dummyAccelerator
{
public:
  // will be called before the movement. Do all needed precalculations here and return the starting speed
  inline int32_t prepareMovement(int32_t currentPos, int32_t targetPos, uint32_t targetSpeed, uint32_t pullInSpeed, uint32_t pullOutSpeed, uint32_t a)
  {
    v = targetSpeed; // no acceleration so we only save the requested speed
    return v;
  }

  // will be called whenever TeensyStep needs a new speed, do the calculation based on the current position here and return the new speed
  inline int32_t updateSpeed(int32_t currentPosition)
  {
    return v; // no acceleration, so just return the target speed
  }

  // calculate the steps required to decelerate to a stop
  inline uint32_t initiateStopping(int32_t currentPosition)
  {
    return 0;  // no acceleration, we can stop imediately
  }

protected:
  int32_t v = 0;
};

using MyController = TeensyStep::StepControlBase<dummyAccelerator, TimerField>;

MyController ctrl;
Stepper s0(0, 1), s1(2, 3);

void setup()
{
  s0.setMaxSpeed(10000);
  s1.setMaxSpeed(20000);

  ctrl.move(s0, s1);
}

void loop()
{
}

@BlueGene00
Copy link
Author

BlueGene00 commented Jul 22, 2022

Ok, I have tried that code and it seems to work somehow. (Teensy 3.6 @180MHz) But when you look closer, there seems to be something wrong.

This is the code I have used for testing:

void setup(){
  s0.setMaxSpeed(3200);
  s1.setMaxSpeed(3200);
}


void loop()
{

  //------Move to Position 1 --------
  s0.setTargetAbs(3200);
  s1.setTargetAbs(3200);
  ctrl.move(s0, s1);


  //----Move back to Start -------
  s0.setTargetAbs(0);
  s1.setTargetAbs(0);
  ctrl.move(s0, s1);
}

The distance is the same as the speed, so in theory, the movement should take exactly 1s. I have measured the STEP and DIR signal with a Picoscope and there is something wrong...

The time of the DIR signal is 1.001s, which is not exact 1.000s. Here is does not matter really, but if you take 200 segments with a much shorter time for each signal, then 0.001s will be definitly noticable.

teensyStep1

But if you look close between the segments, you see a gap on the STEP output. This gap will be very noticable in my application, since I want to use many small segments to replicate a complete bezier curve.

teensyStep2

teensyStep3

The cylce time is fine (1s/3200Hz = 312,5 microseconds), so there seems to be some kind of delay before and/or after the segment...

teensyStep4

@BlueGene00
Copy link
Author

I did a similar test with the Accelstepper-library and the MultiStepper code. Here are the results different. Total movement time is 0,9984s, so the error is much bigger.

accelstepper1

Cycle time here is different to TeensyStep:
image

But the transition between the segments looks perfect, so this is my goal with TeensyStep. Accelstepper works fine with one stepper, but when adding more steppers, the total speed is limited more and more... So I want to see if Teensystep can handle this.

accelstepper2

accelstepper3

@luni64
Copy link
Owner

luni64 commented Jul 22, 2022

Interesting, I'll have a closer look tomorrow.

@luni64
Copy link
Owner

luni64 commented Jul 23, 2022

The gap is due to the call of move() which spins while it waits for the controller to finish the movement (see here)
While it spins it calls delay(1) to not overrun the library with high frequency calls to stepTimerIsRunning(). This delay is causing your 'gaps' in the pulse train.

A better solution would be to use the built in callback functionality which invokes a user supplied function whenever the controller reached the target.

ctrl.setCallback(onPositionReached);

Unfortunately the callback is invoked at the beginning of the last pulse. Thus, setting new targets in the callback (while the last pulse is still active) will mess up the library. I therefore changed the library to invoke the callback after the last pulse is done (use the branch improveCallback for testing). With this you can do the following (please note that I renamed and moved the accelerator to NoAccelerator.h):

#include "Arduino.h"
#include "TeensyStep.h"
#include "NoAccelerator.h"

using MyController = TeensyStep::StepControlBase<NoAccelerator, TimerField>;

MyController ctrl;
Stepper sx(0, 1), sy(2, 3);

struct pos
{
    int x;
    int y;
};

constexpr pos path[] = {
    {3200, 3200},
    {   0,    0},
    {5000, -1000},
    { 200, -3200},
    { 0, 0},
};
constexpr size_t pathLength = sizeof(path) / sizeof(path[0]);

// this will be called whenever the controller finished a movement
void onPositionReached()
{
    static unsigned p = 0;
    digitalWriteFast(4, HIGH);  // measure switching speed

    if (p < pathLength) 
    {
        sx.setTargetAbs(path[p].x);
        sy.setTargetAbs(path[p].y);

        ctrl.moveAsync(sx, sy);
        p++;
    }
    digitalWriteFast(4, LOW);
}

void setup()
{
    pinMode(4, OUTPUT);  // use pin 4 to measure switching speed

    ctrl.setCallback(onPositionReached);
    sx.setMaxSpeed(3200);
    sy.setMaxSpeed(3200);

    onPositionReached(); // invoke the callback to start the chained movements
}

void loop()
{
}

The code generates the following pulse train:
image

Zoomed into the last transitions:
image

image

image

The transition between two path points takes some 6µs which is needed to calculate the new movement parameters and setup the timers. Thus, the first pulse after the switch will be 6µs too late. Depending on your application this may or may not be a problem.

Hope that helps.

@BlueGene00
Copy link
Author

BlueGene00 commented Jul 25, 2022

Thanks for the quick support, I tried your latest example, but there still seems something wrong.

There is now an impuls on each new segment, but somehow this first impuls is way too long. It looks like this impuls is connected to the next impuls. On your measurement it looks ok, but with my test, this seems to be wrong.

01

02

03

04

@luni64
Copy link
Owner

luni64 commented Jul 25, 2022

Did you use exactly my firmware and the improvedCallback branch from the library?

@BlueGene00
Copy link
Author

Yes, code is the same, just different pins:

NoAccelerator.h

class NoAccelerator
{
public:
  // will be called before the movement. Do all needed precalculations here and return the starting speed
  inline int32_t prepareMovement(int32_t currentPos, int32_t targetPos, uint32_t targetSpeed, uint32_t pullInSpeed, uint32_t pullOutSpeed, uint32_t a)
  {
    v = targetSpeed; // no acceleration so we only save the requested speed
    return v;
  }

  // will be called whenever TeensyStep needs a new speed, do the calculation based on the current position here and return the new speed
  inline int32_t updateSpeed(int32_t currentPosition)
  {
    return v; // no acceleration, so just return the target speed
  }

  // calculate the steps required to decelerate to a stop
  inline uint32_t initiateStopping(int32_t currentPosition)
  {
    return 0;  // no acceleration, we can stop imediately
  }

protected:
  int32_t v = 0;
};

main code:

#include "Arduino.h"
#include "TeensyStep.h"
#include "NoAccelerator.h"


using MyController = TeensyStep::StepControlBase<NoAccelerator, TimerField>;

MyController ctrl;
Stepper sx(7, 6), sy(24, 10);

struct pos
{
  int x;
  int y;
};

constexpr pos path[] = {
  {3200, 3200},
  {   0,    0},
  {5000, -1000},
  { 200, -3200},
  { 0, 0},
};
constexpr size_t pathLength = sizeof(path) / sizeof(path[0]);

// this will be called whenever the controller finished a movement
void onPositionReached()
{
  static unsigned p = 0;
  digitalWriteFast(32, HIGH);  // measure switching speed

  if (p < pathLength)
  {
    sx.setTargetAbs(path[p].x);
    sy.setTargetAbs(path[p].y);

    ctrl.moveAsync(sx, sy);
    p++;
  }
  digitalWriteFast(32, LOW);
}

void setup()
{
  pinMode(32, OUTPUT);  // use pin 4 to measure switching speed
  pinMode(7, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(24, OUTPUT);
  pinMode(10, OUTPUT);

  ctrl.setCallback(onPositionReached);
  sx.setMaxSpeed(3200);
  sy.setMaxSpeed(3200);

  onPositionReached(); // invoke the callback to start the chained movements
}

void loop()
{
}

Here are some screenshot from all position changes. I have now included the measurement of the second motor:

Segment 1 ( 3200/3200 ):
01

Segment 2 ( 0/0 ):
02

Segment 3 ( 5000/-1000 ):
03

Segment 4 ( 200/-3200 ):
04

End:
05

As you can see, the long step always happens, except on last change on the second motor... If you have look on the step interval on the motor in the last segment, you also see, that it is not consistent, it looks like 5 or 6 steps are made, then there is a little gap, and the the next 5/6 steps are made...

@luni64
Copy link
Owner

luni64 commented Jul 26, 2022 via email

@BlueGene00
Copy link
Author

Yes, this was the error...

I have copied the new branch to "C:/Programs/Arduino/libraries", but the original library was still under "C:/Documents/Arduino/libraries". Now it seems to be alright.

The next step is to update the speed for each segment. In the example above, the maxSpeed was set for both axis to 3200. This should now also be updated on each new segment. So with each new target-position, there will be a new speed.

The end goal is to have a fixed time interval and then calculate the position and speed accordingly, so that each segment is run in a fixed time.

Here is a quick example with a 1 second interval:
Segment 1: Absolute distance 2000-->Relative distance 2000-->Speed of 2000 Hz is required
Segment 2: Absolute distance 1000-->Relative distance 1000-->Speed of 1000 Hz is required
Segment 3: Absolute distance 4000-->Relative distance 3000-->Speed of 3000 Hz is required
...
...

Distance and Speed for each segment will be calculated before the movement and stored in an array. So basically I just need a second array which contains the speed and is updated with each new position value. I will calculate the speed in a way, that it will match the fixed time interval for each motor. (in this example 1s, but in the real application it will be much less).

06

I have added this second array and I call the setMaxSpeed before the setTargetAbs. This seems to work fine. Is this approach a good one or could this lead to problems?

I will now perform a few more tests (with my real application code, more motors and much higher speed). When everything works as expected, this should be somehow integrated into the TeensyStep as example etc.

The end result will enable users to run multiple stepper motors on a definied path in a fixed time.

constexpr spee speedpath[] = {
  {2000, 2000},
  {1000, 1000},
  {3000, 3000},
  {2000, 2000},
  {4000, 4000},
};

void onPositionReached()
{
  static unsigned p = 0;
  digitalWriteFast(32, HIGH);  // measure switching speed

  if (p < pathLength)
  {
    s0.setMaxSpeed(speedpath[p].x);
    s1.setMaxSpeed(speedpath[p].y);
    s0.setTargetAbs(path2[p].x);
    s1.setTargetAbs(path2[p].y);


    ctrl.moveAsync(s0, s1);
    p++;
  }
  digitalWriteFast(32, LOW);
}

@luni64
Copy link
Owner

luni64 commented Jul 26, 2022

Glad it works. Instead of changing max speed I'd try the speed override in moveAsync. I.e. you can call it in that way:

ctrl.moveAsync(0.3,s0,s1) 

to move the motors with 30% of max speed. This might be more convenient. All in all the method is not very efficient, it needs to calculate new motion parameter for each segment. So, make the segments not too short.

I was thinking of doing something like a dedicated pathController which could do this much more efficient and therefore more smoothly. You would give it an array or a ringbuffer with coordinates/speed entries and it would sequentially move to this points with the given speed. Implementing this is something which will take quite some time (but would be fun).

Let me know if it works at the end. Also, having it as an example is a good idea.

@luni64
Copy link
Owner

luni64 commented Jul 27, 2022

The end goal is to have a fixed time interval and then calculate the position and speed accordingly, so that each segment is run in a fixed time.

I don't get this part. Is there a special need why you factor in time? This will lead to large jumps in the motor speed which will generate a rather jumpy/rough movement? Can you explain why in your application it is important to have each segment done in the same time?

Shouldn't the trajectory speed be constant during the movement (after acceleration of course)? The trajectory speed could be calculated by some acceleration scheme and the speed of the single motors could be determined from the tractectory speed by simple trig. If you choose the segments short enough (distance wise) you should get a very smooth movement.

@BlueGene00
Copy link
Author

BlueGene00 commented Jul 27, 2022

Here is the background:

It is a multi-axis system and the user will set the overall movement time for all motors and individual keyframes(positions) for each motor. Those keyframes can be location anywhere, so all axis are independent from eachother. Some axis can have only 1-2 keyframes, some 20 keyframes.

The keyframes are connected by bezier-splines, so there is always a smooth transition between the points. This bezier-calculation takes some time, so it is not possible to calculate the next movement in real-time. Instead, I do this before the movement and store the values in arrays.

To "follow" this bezier curve, the entire curve is split into very small linear segments. So lets say the movement is 6 seconds long and I take 300 segments, each segment is 0,02 seconds. So the movement will be updated every 0,02 seconds. I still need to determine the perfect amount of segments/update interval...

This is my approach to run a custom bezier-curve. If there are any other solutions, let me know. But as far as I researched, this linear interpolation is implemented by most CNC machines, so it should be nothing "new".

splines

@luni64
Copy link
Owner

luni64 commented Jul 27, 2022

Ok, got it. Looks like some camera movement system where you need the camera be at some points/angles at specific times. I come from the CNC world, where the time aspect is not so critical, it is more important that the path is followed with a given and constant speed.

For testing I suggest to have a close look at the movement. If one of the motors ends up with a significantly different speed, Bresenham's algorithm tends to generate jumpy movements which might be a problem. Also have a close look at the timing, since time doesn't enter the used algorithms, the movements might take a slightly different time as calculated.

Looking forward to see some results

@BlueGene00
Copy link
Author

Yes, it is indeed a camera motion system ;)

I will have a good look at the timing, speeds etc. This was the problem with accelstepper... The time was not really exact and with a certain speed, the entire movement was slowed down. But I have the picoscope and will measure the step signals and run an evaluation to verify proper behavior. For my first tests it looked very promising. Once everything is verified, I will publish a complete documentation with examples.

Next step would be to test and implement it for the Teensy4. I saw there there is separate library for T4, but it is still in Beta? Since T3.6 will be not available for some more months, it would be good to have an alternative with T4. What is the current status and are the changes above easy to implement into the T4 library?

@luni64
Copy link
Owner

luni64 commented Jul 27, 2022

The T4 beta is a completely different setup. I still needs a lot of work but I didn't find sufficient time so far. I'd rather not do experiments with it at the moment.
I'll have a look if I can come up with a stripped down version for the T4 suitable for your use case.

Can you post some key performance requirements?
E.g.

  • minimal/maximal motor speeds (stp/s)
  • maximum number of motors to be run in parallel

@BlueGene00
Copy link
Author

BlueGene00 commented Jul 27, 2022

With my previous code, I had some issue with small movements/small speeds. There were some rounding errors and this cause some jerky movement at slower speeds. With increased microstepping, it worked much better. That is the reason, why I am currently testing with 1/32 microstepping. This will of course lead to higher step frequency for the controller.

So lets say the motor should reach 3000 rpm, this would be 96.000 steps/s with a standard 200 steps/turn motor. I guess 100k will be sufficient for most cases.

Currently I am testing with 6 motors, but having the ability to go up to 10 (or even higher) would be great. I know that there will be some processing limitation at some point, but in this case, I could use multiple Teensys and trigger the movement by a common start signal.

How is overclocking of the Teensy effecting the library? I guess most tests were performed with the standard clock frequency.

Thanks for your support ;)

@BlueGene00
Copy link
Author

Here is a basic first preview of the movement:
https://youtu.be/KsOfKRVKJsQ

testmove

The input was 5 keyframes (0 / 32.000 / 8.000 / 32.000 / 0) for all 6 stepper motors. Then there was the bezier calculation which split up the entire movement into 400 segments. Moving duration is 4s, so each segment is 10ms. The movement is identical, because all motors received the same input positions. But the code in the background was individual for each stepper, so I just need to change the input position and the movement would be different. So far so good, but I noticed some smaller issues.

This measurement here was from a different motion, but the issue is the same.

Positive positions:
Positive

Negative positions:
Negative

As you can see, when the movement is "positive", the DIR signal stays low the entire time. But when the direction is negative, you can definitely some small gaps in the DIR signal each time there is a transition between the segments.

Time between the gaps is ~ 10ms, which is the update interval.
10ms

The gap length of the DIR-signal is ~ 1ms.
1micsec

I guess this will have no big influence, since this gap in the DIR is that short and most times it is not during a STEP impuls, so it will have no effect on the movement. But of course there could be some situations, were they align and this would impact the movement, since the motor will try to change direction for that one single step.

As you can see in the STEP signal, there is also a larger gap, but I can not tell for now if this will slow down the movement at some point or cause other issues, need to investigate further...

Another issue I have noticed, is when you have for example one axis which will have no movement. So you might want to run Motor A the entire time, lets say 10 seconds, but Motor B should only run from 0 to 5 seconds. So the last 5 seconds, Motor B will have a speed of 0 and the position change is also 0. This will definitely cause a crash in the library, because you try to devide by 0 at some point and this is not handled in the library. I did a quick fix and will not transmit a speed value of 0, but will change it to 1, then it works. But of course, this is not a real solution, just a work-around.

Will do more tests, but until now, it looks really good and is definitely much faster and more precise then my previous solution I tried with Accelstepper.

@luni64 luni64 closed this as completed Jul 29, 2022
@luni64 luni64 reopened this Jul 29, 2022
@atlesg
Copy link

atlesg commented Aug 3, 2022

@BlueGene00 Hi, your topic is very interesting. May I talk to you directly to learn more about motion control in CNC / Cinematography? I've been researching in these topics for a while but I don't think I know enough. It's okay if this is improper, I would be happy catching up any new comment from Luni64 and you. Cheers and I hope TeensyStep will solve everyone's problem in no time! :D

@BlueGene00
Copy link
Author

@atlesg Yes, a direct discussion might be the fastest way to exchange different ideas. How do I contact you directly?

I will keep this topic updated anyways. This current issue which I have noticed, is when the target values and/or speed is 0, then the entire library will fail. I try to investigate further, but this must be taken care of. If there is no movement of the segment, the time must delayed anyway.

@luni64
Copy link
Owner

luni64 commented Aug 4, 2022

While using TeensyStep and a dummy accelerator "kind of works" for the task it is definitely not very efficient. To test better solutions I wrote a dedicated lib from scratch last week. It works much better but needs some improvements and fixes. I hope I have something to share in the next days.
It runs on T3.x and T4.x boards which might be good during the ongoing chip shortage.

@atlesg
Copy link

atlesg commented Aug 4, 2022

@atlesg Yes, a direct discussion might be the fastest way to exchange different ideas. How do I contact you directly?

I will keep this topic updated anyways. This current issue which I have noticed, is when the target values and/or speed is 0, then the entire library will fail. I try to investigate further, but this must be taken care of. If there is no movement of the segment, the time must delayed anyway.

Hi yes! I use many social accounts. Do you have Discord? mine is Morty#8279 - I also have Whatsapp, Viber... but I don't think it's not suitable to post them here. Let's catch up (I don't know how to text private message on Github tho...)

@atlesg
Copy link

atlesg commented Aug 4, 2022

While using TeensyStep and a dummy accelerator "kind of works" for the task it is definitely not very efficient. To test better solutions I wrote a dedicated lib from scratch last week. It works much better but needs some improvements and fixes. I hope I have something to share in the next days. It runs on T3.x and T4.x boards which might be good during the ongoing chip shortage.

No word can describe your effort. Guess I gotta find some T3 and T4 in country, they are quite overpriced here because not many people use it. Do you suggest any shield/stack up board to use with Teensy (Op-Amp board will protect Teensy from Stepper/Servo Motors).

@luni64
Copy link
Owner

luni64 commented Aug 5, 2022

Do you suggest any shield/stack up board to use with Teensy (Op-Amp board will protect Teensy from Stepper/Servo Motors).

Depending on the size of your motors I recommend to either use one of the ubiquitous small drivers (DRV8825, A4988...). For larger motors a good choice is one of the digital Leadshine drivers. (Be careful, a lot of not so good Leadshine clones are around). Use a drive voltage as high as possible to get good performance. There is no need to protect the teensy if you use one of the small drivers. For the larger drivers with opto coupled inputs a 3.3->5V converter (e.g. 74HC245) might be necessary.

Make sure that you get a good stepper. Look for low inductance motors (see here for more information: https://luni64.github.io/TeensyStep/applications2/901_stepper/steppers#electrical-parameters)

@atlesg
Copy link

atlesg commented Aug 10, 2022

@luni64 Thank you! Please keep us updated with your new dedicated lib. I think you should open some donation links, I would like to contribute to your awesome project.

@SourabhPrasad
Copy link

Hi, I have been testing the following code with a single motor to follow the path with varying speed. I seem to have stumbled onto an issue.
I am trying to execute the following motion.

pos path[] = {
  {8000, 0.3f},
  {16000, 0.4f},
  {20000, 0.5f},
  {24000, 0.4f},
  {28000, 0.3f},
  {32000, 0.1f},
};

While execution, when updating to {24000, 0.4f} the motor stops moving, but control.getPosition() keeps incrementing and updating to the next position and velocity. In addition, at 32000 steps, the velocity does not become zero and control.getPosition() continues to increment.
This issue has only been observed when the velocity values of any element after the path[3] is lower that the previous value. If the velocity value is greater, the motor rotates as expected.
Here is the full code

#include "Arduino.h"
#include "TeensyStep.h"
#include "NoAccelerator.h"

//define motor pins
#define STEP    2
#define DIR     3
#define ENABLE  4
#define ALARM   5
#define BRAKE   6

const int maxSpeed = 3500;
const int maxAcceleration = 3500;
static unsigned p = 0;

struct pos
{
    int position;
    float velocity;
};

pos path[] = {
  {8000, 0.3f},
  {16000, 0.2f},
  {20000, 0.5f},
  {24000, 0.4f},
  {28000, 0.3f},
  {32000, 0.1f},
};
constexpr size_t pathLength = sizeof(path) / sizeof(path[0]);

using customControl = TeensyStep::StepControlBase<NoAccelerator, TimerField>;
customControl control;
Stepper motor(STEP, DIR);

void onPositionReached();
void handleCommands();

void setup() 
{
  Serial.begin(9600);

  control.setCallback(onPositionReached);
  motor.setMaxSpeed(maxSpeed);
  motor.setAcceleration(maxAcceleration);
  motor.setStepPinPolarity(LOW);
  motor.setInverseRotation(true);

  pinMode(ENABLE, OUTPUT);
  digitalWrite(ENABLE, HIGH);
  pinMode(BRAKE, OUTPUT);
  digitalWrite(BRAKE, LOW);
}

void loop() 
{
  handleCommands();
}

void handleCommands()
{
  if (Serial.available() > 0)                 // skip if the serial buffer is empty
  {
    int cmd = Serial.read();                  // get one char from the buffer...
    switch (cmd)                              // ... and analyze it
    {
      case '1':                               // move command
        if(!control.isRunning())
        {
          Serial.println("Starting Motor");
          onPositionReached();
        }
        break;
      case '2':                               // stop command
        control.stopAsync();                  // initiate stopping procedure
        Serial.println("Stopping motor");
        break;
      default:
        break;
    }
  }
}

void onPositionReached()
{
  Serial.println(p);
  Serial.print("Position: ");
  Serial.print(motor.getPosition());
  Serial.print(" | Velocity: ");
  Serial.println(control.getCurrentSpeed());
  if (p < pathLength) 
  {
    if(path[p].velocity != 0.0f)
    {
      motor.setTargetAbs(path[p].position);
      control.moveAsync(path[p].velocity, motor);
      p++;
    }
  }
}

@luni64
Copy link
Owner

luni64 commented Aug 10, 2022

Sorry for answering slowly. I'm currently recovering from a covid infection. I still need a couple of days before I can continue working on this.

@BlueGene00
Copy link
Author

@luni64 Any update on this topic? Hope you are feeling better now ;)

@luni64
Copy link
Owner

luni64 commented Aug 30, 2022

Oh, looks like I was distacted by other stuff after the infection. Frankly, I completely forgot about it. Can't promise, but I try to work on it on the weekend.
Sorry for that

@BlueGene00
Copy link
Author

@luni64 Have you checked?

I have made some further testing with the code above and found some points. Point 1 is currently the most important for me.

Point 1:
I have tried to implement the code above into my other code, but had no success.

When it is inside the "setup", it is working properly:

void setup(){
...
ctrl.setCallback(onPositionReached);
onPositionReached();
...
}

But when I move the functions into the loop, it does not work:

void loop(){
...
ctrl.setCallback(onPositionReached);
onPositionReached();
...
}

To avoid any influence from the other code, I would like to run this in a while loop. This should be put inside a function, so that I can just call it from anywhere else inside the "loop", for example:

void performMovement()
{
   while (currentSegment< totalSegmentAmount)
     {
       stepperX.setMaxSpeed(newSpeedArrayX[currentSegment]);
       stepperY.setMaxSpeed(newSpeedArrayY[currentSegment]);

       stepperX.setTargetAbs(myArrayX[currentSegment]);
       stepperY.setTargetAbs(myArrayY[currentSegment]);

       ctrl.moveAsync(stepperX, stepperY);

       currentSegment++;
     }
  Serial.println("Movement is finished");
}

I have tried almost everything, but as soon as the functions are not inside the "setup", it will not work. It will just go through the function, but there will be not movement...

Point 2:
I did some "high-speed" testing, so 6 axis with around 128 kHz step frequency. I have noticed, that the Teensy CPU speed will have an influence on the step output.
180 MHz CPU --> total steps 239.977 (should be 240.000)
196 MHz CPU --> total steps 229.761 (should be 240.000)
216 MHz CPU --> total steps 239.994 (should be 240.000)
240 MHz CPU --> total steps 240.000 (should be 240.000)
256 MHz CPU --> total steps 240.000 (should be 240.000)

So I got the exact step amount only with 240 and 256 MHz. There was a big error with 196 MHz... I need to investigate what the max. possible Step-frequency is, or at which frequency the CPU speed will lead to different results. Not important for now, but if you are working on "special library", this should be considered, especially when the library should be compatible with Teensy 3.6 and 4.1.

Point 3:
Another point to consider is a segment with no movement. Imagine a very slow movement at the beginning. With the right combination of duration and distance, this could be distance = 0 and speed = 0. Having distance = 0 will just mean, that the absolute position will not change. This is no issue for the code/library as it is right now. But having speed=0 will block it. That is why I have added a function to avoid speed=0. It will just replace 0 with a value of 100, but there will still be not movement, since the position is identical.

But we still need to consider, that the code must pause for the segment duration, no matter if there is a movement or not. This is why I have added a "support" axis, which is not connected to an actual motor, but just will change position value between each segment. So lets say, we have a 10 second overall movement with 100 segments. So each segment has a duration of 0.10 seconds. Now I take this help axis and move between position = 0 and position =500 for example. So the speed is 500/0.1=5000. With this, it is ensured, that the movement on all other actual axis will be paused and not just skipped by the code. It is just a work-around, I guess there are much "cleaner" solutions for this kind of problem.

@SourabhPrasad
Copy link

Hey @luni64, any update on this issue?

@vp8181
Copy link

vp8181 commented Sep 28, 2022

Hello sir @luni64
First of all thankyou so much for this library. I am eagerly waiting to use teensystep4 lib on my 4.1 as there is shortage of 3x .
This library will be really helpful in my clg project.
Once again thankyou so much.

@BlueGene00
Copy link
Author

Any update on this topic or the support for Teensy 4.x? Teensy 3.6 will not be available for some more months, so it would be great to have a version for 4.x

@atlesg
Copy link

atlesg commented Jan 24, 2023

Any update on this topic or the support for Teensy 4.x? Teensy 3.6 will not be available for some more months, so it would be great to have a version for 4.x

@BlueGene00 Have you tried STM32? quite good alternative for Teensy shortage crisis

@BlueGene00
Copy link
Author

STM32 would require a complete redesign of my control board. So I would like to stick with Teensy.

There is a new update on the Teensy 3.6 page:
Update: February 14, 2023: We are waiting for MK66 chips. Latest word from NXP is a small quantity (of the parts we ordered in 2021) are expected by June 2023. Larger quantity are delayed until at least 2024. We highly recommend migrating to [Teensy 4.1](https://www.pjrc.com/store/teensy41.html). For audio projects needing the DAC pins, we recommend using the [PT8211 DAC](https://www.pjrc.com/store/pt8211_kit.html). All signs we are seeing paint a grim future for the MK66FX1M0VMD18 chip. Please do yourself a huge favor and migrate to Teensy 4.1.

So it would be good to have an adjusted library for 4.1. I guess there are some more people who use Teensy for stepper control, so it definitly help. AccelStepper is not realy an alternative, unless you drive with very low step frequencies. Let hope @luni64 or somebody else finds some time to finish the TeensyStep for 4.x

@HackinSpock
Copy link

@BlueGene00 Hi there! I discovered this thread and I am also working on my own motion control system for Teensy 4.1. I was able to fork the teensystep4 library and implement 74HCT595 level shifters to expand the # of stepper drivers (in this case I need 16 steppers).

I would love to get to talk to you through DMs about your implementation of motion control software in teensystep. Thank you!

@HackinSpock
Copy link

Any update on this topic or the support for Teensy 4.x? Teensy 3.6 will not be available for some more months, so it would be great to have a version for 4.x

@BlueGene00 Have you tried STM32? quite good alternative for Teensy shortage crisis

Hey atlesg. I sent you a discord friend request/PM if you'd be interested in checking that out. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants