Kinematics: Interpolation with Non-Linear Motor Moves
-
@xyzdims your analysis is interesting and imho correct. But there is something strange additionally, because even if only calculating the distance for x and y, as Y moves with B20, the value for moveLength should be > 0. currentUserPosition[Y_AXIS] is probably not updated.
float moveLengthSquared = fsquare(currentUserPosition[X_AXIS] - initialUserPosition[X_AXIS]) + fsquare(currentUserPosition[Y_AXIS] - initialUserPosition[Y_AXIS]); should be > 0.
Update:
Probably the reason is
totalDistance = NormaliseLinearMotion(reprap.GetPlatform().GetLinearAxes());
in DDA. When there are no linear axes by AxesBitmap::MakeFromRaw(0), totalDistance is 0. (I was not aware that this method is so important...) And totalDistance is used to calculate the current position, speed etc.It's interesting that in the case of G1 B20, totalDistance is correctly 0, but for other reasons. B, Y and Z movements nullify themselfes, there is no movement of the nozzle. But this is not due to the code. In DDA, linear and rotational axes totalDistance are calculated separately as if-else.
-
@xyzdims I see what is happening, it is calculating segmentation based on XYZ movement only. That isn't sufficient for your kinematics when you do a move involving the rotary axes but little or no XYZ movement.
-
@joergs5 Interesting observation of yours - it really helps me to understand RRF better, thanks for that. I need to ponder on this all some more as I can't see what the proper solution is.
-
@xyzdims I return the compliment: I have never dived that deeply into the topic of segmentation before. A classical win-win
There is a saying: the truth is in the code. That's why I increasingly read the code additionally to the documentation.
-
It's interesting that in the case of G1 B20, totalDistance is correctly 0, but for other reasons. B, Y and Z movements nullify themselfes
I thought about my sentence above. The DDA code calculated totalDistance to be > 0, which will result in extrusion. But in reality, totalDistance is 0 and there should be no extrusion, otherwise you get an additional clump. The reason for > 0 is, rotationalAxis and linearAxis are handled separately in DDA, but imho the totalDistance should be calculated for all "moving/rotating, non extrusion" axes. But firmware needs to know all angles of the axes (linear and rotational) to calculate it, eg settings in config by G-Code. An alternative is to introduce totalDistance to be calculated in a kinematics specific method, this can be used to resolve the LimitSpeedAndAccel method also.
-
-
@dc42 I'll recheck, thank you for your hint.
-
@dc42 yes, saw it; I didn't want to impose my haste solution (better would be loop over numVisibleAxes to calculate moveLength), and your response kind of indicated that you ponder on a proper solution. I keep my haste patch for now until you come up with something proper.
Update: there might be a requirement to transform degrees to moveLength somehow (a multiplier would be sufficient - maybe not on a second thought) provided from underlying kinematics using rotary axes; but I'm not fluent enough in C++ to do this properly.
-
@xyzdims I am thinking that as well as a parameter in Kinematics for segmenting linear movement, we need a parameter for segmenting rotary movement and specifying minimum segment length in degrees. Or perhaps move the code that calculates segmentation into the Kinematics class so that it can be overridden. I will think about this when I have time. Feel free to remind me a week from now.
-
@dc42 I thought about it the past days, I definitely like to hear @JoergS5 thoughts as well, as his kinematics has more rotary axes than mine.
So, for my case there are just two rotary axes, Z rotation (A) and tilt (B), which means, that depending on angle of B, the segmentation of A is determined:
- 0° B angle => A rotation segments = 1
- 90° B angle => A rotation segments = max
- 180° B angle => A rotation segments = 1
which leads me to something like
=> A rotation segmentation = ( sin(B angle) * A angle-delta * A seg-factor ) + 1(This formula disregards when B is also rotating together with A; I likely have to calculate it twice or more with B angle-start and B angle-end)
Segmenting for B rotation is not depending on other axes, therefore:
=> B rotation segmentation = B angle-delta * B seg-factorIn order to determine the segmentation of a single rotary axis properly, I need to know the other positions and angles (start & end) too, so those need to be passed on or made accessible in the method/function.
In a way, the actual movement length to calculate amount of segments I cannot determine from machine position delta, but from motor position delta, that is consulting
CartesianToMotorSteps()
. As I understand the current state of code ofGcodes.cpp: GCodes::DoStraightMove()
these are machine positions.So conceptually, any actual segmentation must be calculated from actual motor position delta, because at the end of the day, the motors move, and the segmentation is a way to optimize those. The machine position is conceptually higher and more abstract and there I can't really know the kinematics and how much the individual motors really move - in linear context machine pos ~ motor pos, but in non-linear context with more complex inverse kinematics, this is not the case anymore.
@dc42 It took me a bit longer to get back to this, I'm also not sure of the best way to proceed.
To sum up [my current state of conclusion]: the segmentation of (motor-) movements needs motor positions (not machine positions), and the Kinematics class can provide that, and optimize certain cases (as I pointed out near the beginning of this reply).
-
Adding to my own reply, the hidden issue I struggle with is, machine pos are real absolute positions [e.g. mm] of the tool tip, where as motor pos are arbitrary units again (microsteps etc), yet, segmenting is optimizing ridiculous short movements [in mm], yet, motor pos gives me mechanical lower bound, but not information about absolute movement as of [mm] - so, what does segmenting really do?
Does it define shortest possible movement motor-wise, or defined highest resolution of the motion of the tool tip?
-
@xyzdims said in Kinematics: Interpolation with Non-Linear Motor Moves:
what does segmenting really do
I currently cannot read your other questions, but about segmentation, I know that movements are segmented into short straight segments. Analogous like a circle can be approched by many short straight lines to calculate pi. You have control how many segments to use. The more, the better approaching the curve, but more processing needed and possibly jerks between the segments. (RRF tries to smooth speed between the segments, but I don't know how good). Segmentation is not only needed to approach the line, but the angular speed changes with position, so extrapolating the angle speed withouth segmentation would leed to an incorrect result (straight line being printed as curve).
-
@xyzdims said in Kinematics: Interpolation with Non-Linear Motor Moves:
I need to know the other positions and angles (start & end) too,
If you really need it (and is not supported by firmware), a trick could help you, which I used: when a new move starts, the method LimitPosition is called once. I store the planned path in this method, because the method gets initialCoords and finalCoords. But G2/G3 is empty for initialCoords (you can take the finalCoords of the move before). I needed to declare the path variables as mutable.
BTW this trick is not good design, but I wanted to avoid changing something in the main code, and wanted to use code only in the kinematics class.
-
@dc42 I think I have a conceptual solution for my dilemma:
RRF right now (as of 3.4) as far I understood:
- machine position [mm]
- motor position [microsteps]
but I need to think, as for 5-axis PAX kinematics, in 3 layers:
- machine position [mm] - that is what G-code states
- motor position [mm] - that what motors have to do in [mm] after inverse kinematics calculated
- motor steps [microsteps] - that's the actual steps to perform
Right now I do 2 & 3 in
CartesianToMotorSteps()
, which isn't ideal, and the trick withLimitPosition()
as @JoergS5 mentioned to get start & end machine position helps a bit.I think in the long term RRF might introduce the layer 2), and make a hard distinction of layer 1, 2 and 3.
The segmentation count and length in my opinion must be calculated from 2), and consider 3) as lower-bound. If segmentation is only calculated at 1) level, then the other involved motor moves as part of inverse kinematics cannot be determined or
CartesianToMotorSteps()
needs to be called (where inverse kinematics is calculated) but with motor steps given back that's not sufficient details or is it?In a nutshell, machine movements do not reflect actual motor movements, no assumption can be made outside of Kinematics which motor actually have to move and how much (!!) - therefore such layer 2) might be worth to introduce in the long term, e.g.
Kinematics::CalculateMotorPosition()
.From top-down:
Gcodes::DoStraightMove()
: Gcode positions in [mm]Kinematics::LimitPosition()
Kinematics::CalculateMotorPosition()
[new] transforming "tool or machine position" to "motor position" yet still in [mm], e.g. applying inverse kinematics, if not implemented, machine position [mm] => motor position [mm]Kinematics::MotorPositionToMotorSteps()
(formerly known asCartesianToMotorSteps()
), calculating [mm] into [steps], if not implemented simply domotorPos[axis] * stepsPerMm[axis]
DDA::*()
: deals with actual steps / time
or in short:
MachinePosition
[mm] ->MotorPosition
[mm] ->MotorSteps
[steps]Anyway, these are my thoughts based on my limited comprehension of RRF.