TMC Adaptive Microstep Table

Driver has up to 256 microsteps, so up to 200 * 256 = 51200 of possible data points.
The Encoder has a resolution of 15 bit = 32768 positions.
(I decreased hysteresis to 0.003 degrees, so it can be noisy)

I modified the angle.py a little, to sample each microstep 0…1023,
Export to JSON & etc.
In the end, I sampled 4 full steps, as before.
Export dicts like:

#mscnt, angle data
0: [128, 128, 127]
1: [129, 129, 128]
...

Made some computations to calculate mean angle, and extract diffs in angle for diffs in mscnt.
And convert it to um for easy reading (in the end we want to compare distance :D, right?)

I calculate stddev for each mscnt and use max one.

# For 128 microsteps
Maximum Standard Deviation across all (in degrees): 0.01083
# For 64 microsteps
Maximum Standard Deviation across all (in degrees): 0.011075156035949418
# for reference 1.8 / 256 = 0.00703

There are some graphs,
Y axis is um, X one is MSCNT as before.




I don’t want to be too optimistic, but it looks better than I expected.


Maybe it is a wrong assumption, that the encoder has better linearity than the stepper motor, but it looks like so, and it is good that the output is matched to previous graphs.

Now, I think about how to automate table tuning.

I think a good approach here will be to use direct_mode and the XTARGET interface to drive motor current directly to specific micro steps.
Those should allow us to get theoretically “perfect” sine wave for a specific motor and current for specified micro steps.
And later interpolate, encode, and validate it.

At least, I think so.


I can’t get direct_mode working.


To my surprise, full steps are also not equidistant.
I expected that at least the angle between 0, 255, 512, and 1023 would be around 1.8 ± 1%.
But in fact, it is like this:

Full Step 0 Angle expected: 1.800, actual: 1.697
Full Step 1 Angle expected: 1.800, actual: 1.839
Full Step 2 Angle expected: 1.800, actual: 1.807
Full Step 3 Angle expected: 1.800, actual: 1.829
3 Likes

I made something, this is a snapshot of PoC.

It should allow anyone interested to play around with an angle sensor and TMC SPI driver.
There are 2 commands, one is to dump micro step data as json, and another converges micro steps to “ideal”, it even allows to save it to config (at least it will try to do so).

This is a sophisticated process, but now an automated one.
Basic, a little bit naive approach for now:

  1. Decode MSLUT
  2. Choice the widest full step (I assume there will be more available angle data points)
  3. Calculate ms angle
  4. Get start angle
  5. Iterate over each micro-step and compare it with the “ideal” angle.
  6. If it is behind → increase current, if it overreaching - decrease current.
  7. Check if target metrics are better (if not - abort)
  8. Fit a 4th-degree polynomial function to smooth out (4th because we have 4 segments or 3 points where we can change the table encoding) (I don’t know what I’m doing :smiley:)
  9. Normalize it, cause even after smoothing, it can stop fitting to the encoder. (buggy, in a perfect scenario, it should always return a valid, encodable function)
  10. Write MSLUT registers, repeat 0

After numerous iterations, it looks like it is “working” for 128 microstepping and 15-bit encoder:

  • The motor still rotates like a motor.
  • Different smaller or larger microstepping still works
  • It converges to something and by real-time metrics it looks like the right direction

It is still not perfect, there still can be bugs (Ex: I think it will break around the 0 position of the angle encoder :frowning: ).
It can work for 64 micro steps (I didn’t test it), for smaller ones, I think that calibration changes will return too weak a signal for function fit and will be ignored.

Trying to do it iteratively on different rotor positions is a good approach.
After the function gives up - move the motor by 4+ full steps and repeat.

This is what I got after the last “successful” iteration:
image
To my surprise, it still works, with this wild form, but can’t continue to converge because of mslut table encoder limitations or bad fitting function/normalization functions.

image
The ideal graph should show us a horizontal line,
Left - After calibration, it is far from the flat line, and from stddev’s point of view, it is worse than the initial one on the right.


There is a strange behavior, looks like the driver does not completely apply MSLUT, so after saving and reloading it behaves badly, and if I just remove modifications from the config file, simple FIRMWARE_RESTART is not enough to make it sane again.

2 Likes

I found a fix for the insane behavior of the TMC driver.
TLDR: During calibration, interpolation should be enabled. Because driver REALLY applies only when MSCNT==0, not when it is passed.

Long story:
I have no idea what happens underneath, from spi CUR_A/CUR_B are sane and right where they should be according to MSLUT.
But the driver can’t drive the motor correctly and this error accumulates and can only be fixed with a power reset. I will not theoretize here.


I slightly improved the code, added emergency stops & etc.
The most important are:

  • Apply changes only up or down, because they all change the behavior of others. Like high stable current on the right side of the sinus quarter compensates for to “fast” incline of the left side and vice versa.
  • Added some “genetic programming”, where I chose the best-generated MSLUT based on the lowest standard deviation.
  • Use raw angle sensor data without interpolated compensation.
  • Added code to guess/detect angle sensor resolution.
  • Average changes over 4 full steps, because change has a little different impact on different full steps.
  • Use angle sensor resolution as hysteresis.

We can’t directly map micro-steps to the angle sensor or match, so we can assume the motor position is aligned or in between angle sensor divisions of measurement, so ± 360 / (1 << N bits) should be enough in regards to actual microstepping which we try to calibrate

I unsuccessfully played with scipy, to make a better-fit function, but I was unsuccessful.
For now, this is already can be called positive change, I got a slightly better signal form in comparison to the default one.

Last graphs:
64 micro steps stddev 0.56us → 0.63us (slightly worse by the graph data, but calibration code thinks it is better).


32 micro steps stddev 0.83us → 0.73us, better by code and by graph data.

So, now I can safely say my brute-force methods are silly, but it is possible to make stepper behave better, I want to believe in making it almost linear.
Also, I removed screws from the belt pulley of the calibrated motor while calibration, for now, it helps to make calibration stable by reducing mechanical drag.


Code snapshot: GitHub - nefelim4ag/klipper at angle-tmc-calibration-20241202
I think the next step is to search for a better way to change and fit MSLUT.

From a real-world testing point, I think that for smoke testing it will be enough to compare shake tune vibration profile before and after angle calibration. If it detects differences, it is make sense to play with VFA tests.

1 Like

Some good news, it now does try to make microsteps equal and it looks less like brute force.
Snapshot: GitHub - nefelim4ag/klipper at angle-tmc-calibration-20241215

Some graphs, left before, right after 16ms:


After several runs, it is still probabilistic in some way.

32 microsteps:

So, for now, I can safely say - you can try to play with it - it should work.

Changes:

  • I incorporated linear interpolation here because in “an ideal” situation it is the simplest way to move points.
  • Fit is used as a fallback because it often produces results that are too conservative.

Now it works for 16/32 micro steps on my setup, for 64 it looks like my hysteresis of ± 1.2 of encoder resolution is too high.


It was interesting to look at motan and linear motion here, can I spot a difference in deviation?

./scripts/motan/motan_graph.py -g '[["deviation(angle(mt6826s),stepq(stepper_x))"],["derivative(stepq(stepper_x))"],["step_phase(tmc5160 stepper_x)"]]' ./linear-motion-4 -d 25

32ms custom mslut, interpolation is on (4)


32ms default mslut, interpolation is on (5)

For me, it looks very similar.

Let’s assume that according to old vibration profiles I had resonances on CoreXY at 75-120 mm/s
I’m not sure how to properly compare speeds here with the speed of the motor and axis, so 120 / 1.44 ~ 80 mm/s.
I use 20kHz (sample_period: 0.00005), so 360 / 200 / 32 = 6400 pulses for 40 mm/s.
12800 pulses for 80 mm/s.

Let’s look under a microscope 80 mm/s:



10 mm/s:


Zoom to 4 full steps (one phase revolution):

I feel biased here, so I will not comment on it.


If we look at the last graph, we can spot that the form of deviation is repeating, like 1 and 3 quarters have hills and 2 and 4 quarters are more or less flat.
These are full steps, maybe, they can be better if there is possibility to control sin/cos offset like in TMC 2240. But I do not have one to test.

1 Like