Skip to content

Conversation

@pvyleta
Copy link

@pvyleta pvyleta commented Jun 28, 2025

This PR implements constant torque algorithm which maintains |A|² + |B|² = constant throughout the microstep cycle for TMC2130 stepper drivers. The algorithm is inspired by ideas presented in Prusa3d forum, but reimplemented from scratch.

User Interface & Configuration

  • Runtime Algorithm Selection: EEPROM-based switching between Default and Constant Torque algorithms
  • Menu Integration: Added algorithm toggle in linearity correction menu
  • Unified Parameter Range: 0-200 maps to power factors 1.0-1.2 for both algorithms
  • Immediate Application: Changes take effect without requiring restart
  • Complete Translation Support: Added translations for all 13 supported languages; respects 13-character display limits

Algorithm Implementation

  • Phase 1 (0-127): Power-corrected sine curve with tcorr adjustment for midpoint matching (same as default)
  • Phase 2 (128-255): Constant torque constraint solving using mirror position values
  • Delta Limiting: Slope-based delta limiting ensures the curve can fit TMC2130 compression
  • Range Clamping: Final sanity-check amplitude clamping to valid TMC2130 range [0, 248]

Testing

  • Tested on MK3S+

Algorithm comparison

The Curve is pretty much identical to the original in the first half, jsut scaled to have correct midpoint value. It still helps with moving the miscrosteps positions almost identically as the original, however it maintains constant torque. Real world results on my steppers show, that the constant torque algorithm needs slightly higher correction factor to achieve the same results on prints, in my case with default value 80, the const torque algorith provided best results at around 100. The differences are so negligible though, that I failed to capture it on camera.

tmc2130_final_comparison_factor_1 2

@github-actions
Copy link

github-actions bot commented Jun 28, 2025

All values in bytes. Δ Delta to base

Target ΔFlash ΔSRAM Used Flash Used SRAM Free Flash Free SRAM
MK3S_MULTILANG 610 0 248764 5654 5188 2538
MK3_MULTILANG 612 0 248146 5663 5806 2529

@pvyleta pvyleta force-pushed the feature/tmc2130-constant-torque-algorithm branch from 4e1b433 to 73bcafa Compare June 28, 2025 18:57
@pvyleta pvyleta marked this pull request as ready for review June 28, 2025 19:37
…r performance

- Add tmc2130_calc_constant_torque_value() function implementing advanced constant torque algorithm
- Maintain |A|² + |B|² = constant throughout microstep cycle for consistent motor torque
- Use full range utilization (SIN0=0, AMP=248) with natural mirroring approach
- Add EEPROM settings for wave algorithm selection
- Include menu integration for runtime algorithm switching
- Add complete internationalization support for TMC2130 stepper configuration strings

References:
- Analog Devices AN-026 application note
@pvyleta pvyleta force-pushed the feature/tmc2130-constant-torque-algorithm branch from 73bcafa to 5251bfe Compare June 28, 2025 19:39
Copy link
Collaborator

@gudnimg gudnimg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening a PR! I left some comments with initial thoughts/feedback.

Can you explain a bit why one would choose the constant torque algorithm over the previous one? What is the problem it tackles/solves? Are there any downsides?

I unfortunately don't have a printer to test this on myself.

// Determine allowed delta range
// This ensures delta ranges match slope ranges for optimal compression:
// slope ∈ [0,1) → deltas ∈ [0,1], slope ∈ [1,2) → deltas ∈ [1,2], etc.
int8_t min_delta = (int8_t)floor(slope);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the floor function required? It treats positive and negative numbers differently.

Would it work for slope to be int8_t instead of float if we are truncating the value anyway? I don't see slope being used anywhere after this line.

// Phase 1 (positions 0-127): Power-corrected sine curve
// Calculate theoretical value using sine function with power factor
// correction and tcorr adjustment for midpoint matching
float sin_val = sin(M_PI * (float)i / 512.0f);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the compiler doesnt calculate M_PI / 512.f constant at build time.

If I'm right this will consume less memory and drop one division operation:

constexpr float sin_gain = M_PI / 512.f;
float sin_val = sin(sin_gain * i);

I may be wrong but I think I have seen this happen before.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is only calculated upon startup or changing of the value, I did not spend too much time optimizing, but I can certainly investigate further.

if (min_delta < -1) {
min_delta = -1;
} else if (min_delta > 2) {
min_delta = 2;
Copy link
Collaborator

@gudnimg gudnimg Jun 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes we can save some memory by using the constrain macro. https://docs.arduino.cc/language-reference/en/functions/math/constrain/

min_delta = constrain(min_delta, -1, 2);

It wouldnt save much but perhaps some.

#define EEPROM_UVLO_MIN_SEGMENT_TIME_US (EEPROM_UVLO_MIN_TRAVEL_FEEDRATE-4) //uint32_t
#define EEPROM_UVLO_MAX_JERK (EEPROM_UVLO_MIN_SEGMENT_TIME_US-4*4) // 4 x float
#define EEPROM_CHECK_FILAMENT (EEPROM_UVLO_MAX_JERK-1) // uint8_t
#define EEPROM_TMC2130_WAVE_ALGORITHM (EEPROM_CHECK_FILAMENT - 1) // uint8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new variable should also be documented in the large table near the top of this file. Between lines 100-400 :)

@gudnimg
Copy link
Collaborator

gudnimg commented Jun 29, 2025

There are some compiler warnings during the build, these need to be resolved.

Examples:

/home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/tmc2130.cpp: In function 'uint8_t tmc2130_calc_constant_torque_value(uint8_t, uint8_t, float, float, float&)':
/home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/tmc2130.cpp:972:22: warning: comparison is always false due to limited range of data type [-Wtype-limits]
  if (candidate_value < SIN0) {
      ~~~~~~~~~~~~~~~~^~~~~~

 /home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/tmc2130.cpp: In function 'void tmc2130_set_wave(uint8_t, uint8_t, uint8_t)':
/home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/tmc2130.cpp:994:14: warning: comparison is always false due to limited range of data type [-Wtype-limits]
  if (fac1000 < TMC2130_WAVE_FAC1000_MIN) fac1000 = 0;

/home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/ultralcd.cpp: In function 'void lcd_settings_linearity_correction_menu_save()':
/home/runner/work/Prusa-Firmware/Prusa-Firmware/Firmware/ultralcd.cpp:4549:36: warning: comparison is always false due to limited range of data type [-Wtype-limits]
  if (tmc2130_wave_fac[axis] < TMC2130_WAVE_FAC1000_MIN) {

See: https://github.com/prusa3d/Prusa-Firmware/actions/runs/15947546512/job/44997438159?pr=4871

@pvyleta
Copy link
Author

pvyleta commented Jun 29, 2025

@gudnimg Thanks for looking into the PR! I should have stated that first - this PR is in the realm of very, very subtle optimizations (as the whole linearity correction anyways).

The standard current wave is sinus and cosinus for the two respective phases of the stepper. That guarantees constant torque throughout the microstepping positions. However, when the Standard Prusa linearity correction curve is appliad, the torque on the stepper varies throughout microstep position (as the provided curves explain). Now - as long as the torque is sufficient, no issues are observed, but the torque is lower for high values of correction factor.

This PR adresses that, and instead provides a curve that very closely resembles the stock one, but calculates the values in the other half in a way that the constant torque is maintained throughout all microstep positions.

If there is interest by community for this fuctionality, I am more than happy to apply all the requested changes - personally I even believe this can substitute the default linearity compensation, and no EEPROM changes would be needed, but as. it is, it is easy for anyone to just test the behavior and then go back to stock.

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

Successfully merging this pull request may close these issues.

2 participants