@dc42 great news!
The modulation parameter goes from -27 to 39.
@dc42 great news!
The modulation parameter goes from -27 to 39.
@dc42 unfortunately, the IDE doesn't say anything more than the datasheet/application notes.
My understanding here is that the sine wave is encoded as a list of increments/decrements to add to the START_SIN and START_SIN90 registries while increasing the microsteps. Since START_SIN and START_SIN90 represent the current of the two coils at the beginning of the electrical revolution, the value has to be greater or equal than zero.
It can be tuned for some desired behaviours or to make some weird configurations feasible (as in the picture below, where for offset less than 28 the wave is not feasible).
In general, from the examples in the datasheet and application notes, I saw the START_SIN registry always set to zero.
@dc42 here they are. The sine offset should be "the absolute current at entry of the lookup table". It shifts up or down the sine wave, so to have it feasible I had to reduce the microstep amplitude. The maximum offset is not feasible, the higher it gets, the flatter the sinewave must be. Hope it's enough to clarify the behaviour, if you need something else, let me know.
@AndyE3D I followed the driver's datasheet and the application note 26:
https://www.analog.com/en/resources/app-notes/an-026.html
https://www.analog.com/media/en/technical-documentation/data-sheets/TMC5160A_datasheet_rev1.17.pdf
These are settings strongly dependent on the motor itself, and I have only nema24 motors. The configuration I posted is the best I found for this type of motor.
@AndyE3D I am using the TMC5160 evaluation kit (https://www.mouser.de/ProductDetail/ADI-Trinamic/TMC5160-EVAL-KIT?qs=TiOZkKH1s2TJebvRqsmGsQ%3D%3D).
Once you connect the eval-kit to the pc you will find something called "sinus tool" and you can play with it. This is what in the user manual is called "microstepping look-up table"... I'm not sure if with "phase modulation" you're referring to the same thing.
@dc42 Thinking in abstract terms, to completely describe and have full control of such a transition, the total amount of parameters might be: max speed change, min speed change (square corner speed), angle threshold (to determine at which angle the curve has to drop), and the slope of the curve.
Focusing on the parameters and not on the type of curve now, I agree that for simplicity two parameters are preferable, but if we use max speed change and min speed change, the user doesn't have control over the transition between the two speeds. I think the slope of the curve can be hard coded (with a parameter or by the type of the curve itself), but if I had to choose a second parameter, between a min speed change or an angle threshold, I would rather prefer to have control on the angle threshold and have the min speed change decided by the FW (it might also be the minJerk).
@dc42 thanks for the feedback!
@gloomyandy here it is. It's not much more complicate than the original DDA::MatchSpeed function. It can also be simplified by removing the rotation matrix definition and just compute the X coordinate of v2 in the reference frame of v1.
I also decided to implement it only for xy moves and keeping the possibility to disable it by setting the JerkDecay parameter to zero.
void DDA::MatchSpeeds() noexcept
{
// v2 needs to be expressed in the reference frame of v1
// directionVector and next->directionVector are unit vectors
// The rotation matrix R(-angle) can be expressed by using the directionVector[0] and directionVector[1] which are the X and Y components of v1 in the absolute reference frame (v1_0).
const float rotationMatrix[2][2] = {
{ directionVector[0], directionVector[1] },
{ -directionVector[1], directionVector[0] }
};
// Project v2 in the reference frame of v1 (v2_1)
const float nextVectorProjected[2] = {
rotationMatrix[0][0] * next->directionVector[0] + rotationMatrix[0][1] * next->directionVector[1],
rotationMatrix[1][0] * next->directionVector[0] + rotationMatrix[1][1] * next->directionVector[1]
};
debugPrintf("JUNCTION: START targetNextSpeed [mm/s]: %f \n", (double)InverseConvertSpeedToMmPerSec(beforePrepare.targetNextSpeed));
DebugPrintVector("JUNCTION: directionVector \n", directionVector, 2);
DebugPrintVector("JUNCTION: next->directionVector ", next->directionVector, 2);
DebugPrintVector("JUNCTION: nextVectorProjected ", nextVectorProjected, 2);
for (size_t drive = 0; drive < MaxAxesPlusExtruders; ++drive)
{
if (directionVector[drive] != 0.0 || next->directionVector[drive] != 0.0)
{
const float totalFraction = fabsf(directionVector[drive] - next->directionVector[drive]);
const float jerk = totalFraction * beforePrepare.targetNextSpeed;
float allowedJerk = reprap.GetPlatform().GetInstantDv(drive);
if (flags.xyMoving && flags.isPrintingMove && reprap.GetPlatform().JerkDecay() != 0)
{
const float A = (reprap.GetPlatform().GetInstantDv(drive) - ConvertSpeedFromMmPerSec(MinimumJerk));
const float B = reprap.GetPlatform().JerkDecay();
const float C = ConvertSpeedFromMmPerSec(MinimumJerk);
allowedJerk = A / (1 + (float)exp(B * (-nextVectorProjected[0] + reprap.GetPlatform().JunctionDeviation()))) + C;
debugPrintf("JUNCTION: allowedJerk for drive %i: %f \n", drive, (double)InverseConvertSpeedToMmPerSec(allowedJerk));
}
if (jerk > allowedJerk)
{
beforePrepare.targetNextSpeed = allowedJerk/totalFraction;
}
}
}
debugPrintf("JUNCTION: junctionSpeed [mm/s]: %f \n", (double)InverseConvertSpeedToMmPerSec(beforePrepare.targetNextSpeed));
}
@gloomyandy your interpretation is correct. However, I have a couple of parameters that allow to change the behaviour of the curve.
The function is:
allowedJerk = A / (1 + e^(B*(cosAngle + cosAngleThreshold))))+ C
where:
Below the plot of the function with maxJerk = 600 mm/min, minJerk = 6 mm/min cosAngleThreshold = -0.707 (following the first picture, alpha=45deg) and increasing B.
In that configuration, vectors at 45deg will have an allowableJerk of 300mm/min, while for 90deg 6mm/min. I think having such a low jerk for sharp corners is not bad, also it allows the input shaping to work better since you have a bigger acceleration profile.
This approach gave me more flexibility, and with a proper setting of parameters you can have pretty much the same behaviour of the junction deviation. I can post the code of course if anyone is interested.
The weak point I see in both approaches (or working only inside DDA::MatchSpeeds) is when the round corners have a very fine mesh and the angle between two consecutive vectors is small. In that case, you may have the "illusion" of traveling a linear path while actually doing a circle and then end up going too fast.
@oliof the possibility of having the s-curves sounds exciting! Really looking forward to it.
Meanwhile, I did some other tests to smooth the motion of the printer I'm currently working with.
As @gloomyandy suggested, I tried to implement the grbl junction deviation algorithm inside the function DDA::MatchSpeed, but I wasn't able to get decent behavior out of it.
I believe the main reason why the junction deviation didn't perform well is because of the slow decreasing behavior in function of the angle, having again the problem of balancing the high jerk for arcs and the low jerks for corners.
So I improved my "adaptive jerk" approach, implementing a continuous function to scale down from the maximum to the minimum jerk value. I'm still under testing and tuning it but so far it seems to perform quite well. Below the difference of the junction deviation (tuned to have decent arcs) compared to the "adaptive jerk". (X axis is -cos(alpha) wrt the first sketch).
@gloomyandy good points. So far I cannot tell you if I noticed artifacts due to rounding, but it might be possible.
The junction deviation might be a good improvement, although I'm not sure how to merge this approach with the current workflow.
From my understanding the purpose of DDA::MatchSpeed is to check whether beforePrepare.targetNextSpeed is actually feasible by the drives, so implementing there the junction deviation speed would mean "overwriting" how beforePrepare.targetNextSpeed was computed before.
Does someone know if there are plans to implement the junction deviation or something similar in the codebase?
Hi all,
I am doing some motion tuning for a quite big and heavy machine. In general I noticed that having high values of jerk (about 500-600 mm/min) are good enough to travel circular paths smoothly (even tough it depends a lot on the mesh quality), but they are a bit high when printing corners, reducing a lot the effect of the input shaping and introducing lot of ringing.
Since I couldn't find a decent balance of jerks, acceleration and the Input Shaping L parameter, I modified a bit the function DDA::MatchSpeed and the command M566:
Basically my idea is to estimate the angle between two consecutive segments and if it is greater than a certain threshold I use a lower value of jerk.
I added to M566 a parameter J "junctionDeviation" which is the threshold below which an high value of jerk is used (the one set from M566 X-- Y--). For greater values, the minimum jerk multiplied by the parameter S is used.
I consider as threshold the difference between the X components of v1 and v2 in the reference frame of v1, and since v1 and v2 are unit vectors the threshold is equal to (1 - cos(alpha)).
void DDA::MatchSpeeds() noexcept
{
// v2 needs to be expressed in the reference frame of v1
// directionVector and next->directionVector are unit vectors
// The rotation matrix R(-alpha) can be expressed by using the directionVector[0] and directionVector[1] which are the X and Y components of v1 in the absolute reference frame (v1_0).
const float rotationMatrix[2][2] = {
{ directionVector[0], directionVector[1] },
{ -directionVector[1], directionVector[0] }
};
// Project v2 in the reference frame of v1 (v2_1)
const float nextVectorProjected[2] = {
rotationMatrix[0][0] * next->directionVector[0] + rotationMatrix[0][1] * next->directionVector[1],
rotationMatrix[1][0] * next->directionVector[0] + rotationMatrix[1][1] * next->directionVector[1]
};
// Difference of X and Y components in the reference frame of v1 (v1_1 - v2_1)
const float relativeFraction[2] = {
fabsf(1 - nextVectorProjected[0]),
fabsf(0 - nextVectorProjected[1])
};
for (size_t drive = 0; drive < MaxAxesPlusExtruders; ++drive)
{
if (directionVector[drive] != 0.0 || next->directionVector[drive] != 0.0)
{
const float totalFraction = fabsf(directionVector[drive] - next->directionVector[drive]);
const float jerk = totalFraction * beforePrepare.targetNextSpeed;
float allowedJerk = reprap.GetPlatform().GetInstantDv(drive);
// if dealing with X and Y axes reduce jerk if sharp edges
if (drive < 2 && relativeFraction[0] > reprap.GetPlatform().JunctionDeviation())
{
allowedJerk = ConvertSpeedFromMmPerSec(MinimumJerk * reprap.GetPlatform().MinimumJerkMultiplier());
}
if (jerk > allowedJerk)
{
beforePrepare.targetNextSpeed = allowedJerk/totalFraction;
}
}
}
}
Of course the code is not optimised, but it was just a first test to see if the idea could work.
I have done a couple of prints with a bit extreme setting (M566 X600 Y600 J0.01 S1) and I noticed a good improvement in print quality. Corners don't induce much ringing, so the input shaping can play a role there, and circular motions are done without too much scattering.
I'd like to have some feedback from this approach, especially if you see weird/dangerous edge cases that I didn't think about, in the code implementation and/or in the machine behaviour.
Thank you in advance to all of you!
@dc42 yes, I set the driver parameters as in the TMCL-IDE (I copied the contend of the register 0x6C with M569.2).
I am using a custom board, I will check over the next few days if there is interference in the output of the TMC5160 distorting the signal going to the motor.
@dc42 thank you for having looked into that, the conversion now works.
Unfortunately, the behavior of the motors after changing the lookup table is still not the same as with the TMCL-IDE.
hi @droftarts, I confirm that if I read a readable registry (e.g. M569.2 P10.0 R106) the command works.
What I see now, is that for some hex values, the command doesn't work because the value I'm passing is converted to a floating point.
M569.2 P10.0 R97 V{0xA5292492}
Error: at column 32: M569: expected non-negative integer
echo {0xA5292492}
2770937088.0
also, this number is different from a conversion I've done with a calculator (0xA5292492 --> 2770936978).
The following value it seems to be ok.
echo V{0x44888888}
1149798536
Why do you think the conversion is generating a floating point number?
@dc42 thank you for the clarification and the suggestion. Unfortunately I get some errors with some hex values:
M569.2 P10.0 R97 V{0xA5292492}
Error: at column 32: M569: expected non-negative integer
and if I echo the string, I get a decimal number which is different from a conversion I've done with a calculator (0xA5292492 --> 2770936978).
echo {0xA5292492}
2770937088.0
echo V{0x44888888}
1149798536
Can you replicate this behaviour?
Hi all,
I'm doing some tests with NEMA24 ST6018 motors and TMC5160 driver using the Trinamic eval-kit and the TMCL-IDE.
The IDE allows also to define a lookup table to compensate torque ripples, and by using the settings in the picture below, I noticed a huge noise reduction of the motor.
You can find also attached the export of the registries' content from the TMCL-IDE.
20240815_10.40.36_TMC5160_Settings.txt
Now I'm trying to port these settings in a Duet-based system but unfortunately, I'm not getting the same results... I'd say the noise even got slightly worse.
I have a CAN-connected expansion board and I've run these commands to set the microstepping LUT (I converted to decimal the content of the registry from 0x60 to 0x69).
M569.2 P10.0 R96 V1149798536
M569.2 P10.0 R97 V2770936978
M569.2 P10.0 R98 V2840930900
M569.2 P10.0 R99 V575228500
M569.2 P10.0 R100 V2082
M569.2 P10.0 R101 V2876095456
M569.2 P10.0 R102 V2299679402
M569.2 P10.0 R103 V525328
M569.2 P10.0 R104 V4294943830
M569.2 P10.0 R105 V16121857
Furthermore, if I run the M569.2 commands without the V param to check if the registries are correctly written, I always get 0x00000000.
Does anyone have a hint if this could be the right approach to implement the lookup table and how I could check the registries' content?
Did someone try to exploit this Trinamic feature?
Thank you in advance.
Hi all,
I tested on MB6HC+TOOL1LC with RRF 3.5.2 a very long extrusion move (G1 E20000 F100
) but the extruder is not able to do it successfully. It starts but it seems to be stuck or losing steps (see video attached).
In another setup, I have also the extruder connected to the mainboard (Mini5+) and the same command runs properly.
Does anyone have an explanation for that? It seems related to the CAN communication.
Thank you all in advance.
Hi all,
I'm getting software crashes when I try to set variables from certain Object Model items:
echo state.restorePoints[0].coords --> OK
var a = state.restorePoints[0].coords --> OK
echo state.restorePoints[1].coords --> OK
var b = state.restorePoints[1].coords --> KO
var c = state.restorePoints[1].toolNumber --> OK
it crashes also with var z = tools[1].extruders
.
It seems like the problem is when copying the array field from the second element of an array of objects.
I'm currently using RRF 3.5.2, but I tried also 3.5.0 and the issue is the same.
Can you replicate the issue or am I missing something?
Thanks