Closed kthod861 closed 2 years ago
Hi,
happy to hear, that you can successfully drive five steppers with esp32. Still one to go. From your description it is not clear, what your application really is. Perhaps a short video would help to understand it.
So i can only comment on the provided information as I interpret your application from it:
moveTo()
, you lack the speed. If you focus on the speed and calculate the moveTo()
value accordingly, then at the next path point you may have deviation from the target position.moveTo()
commands, which ensure arrival at the right time and speed at the next path point.moveByAcceleration()
and correct over/undershooting on the fly.As I have written in the README, the recommendation is - especially because you use a performant esp32 - to switch to raw commands using addQueueEntry()
. If acceleration/deceleration is not needed, then the implementation is more or less straightforward. For each time step of 1/30s aka 33ms you need to create 16 commands for each stepper (every command should cover 1-2ms). One command then issue either no step (aka a pause of x timer ticks) or 1 step or many steps at defined frequency.
If acceleration/deceleration is needed, then the question is, if all five steppers should run synchronously aka the fastest stepper determine the acceleration profile for the other steppers. Or every stepper can use its own acceleration profile, but just need to ensure to be at the next path point at the right time and with the right speed. In any case, the implementation is much more complex.
Not sure, if this explanation is clear enough.
Hi, Thanks for the fast answer!
Here's an already old stage of the project showing a little bit more what i'm trying to achieve: https://www.youtube.com/watch?v=QUfriYirfP0
I'm currently providing position and speed for each path point and make sure i use the last known position as a reference for the next frame/move calculation ( this way i think i minimize the deviation/drift ).
And yes i don't need any accel/decel as it's provided by the incoming animation, i agree if i had to use it ,it would be a hell to manage ;)
So as you mentioned :
Again, thanks a lot for your support.
Hi,
nice video. Now it gets more clear. Thank you.
For the raw commands, there are two examples: RawAccess and RawAccessWithPause
For your questions and additional explanation: 1-2 ms is the typical length of a command sent to the queue. Each stepper has its own queue, which can keep 32 commands. So with 5 Steppers, there are 5 queues. Maximum command length (duration) is 255*65535/16 Mhz= 1s, which issues 255 steps at a frequency of 16MHz/65535=244Hz. Anything lower than 244 Hz requires additional pause commands.
Hi, Ok, will try to decypher those examples again :)...
I'm far from being comfortable with that kind of low level coding as i'm learning by myself all those subtleties... For example, 3 weeks ago if you've asked me how struct works i couldn't have answered ;)..now i can ( enough so i can use them at least ).
i'll stop asking questions because i've too much of them like why ticks are set to 32768 if > 65535...what's the relationship between ticks and step etc etc etc... and will do some more unit testing.
Regards
So here is the unit test i'm trying to put in place :
Honestly, it....work....but i've no real clue why, if i double the frame array it won't do 2 turns, etc.
Pretty sure i'm missing something obvious here but i don't really see outside of Ticks where i may have totally misunderstood them or those delay that seems to have a big impact here.
void setup() {
Serial.begin(115200);
engine.init();
stepper = engine.stepperConnectToPin(stepPinStepper);
if (stepper) {
stepper->setDirectionPin(dirPinStepper);
stepper->setEnablePin(enablePinStepper);
stepper->setAutoEnable(true);
stepper->enableOutputs();
}
int lframes[] = {107,107,107,107,107,107,107,107,107,107,
107,107,107,107,107,107,107,107,107,107,
107,107,107,107,107,107,107,107,107,97};
// a representation of a 360 degree rot at 30 fps
int lenAnim = 30;
for (uint16_t i = 0; i < lenAnim ; i++) {//for each frame
int steps = lframes[i];
int divider = 15;
int m = 1;
while (steps > 0 && divider > 0) {// divide and prep 15 step values
////a way to get 15 int values averaged..kind of
uint8_t subvalue = floor(steps / divider / m) * m; //
steps -= subvalue;
divider--;
///
////2880 is the freq needed for a 360 deg rotation in 1 sec using 3200 steps
const struct stepper_command_s cmd_step = {
.ticks = 2880, .steps = subvalue, .count_up = true};
int rc = stepper->addQueueEntry(&cmd_step);
delayMicroseconds(1000); // i believe this is to avoid overfiling the queue ?
}
delayMicroseconds(3000);// i believe this is to avoid overfiling the queue ?
}
}
This compiles, but not sure, if this works. I have used the overall frame of your code and changed it accordingly.Hopefully it is good enough commented to be understandable:
#include "FastAccelStepper.h"
// for avr: either use pin 9 or 10 aka OC1A or OC1B
#define stepPinStepper 17
#define enablePinStepper 26
#define dirPinStepper 18
FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper;
void setup() {
Serial.begin(115200);
engine.init();
stepper = engine.stepperConnectToPin(stepPinStepper);
if (stepper) {
stepper->setDirectionPin(dirPinStepper);
stepper->setEnablePin(enablePinStepper);
stepper->setAutoEnable(true);
stepper->enableOutputs();
}
int lframes[] = {107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
107, 107, 107, 107, 107, 107, 107, 107, 107, 97};
// a representation of a 360 degree rot at 30 fps
int lenAnim = 30;
#define TICKS_PER_FRAME (TICKS_PER_S / 30)
for (uint16_t i = 0; i < lenAnim; i++) { // for each frame
int steps = lframes[i];
// THIS WORKS ONLY, IF STEPS > 0
uint32_t ticks_per_step = TICKS_PER_FRAME / steps;
uint32_t this_frame_steps = 0;
while (this_frame_steps < steps) {
// repeat as long as not all steps for this frame created...
if (ticks_per_step <= 65535) { // does it fit into an uint16_t ?
// Yes, so every command will generate at least one step
uint8_t this_cmd_steps = 1;
if (this_cmd_steps * ticks_per_step < TICKS_PER_S / 500) {
// in order to have ~2ms long command, this command should issue
// multiple steps
this_cmd_steps = TICKS_PER_S / 500 / ticks_per_step;
}
struct stepper_command_s cmd_step = {.ticks = (uint16_t)ticks_per_step,
.steps = this_cmd_steps,
.count_up = true};
// add command to the queue
int rc;
do {
rc = stepper->addQueueEntry(&cmd_step);
if (rc > 0) {
// so the queue is busy => put the task to sleep for
// portTICK_PERIOD_MS
vTaskDelay(1);
}
} while (rc > 0); // repeat addQueueEntry, if queue is busy
// sum up the generated steps
this_frame_steps += this_cmd_steps;
} else {
// For one step, one command with a step + one or several pauses are
// needed
// let's remember how many ticks still to be generated
uint32_t remaining_ticks = ticks_per_step;
// Let's first do the step
struct stepper_command_s cmd_step = {
.ticks = 16384, .steps = 1, .count_up = true};
// add command to the queue
int rc;
do {
rc = stepper->addQueueEntry(&cmd_step);
if (rc > 0) {
// so the queue is busy => put the task to sleep for
// portTICK_PERIOD_MS
vTaskDelay(1);
}
} while (rc > 0); // repeat addQueueEntry, if queue is busy
// sum up this one step
this_frame_steps += 1;
// and reduce the remaining ticks
remaining_ticks -= 16384;
// Now create the pauses until remaining_ticks is 0
while (remaining_ticks > 0) {
uint16_t this_cmd_ticks;
// do remaining ticks fit into an uint16_t ?
if (remaining_ticks > 65535) {
// nope, so make a pause of 32768 ticks
this_cmd_ticks = 32768;
} else {
// yeah, fits. So use that value
this_cmd_ticks = (uint16_t)remaining_ticks;
}
// make a pause command
struct stepper_command_s cmd_pause = {
.ticks = this_cmd_ticks, .steps = 0, .count_up = true};
// and add it to the queue
int rc;
do {
rc = stepper->addQueueEntry(&cmd_pause);
if (rc > 0) {
// so the queue is busy => put the task to sleep for
// portTICK_PERIOD_MS
vTaskDelay(1);
}
} while (rc > 0); // repeat addQueueEntry, if queue is busy
// reduce the remaining ticks
remaining_ticks -= this_cmd_ticks;
}
}
}
}
}
void loop() {}
This multiplication may be problematic:this_cmd_steps * ticks_per_step
Eventually need to be (uint32_t)this_cmd_steps * ticks_per_step
Thanks a lot,
Tried it (with the patch) and it work but seems to output more than 3200 steps, if i trigger it multiple time i can see it drifting. Now i need to understand ,) !
Could be, that this calculation causes the last command of a frame to add too many steps:
if (this_cmd_steps * ticks_per_step < TICKS_PER_S / 500) {
// in order to have ~2ms long command, this command should issue
// multiple steps
this_cmd_steps = TICKS_PER_S / 500 / ticks_per_step;
}
Eventually need to add:
if (this_cmd_steps + this_frame_steps > steps) {
this_cmd_steps = this_frame_steps - steps;
}
hum this just make 2 and a half turn almost :(..but don't worry i know what i'm doing tonight !, with some time i should be able to debug
Yeah. The stupidest errors are in the easiest code parts. This is correct:
if (this_cmd_steps + this_frame_steps > steps) {
this_cmd_steps = steps - this_frame_steps;
}
The previous one has caused an 8bit overflow and this way 10586 steps have been generated. The correct version creates exactly 3200 steps. Just have verified it with the avr-simulator.
Sorry for the inconvenience
No worries, you're already helping me way much than i was expecting... And my overheating brain says thanks, sounds like it's ok now.
out of curiosity, is this something you plan to add as an official command at some point ?
Eventually add it as an example
Thanks a lot !
Works perfectly, i just need now to implement frames with no move, shouldn't be complicated.
Here's the current project status: https://youtu.be/fm2_VkUG10k
Thanks for sharing this cool video and for the intro. Hope it is ok to list it under third party videos.
Hello,
[ Don't hesitate to remove this if it's too dumb of a question ]
I'm almost 100% successful using your lib to drive 5 steppers from an ESP32 board. But i'm wondering if i'm using this lib the right way in my situation... any guidance is welcome.
So, every 1/30 sec i give to all five steppers a new set of frequency and MoveTo position ( that are precalculated to fit the needs ). This work quit correctly but still i have two issues i don't really know how to deal.
I have to set the acceleration to 1000000 as it seems the only way to get a linear motion but i'm really not confortable with this as i've no clue exactly how it behave ( and probably cost some process time for no reason).
I found it difficult to ensure smooth motion between frames (when the input values come from a smooth animation) as the MoveTo will have tendency to stop (yes i know it's a normal behavior ;) ).
So i can totally live with the current results i have but i can't stop thinking i may have choosen the wrong path here from the begining...
Again, don't hesitate to close/erase this if not clear or too dumb. i can share more details if needed but i think the above description should be enough.
Regards