Low speed interpolation and servo anti-hunting

Ok, I've got two related problems to discuss here and think the solution to both probably lies in using the correct piece of maths to smooth out the readings I'm taking, but it's not as simple as I'd hoped it would be:

  1. Low speed interpolation. So I've been building my own servo system, my "encoder" is of such a resolution that it gives one "tick" per degree of the shaft. It is an absolute encoder with analogue functionality and no ability to give timed sharp edges, I cannot read the time at which a change of the position occurs, I can only read what the position is during each iteration of my control loop. The physical sensitivity of the system means that below 1 "degree" I can't hope for any more accuracy. And this is fine at higher speeds, but I want to be able to run at really low speeds too, that is to say speeds where you get less than 1 "degree" (1 tick) of shaft angle change per time-taken-for-one-loop-of-the-control-loop.
    The current I provide to (hence torque developed in) the motor is increased or decreased during each loop of the control loop by an amount proportional to the difference between the speed I'm achieving and the speed I want to achieve, I raise current to speed up, I lower it to slow down.

I tried the obvious answer, low pass filtering of the incoming angle reading, but this just results in jumpy behaviour where my controller responds with bursts of speed followed by bursts of slow. A low pass filter should smooth things, instead it makes them jerkier.

I also tried comparing a stored value of the measured angle from 4 or 8 control loops ago, rather than just comparing the angle measured in this run of the loop to the angle measured during the last run. The same issues as with the low pass filtering occured.

Slowing down the control loop feels wrong in principle, but I tried including delays which could be extended when needed at low speeds, and yet a slowed control loop also ends up with slow motions becoming jerky.

How am I to interploate speed when I'm wanting to run slower than 1 measurable tick per minimum measurable time period (the time for one loop of the control loop)?

3.Anti-hunting for servo position holding. When I stop the servo at a position it is required to hold, it ends up constantly jiggling back and forth with a vibration. This isn;t a big motion, it is barely visible, but it can be very clearly heard and seen. The motor is trying every so often to give an extra burst of power, then relaxing itself. I've tried playing with PI loops during position holding

I understand this is a tricky problem overall, it is unique to my overall system I guess, although likely occurs in a similar form in other servo systems. No invididual part of the system, the motor alone, the encoder alone, the source code alone... can demonstrate this issue, it only occurs when the whole thing is (not-quite) working together. But I'd appreciate any advice on how to interpolate the speed measurements so I can make really slow moves smoothly, and suspect the same technique should let me damp out the hunting of the servo during position holding.

Thanks

One way of downscaling/interpolating is to model the system and calculate the expected values.

In a pulse train, it's frequency multiplying with a phase locked loop. It might help to look at the theory implemented here (and the associated pages):

I'm not sure you should adjust your control loop on the unmeasured/interpolated/extrapolated points, but you could act on the control loop output on the intervening pulses. If you are driving a motor running at 1 degree/sec and need a 1000Hz PWM somewhere between 10/255 and 11/255 duty cycle to maintain speed, you could swap between 10 and 11 ten times per second to get a 10.x/255 duty cycle, (or 15& 5 or 20&0 and 5/255 duty cycle to up the torque a bit).

Others will comment but your servo "hunting" is normally caused by either inertia on the motor overshooting the desired position and/or supply dip as the motor cuts in and out altering the feedback actual voltage.
What I think the manufacturers do is to slow the motor as it approaches the set position.
Most analog servos have a small amount of hysteresis or deadzone whereas digital can eliminate it completely.

Post a link to the encoder's datasheet or brand name and exact part number.
Post your code.

JCA34F:
The encoder is built from AH49H analog linear hall sensors ( https://www.diodes.com/assets/Datasheets/AH49H.pdf ), two of them positioned to be in quadrature compared to the distance between alternating poles on the moving magnets. It detects as magnets mounted on the shaft sweep past it. The strength of those magnets and the distance (just under 1mm) at which the sensor is mounted from then determine the strength of its sine waves. Given the resolution of the atmega328p's adc and the general noise in the environment and how it affects a static analog signal I'm getting a minimum detectable angular difference of 1 degree.

My code is quite a complex lot, with some functions spread over h files, and won't be of much use except to someone who has the same physical system (motor, encoder and all) on the desk beside them. I could post the control loop section of the code though?

DaveX:
Thanks for the links, but those look to be all about PWM level generation. I'm looking for something to interpolate the sensor readings I'm getting, rather than try to generate an intermediate PWM level. If there's some clever trick I should have recognised via which to convert a PWM generation alogirhtm in to a reading interpolator, please enlighten me. Also, my motor is brushless. I control it by setting a different PWM level on each of its 3 phases. As I run it at fairly low power the resolution of different levels of torque I can provide for it might be a bit blocky and discrete, but if I could just get some sort of interpolation on the incoming angle measurements I'm sure that could let me get around any PWM level granularity.

Interpolating noise won't get you useful information. If the hunting is due to the noise, you will get the behavior as the system follows the noise.

Ok, but interpolating might still get me a solution to the slow speed motion problem.

You need to smooth it so you aren't acting on the noise. If, for example, 2° information is repeatable, then you might be able to interpolate/extrapolate meanigfully from there.

What sort of smoothing would you suggest, it's just that each smothing method I've tried so far (low pass filter, comparing to the angle several control loops back rather than the angle in the immediately previous iteration of the loop...) makes things jerkier due to the extra time lag introduced. Is there a clever one specifically designed to deal with cases where you're moving by the minimal detectable distance during each time step? Thanks

You are writing about mutliple sensors
How many sensors build your encoder?

Well if you would post pictures how your encoder looks like
alternative suggestions could be made.
Estimating from the size of the hall-Sensor

I guess that your encoder could be replaced by a AS5600-chip

This is a sensor with 12bit resolution
which means the angular resolution is
360°/(2^12) = 0.08789 degrees per bit

Really professional encoder-systems use encoders with 20000 to 100000 pulses per rotation.

Again: Depending on how your real systems looks like different suggestions how to achieve the goal of slow rpm could be made. But without seeing the real system this would be guessing in the fog suggesting a lot of things that do not work because they don't fit into your system.

So please post pictures of it.

Additionally describe the final purpose of the slow rotations.
Rotating whatever is usually no self-purpose. (except maybe art)
So what is the final purpose of rotating whatever?

With out any code, or circuit or data about your system, I really can't give much advice except for generalities you've written text about. but if the low bit is noise, you could discard it, divide by 2, or run an EWMA with alpha=0.5 and get a smoother signal with slower response than a faster, noisier signal.

The link:

had these recommendations:

How to get very low RPM from a permanent magnet DC motor

1. Use a geared motor. OK, this one is cheating. :slight_smile: More gearing means the motor can be run above it's stall speed and still get the final shaft turning at very low RPM. Fortunately most DC motors with encoders you can buy from hobby suppliers also have gearboxes on the motor.

2. Add more constant load to the motor. Adding load means more PWM is applied making the torque more stable, and likewise the load will dampen tendencies for the rotation to jump around. Even driving a gearbox can be enough load to damp the motor and give smooth rotation. (This is the first thing to check if your motor is jumping around, just let a fingertip drag lightly on the spinning shaft and it will probably stabilise.)

3. Use a low-RPM motor. Some motors run better at low speeds than others! Some testing can be very helpful. Generally 12v and 24v motors can be run slower than 3v and 6v motors. Larger motors (especially larger diameters) usually are more stable at lower RPMs.

4. Use a finer encoder with more pulses per rev. An encoder with more slots runs the closed loop system more times per rev so will be more stable at lower RPM. With less slots you will need to run the motor faster, or tolerate some jumpiness in the rotation (average speed will still be perfect).

5. Add flywheel inertia to the motor. A flywheel or weighted disc directly on the motor shaft can help make the motor rotation more stable.

6. Reduce the PWM frequency. Lower frequency PWM will reduce the stall speed as the motor will tend to move in tiny pulses. My software uses PWM at 3125 Hz but commercial traction drivers using DC motors can run as low as a couple hundred Hz.

7. Tune the gain in my software. The proportional gain (how many encoder pulses lag does it take to go from 0-100% PWM) can be adjusted in software. I found a gain of about 10 steps worked well, so PWM would oscillate between 20,30,40% to maintain the correct speed, and that was enough torque ripple to avoid stall which would occur if the PWM was say at a constant 24%.

Roman Black's clever trick in the clock link is that with phase locked loop/bresenham algorithm you can sort of separate a low frequency measuring/control loop into a higher frequency blocky-output-control-loop.

I'm not sure what speed is 'slow' 60RPM? 1RPM? Or how fast you intend it to change speeds.

If you had a less blocky and discrete PWM, and could feed it a high resolution but constant PWM, for example say 15.67/255=6.15% duty cycle, might your system give a constant "low" steady state speed?

Maybe don't think of it so much as controlling "speed", but instead as controlling position, with a dynamically generated target position, where you want to control the phase-difference between the target position and the actual position. Every degree (or 2°? 5ms??) of rotation you get an new measurement of phase error to use for calculating a smoothed high precision xx.xxx% duty cycle signal to feed into your motor phases.

(Its all handwaving for a general question.)

if you want to update some measurement every Nth secondand you're at such a low speed that there are no events within the Nth second, shouldn't you update the measure based on the time between events.u
a 1 deg event every 125 msec is 1.333 rev/min

1 Like

The trouble is I don't see how to. The position is measured via reading two analog channels then processing with trig, so it can't cause an interrupt at a time when it moves by one "step" as is there is no sharp edge to route to a pin set up for a pin change ISR. How then can I get the time of the last position change when the only times at which I can measure position at all are once per control loop?

As for faking a really slow motion by doing a stepper like behaviour (move to one exact position, then the next position...) I'd like to, but for this to work one has to be able to reach a position to a precision smaller than the amount one wants to change it by. The trouble is I don't seem to be able to find a way to set up a PID, or any other sort of method, which I can find appropriate tunings for. So as it is I'm stuck with a situation where I can only have a pretty shallow proportional slope either side of the set point position. This shallow slope means the motor stops within about +/-40 electrical degrees of where it should and there's not enough proportional gain to give it the power to move closer to where it should be. Then making the prop gain higher or trying to add integral or derivative terms just gives oscillation (serious oscillation). Unless there's an obvious way to get the motor to stop really precisely (within say +/-5 electrical degrees)

these 2 statements seem inconsistent. Does the trig processing produce a "tic"?

i had assumed you used something (ISR) to measure the # of integral and/or fractional rotations and determined speed by computing periodically.

I thought the problem was that at low speeds there's not enough events to compute the speed. But if you do measure partial rotations (i.e. # degrees), then you should be able to compute speed

you need a clearly define measure of the thing being controlled. the time between PID processing events, the dT used for derivative and intergral updates, can be an argument to PID.

1 Like

I didn't express myself so well in the first post. There are no "ticks", what I had been meaning there was that I couldn't get a resolution any higher than a change of +/-1 in an integer variable for a position change of 1 degree (no, changing to a float wouldn't help, the resolution of the incoming sine/cos signals after adc'ing would still give an angle reading which when plotted on a graph had steps of about the same size as the integer version did, where in the ideal world you'd get a continuous slope). I was meaning that 1 degree was the resolution I had in angular measurements. As I can't interrupt when the angle changes, it is a pair of incoming analogue voltages, all I can do is check the angle during each loop of my controller code and then compare it to the angle measured during earlier loops of the code.

As far as the PID attempts were going, I was simply setting the dT as "1 cycle of the controller loop", so was multiplying/dividing the angle related figures by 1 for the integ and deriv terms.

In terms of the thing being controlled by the PID, that is the measured angle of the motor's rotor. Ofcourse I can't directly output an angle for the rotor to be at, all I can output is a command saying how much current gets supplied to the motor (in terms of a multiplier applied to all 3 PWM duty cycles) and whether to place this current vector 90 electrical degrees ahead of or 90 elec degrees behind the measured rotor position (by varying the duty cycles according to 3 sine waves 120 degrees apart before they get multipled by that current magnitude value). The magnitude of the current and whether it is to be 90deg ahead or 90deg behind can be encapsulated in a single signed number, sign representing ahead or behind, magnitude giving level of current to apply.

I have written a PID a few times before, there's a really good blog series on it at:
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/

and I was able to trial-and-error tune those PID loops before so they'd work ok. But with this one I've had an absolute hell of a time trying to set parameters.

I'm at the point of wondering whether my hardware is at fault, that is to say things like the minimum times for my "mosfet" half-bridges (I've used IFX007T integrated half-bridges rather than discrete mosfets) to switch state and the minimum ON-times for them might be having an effect which prevents me from generating very small but non-zero levels of current so as to generate relatively small torques needed in the final stages of getting the rotor to make the last slight shift in to the perfect position. As things are I either always end with the rotor not quite reaching the position, coming to a halt too early as the proportional part becomes weak before the difference between the measurement and the set point has been zeroed. Or I try to include integeral and derivative parts, or try making the prop term bigger. And then I always end up with frantic oscillation about the set point.

The thing that is really frustrating is that in an earlier version of my code, before I learnt the importance of always putting the stator field's current vector 90 degrees away from the rotor's measured position, it was really easy to get the rotor to move slowly, or stop at a chosen position. That code was hellishly inefficient in power terms, because I was pretty much keeping the rotor tracking right on the stator field, so you need a lot of current to get a given torque level as compared to needing much less current for the same torque when stator and rotor are 90 electrcical degrees apart. But with that code I was, by having the rotor almost exactly at the staor field angle, able to move as slowly as I liked, simply by slowing the rate at which I moved the stator field round by varying the commanded angle fed in to the 3 120 degree separated sine waves at a slow rate. And when the rotor was sent to a particular angle it didn't oscillate about it. Is there some way I can bring back those advantages of "blind" 3 phase sinusoidal driving whilst keeping all the advantages that 90degree ahead/behind FOC has given me?
Thanks

can you explain what your hardware is, what is being measured and how you measure "it"(?) ? not sure what "it" is

It really seems like you are attempting to control finer than the limit of what you can measure or adjust. What can you measure reliably? What can you output?

My hardware:

  • Custom designed IFX007T triple half-bridge board,
  • PWM supplied from an atmega328p's pins (3, 9, 10) to each half-bridge to send it LOW or HIGH,
  • 8bit PWM on pin 3 (OCR2...), 11bit PWM on pins 9 and 10 (OCR1...), scaled so they have the same 7.8KHz frequency although the pin9 and pin10 ones have improved resolution,
  • 2830 size 1300kV brushless outrunner 3 phase low resistance motor, so my PWM multiplier is always fairly low such that even at maximum torque it never gets any phase sent high for more than about 10% of a PWM cycle. During a PWM cycle a half-bridge goes high letting current rise according to the motor's inductance (measured at about 10uH between any two of the motor's legs), the the half-bridge goes low and the current slowly decays as it goes through ground and recirculates by buck converter principles. I can get something like 15 amps in the motor coils whilst drawing less than 1 amp from the 12V (wall wart, 3A limited) supply to the half bridges. I can get maximum semi-sustained (minutes at a time) torques of about 600g*cm, which matches fairly closely to the prediction from the 8.3*I/Kv_rating equation (output in Nm).
  • I've, since the first post here optimised my overall position/speed controller loop so I can run it at 2.5KHz, hence if desired I can make the PWM levels change as quickly as every 3 or so PWM cycles
  • I measure position with two analog hall sensors tracking a small disc attached to the motor with magnets embedded in it, they are 90 electric degrees apart giving quadrature outputs. I have to track this magentic disc rather than the motor's own magnets because the motor's back-iron amost completely stops the motor's own magnet's fields from leaking outside it.

gcjr: please point to which "it" in my earlier post you're unsure about and I'll clarify. As things stand I'm as unsure which "it" you mean as you are unsure what that "it" corresponds to. Thanks

DaveX: I can measure 1 electrical degree reliabily. When turning the rotor, while unpowered, really slowly by hand I get a consistently monotonic output of serial.println angle measurements at the 1 degree level. if it were unreliable at this precision I'd expect to see some cases of turning the rotor positively but getting little bursts where the angle decreases before rising again. The size of the analog signals (sorry, not at the desk with it right now so can't give exact number) from the linear hall sensors is such that, when accounting for the arduino's ADC resolution and the possibility of noise induced in to analogue wires by the nearby large current in the motor coils, I don't have much confidence in getting any more precise. Also, the atan2 function has a limit on its precision when converting a pair of input integers (from the ADC readings minus the hall sensors' default output level) to give an output.

I'm not so sure how to clarify the precision I can output at. I'd guess consideration of the PWM levels would be the major thing. I'm already having, at max torque, a max duty cycle of 10% or so for the 8bit PWm that means about 25 levels possible. For the 11bit PWM it means about 205 levels possible. And the thing which matters is the difference between the PWM channels, not the values of the PWM channels themselves. For torques below the max torque, I'd get less levels possible.

Thanks

So the 8-bit PWM gives you an output resolution of maybe 1/2048, since a couple of the coils run at 11 bit resolution, and the third could be just pwm11bit/8. And with the top end is 10%PWM or ~205/204, it sounds like there's only ~205 different levels, or ~0.5% adjustments of power out.

... Oh I see the edit..."And the thing which matters is the difference between the PWM channels,"

Could you run pinA at 10%, pinB at 5% and pinC at 5% Being able to vary between the channels should give you lots more output resolution.

How slow can you run? Can you feed it a constant 5%(2%? 1%? 0.5%) on all channels and run it at a constant speed(s)?

I can try that later on. Modified code to simply spin the rotor by providing X% power at 90 degrees ahaead of the rotor, see what speed it ends up spinning at. Ofcourse, the speed reached by such a system will be very variable depending on the torque loads the shaft is working against, but for test purposes I can definitely get that data, perhaps at a few torque loaded levels as well as when unloaded. My expectation is, certainly for unloaded, that the shaft could end up spinning pretty fast even when only the smallest percentage power level it will move at is provided, I'll find out though.