Skip to content

Comments

Servo to analog range optimization#102

Open
papadako wants to merge 12 commits intohjd1964:mainfrom
papadako:servo-toAnalogRange-optimization
Open

Servo to analog range optimization#102
papadako wants to merge 12 commits intohjd1964:mainfrom
papadako:servo-toAnalogRange-optimization

Conversation

@papadako
Copy link
Contributor

@papadako papadako commented Oct 30, 2025

Some optimizations for servo DCs I have been working on and for which I would like some input

  • Faster path to toAnalogRange
  • Optional zero-crossing hysterisis to avoid pid chatter around 0
  • sigma delta PWM dithering for sub-count resolution
  • stiction kick when the motor is not moving or during direction change for a small window (helps in guiding)

papadako and others added 5 commits October 30, 2025 17:50
…d float math; should be a lot faster and maybe with less jitter

- Cache pwm min/max and gain and recompute only when pwmMinimum, pwmMaximum, velocityMax, or analogWriteRange change
- Replace double paths with single-precision ops (lround→lroundf, fabs→fabsf, use fmaf) -> double precision not crucial and not supported by FPUs
- Remove per-tick map() and % → counts work
- pwmUpdate fabs-> labs
- Generally, shorter and more consistent control-tick critical section. Might reduce preemption impact and free ISR headroom
Add SERVO_HYSTERESIS_ENABLE + SERVO_HYST_ENTER_CPS/SERVO_HYST_EXIT_CPS to prevent PWM chatter near 0 (preserve existing linear mapping)
…esolution

- Introduce SERVO_SIGMA_DELTA_DITHERING (residual, non-overflowing implementation)
- Dither float PWM counts to integer per tick so long-term average matches target for DC motors
- Improve effective torque resolution near sidereal
- Reduces bias and micro-jitter vs. trunc/round-only outputs
- Reset residual on zero-output/disable to avoid stale carryover
- Uses single-precision math
@papadako papadako force-pushed the servo-toAnalogRange-optimization branch from 314c2bb to ef76359 Compare October 30, 2025 18:10
… to pwmMin. Change it so that we map power to 0-pwmMax. If less than pwmMin then set it to pwmMin.
…ion breaking power can be reduced after the motor is moving
…or when changing direction. Implemented as a percentage over the min velocity

kick config: SERVO_STICTION_KICK_MS (related to mechanical and electrical time constants and inertia and the SERVO_STICTION_KICK_PERCENT_MULTIPLIER over the countsMin

- gate kick by tracking mode via ServoDriver::setTrackingMode(bool) (no-op in base, implemented in ServoDcDriver and called from ServoMotor::poll()).
- compute & cache countsBreakCached (SERVO_STICTION_KICK_PERCENT_MULTIPLIER) in recomputeScalingIfNeeded()
- Apply kick for SERVO_STICTION_KICK_MS on leave-zero or direction flip. After the SERVO_STICTION_KICK_MS time window then fall back to countsMinCached.
- Reset kick state on zero output and clamp to min/max counts.
@papadako
Copy link
Contributor Author

papadako commented Nov 9, 2025

Currently, I am thinking that the acceleration ramping during tracking is a bad idea. In the calibration code it was creating inconsistent results and I think it will be a good idea to remove it also from tracking / guiding. What do you think?

P.S. I have implemented it my local branches but unfortunately I can not test it right now due to clouds.

P.S.2 It leads to oscillations. We need some kind of ramping. I will experiment by increasing the acceleration value

@hjd1964
Copy link
Owner

hjd1964 commented Nov 16, 2025

I'll hang on to merge this until you do more testing. As for the "acceleration ramping during tracking" well this is an acceleration limiter and more about protecting motors/drive systems. At tracking rates it should be so darn fast (even instant as in one pass through the routine is enough to hit the rate change?) and its a setting that can be increased/decreased. I'm generally against that change.

@papadako
Copy link
Contributor Author

Yes it is better to wait a bit. No ramping leads to oscillations so it is not the recommended route.

I have so many changes that I am testing and I am still experiencing some mechanical issues.

Maybe you can checkout the fast path e82a345 and

the Sigma-delta dithering
e82a345

@hjd1964
Copy link
Owner

hjd1964 commented Dec 11, 2025

Just now much has changed in OnStepX which regrettably had to break some of this work. One of the big changes is a new Analog class the attempts to deal with global and per-pin resolution and frequency capabilities as well is handling the odd pin38 hack semi-transparently. This also adds deadband support (all Servo child classes) and for DC servo drivers deadtime (on direction change) as runtime parameters.

@papadako
Copy link
Contributor Author

I will try to check those out and adapt the code. Currently we have very bad skies and I did not have time to progress my work.

However, merging things after refactoring usually requires a lot of work. It would help me if we could evaluate/check proposals early.

@hjd1964
Copy link
Owner

hjd1964 commented Dec 24, 2025

The latest code has support for an inner velocity PI control loop with DC motors. The encoders have a blended velocity estimator (t/d and d/t.) Testing seemed to show it improves performance vs. operating without it. Disabling should be easy just set the vP and vI to zero or use it.

Example config:
`// using my "DcStick" and "ABStick", both of which are enabled with logic HIGH so the default LOW won't work
// the MaxESP4 has one enable pin connected to all four motor drivers
//#define SHARED_ENABLE_STATE HIGH

// using my "DcStick" and "ABStick", both of which are enabled with logic HIGH so the default LOW won't work
// the MaxPCB5 has per axis enable pins
#define AXIS1_ENABLE_STATE HIGH
#define AXIS3_ENABLE_STATE HIGH

#define ANALOG_WRITE_RANGE 4095 // 12 bit

// the MaxESP4 pinmap is designed to work with my "ABStick" module
// the module plugs into Axis3 or Axis4 and changes it from stepper driver outputs to encoder inputs
#define AXIS1_ENCODER AB

#define ENCODER_FILTER 200 // allow 200ns until next encoder edge detection to supress spurious signals
// See EncoderBase.h for more info, these are tuned a bit more aggressively that default
#define ENCODER_VELOCITY ON // enable the encoder subsystem blended velocity estimator and velocity control loop for DC motor servos
#define ENCODER_VEL_WINDOW_US 5000UL // default 10ms (10000UL)
#define ENCODER_VEL_STOP_US 100000UL // default 0.3s (300000UL) to detect a motionless state
#define ENCODER_VEL_BLEND_COUNTS 4.0F // default 4 counts (4.0F)

// the encoder direction must be correct, the motor direction can be changed at runtime to match if needed
#define AXIS1_ENCODER_REVERSE ON

#define PID_SAMPLE_TIME_US 2000 // normal sample time for the outside DualPID position loop is 10000us

// use 0 to immediately switch from the Tracking PID set (at 0% power) to the Slewing PID set when a slewing event occurs
// or use 10 (etc.) to scale smoothly from the Tracking PID set (at 0% power) to the Slewing PID set (at 10% power)
// for DC motors this smooth scaling method seems to work best
// for stepper motor based servos I just omit this setting (0 for immediate mode is the default)
#define AXIS1_PID_SENSITIVITY 20

// in arc-seconds to provide a little tolerance for the encoder not being EXACTLY on target
#define AXIS1_TARGET_TOLERANCE 60.0
`

@papadako
Copy link
Contributor Author

That is a nice Christmas present! Thank you Howard!

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