Understanding BLDC position control for position holding

Ok, this isn’t strictly simplefoc related, I’m trying to write my own BLDC position control code from scratch because I want to learn the underlying principles and only really find I get an understanding of this sort of stuff by implementing it rather than using an existing library or trying to read over existing code, and this seemed to be the only place I could find online with a lot of active discussions about this sort of thing… so …

How on earth does one hold position with a BLDC when doing FOC?

When one is moving, however slowly, one says “align the electrical fields in the stator to be 90 electrical degrees ahead of the measured rotor position”, this is the whole FOC principle and gives you maximum torque. But when one tries to hold a position there aren’t relevant lead and lag directions, which way the rotor deviates from the ideal location will depend on the torque loads applied on to the motor by the object being moved. This can constantly vary, especially when holding position against minimal deviating torques.

There are quite a few electrical angles involved:
1.The measured electrical angle of the rotor at a given instant
2.The electrical angle of the stator’s field at a given instant
3.The target electrical angle which the rotor is supposed to be held at
4.The angle which the stator’s field should be at so as to hold the rotor at the target angle, this will be 90 degrees “ahead” of the target angle of the rotor, but “ahead” can constantly switch back and forth at low torques

Constantly switching the stator field angle back and forth as a way of holding position is surely not good, particularly if the position control loop is slowed at all in which case the motor gets time to deviate by quite a bit before being sent back the other way and physically measurable, and very audible, vibration can result.

The trick must be to find a way to precisely balance the amount of current flowing in the motor’s stator, together with the eletrical angle of the field this current generates, so as to hold position against whatever torques are being exerted on the motor by the load it is holding. But whenever the motor’s measured rotor position crosses the target rotor position the electrical angle has to jump by 180 electrical degrees from “ahead” to “behind” or vice-versa. Given this sharp jump involved I’m not sure if anything like a normal PID loop can help, because there isn’t a stable value for the angle to settle on, but rather two angles, each stable for torque loads in one drection but not the other, and both separated by a 180 electrical degree gap.

Setting the stator’s electrical angle to match the target angle for the rotor can’t work, as it would be incredibly wasteful of motor current, you’d get no restoring force at all when the stator field and rotor were aligned, and whatever restoring force you did get for a displacement in either direction would therefore never be able to bring the rotor quite to the proper position.

Can anyone point me to resources to explain the theory of what is going on in this situation? How am I to achieve a desired rotor position, and equal curves for the ramping up of restoring torque when displaced from it in either direction, when all I can control is the position of the stator’s field, to be placed 90 degrees ahead of or behind the rotor?

Thank you

Wow, that’s a long question :smiley:

But you can also control the magnitude of the current vector…
So in effect you can control the amount of current (q axis current), which corresponds to the amplitude of the commutation waveform.

There’s no point in applying D-axis current as it will only generate heat and not torque.

You can’t generate torque “at a position” only to “move to” the position. If you think about the fact that torque makes the motor turn then it becomes clear that holding the position means applying a force (torque) exactly opposite to any force rotating the motor. Any more or any less and the motor will rotate. In absence of any external forces, holding the position means applying zero torque.

We offer position control, and by default using only proportional gain, so in many cases this is capable of “holding” a position - I guess how well it works will depend on the application, and your PID tuning skills :wink:

Won’t that still end up with a situation where, when holding the position, whilst close to the position, every time the sign of the error between the target rotor angle and the rotor’s actual angle changes, you’ll have to flip the angle of the stator’s field by 180 degrees across the target angle? Even with a proportional effect of minimised current flowing when the error is small, you’d still be constantly sending the angle of the stator’s field back and forth between locations at +90 and -90 from the target rotor angle?

EDIT: I’ve tried it, it does. Even as I shrink the magnitude of the stator current when the rotor gets close to the target angle, and set it to be zero when within a small margin around the target angle, I end up generating oscillations. I wonder if cogging torque or some other non ideal effect might be at work here, making it such that there are still “motor” forces acting on the rotor even when the stator current is zeroed? Are there any more “position control” or “speed control” rather than “torque control” type methods to hold a position, because I’m starting to fear that actually generating a torque which is perfectly as expected might be harder than anticipated, and I’m suspecting this could be why oscillations develop when trying to hold a position by commanding a torque?

If I could work out a way to limit speed whilst doing “torque control” type motions that might offer something too, but as it is I’ve been using a more “speed control”/“position control” type method* for moving the motor across larger angles, it is when I try to stop and hold that I get these difficulties.

*
in long distance motions (many electrical angles worth) I control position by setting the stator field angle as I wish it, I can control speed by changing the rate at which I add to or decrement from the angle value (which is used to update the exact PWM duty cycles on the 3 phases during each loop of the controller code), I then implement “foc”-like torque efficiency by having logic which reduces the magnitude of the stator current if the lag between the rotor and stator is less than about 80 elec degrees, and increases the current magnitude at any time when the lag gets greater than 90 degrees. This all works for long distance motions, the trouble is how to then hold position once stopped, is such a goal entirely incompatible with this mode of control? It’s just that in using instead some torque control logic** when close to the target position, I end up with those oscillations, unless the motor is holding position against a large torque, in which case it can stop in a stable manner so long as that torque doesn’t then get released.

**
in pseudocode:
if(abs(targetVsRotorError)<threshold){
//zero the current
}else if(targetVsRotorError>0){
//current in proportion to absolute magnitude size of error
//set stator field to rotor position + 85 degrees
}else{//targetVsRotorError<0
//current in proportion to absolute magnitude size of error
//set stator field to rotor position - 85 degrees
}

I think the main problem is that you forgot to take into account the rotor inertia, which is why you are getting the oscillations.

See this previous discussion: Angle control without nested velocity PID? - #4 by Andrew

Ok, I just tried some further testing of “torque mode” style control, where I was having the stator field angle always placed 90 electrical degrees ahead of the rotor’s measured angle, then varying the magnitude of the current to control a mixture of speed and torque. I’m amazed by how much more efficient, in terms of g*cm torque per A of total motor current, this method is when compared to “position control” where you vary the current to try to keep the rotor 90 degrees behind the position you are setting.

The “torque mode” method of control doesn’t map quite so clearly to obviously separate quantities though, in the way that independently setting a current level and a speed of commutation does. So it’ll take quite a bit more thinking about how to gain full control over the motor when running it this way.

I’m guessing I have to set a maximum current, corresponding to the maximum torque desired, then monitor the actual speed of the rotor and have a control loop varying the provided torque so that the desired speed is reached for any given level of load being moved? It also looks like it might be tricky to have slow speed torque control when there’s not much load present, removing the torque load during experimentation lead to the motor reaching very high speeds even on fairly low current settings? My half bridges have a minimum time they can turn on for, below which they just stay off and provide no current at all, so I don’t know whether this would make slow speeds with light loads when under torque control impossible?

Looks like I’ll need to rewrite all the long distance motions parts of the code to use torque control instead, given just how much more torque per amp you actually get this way, I think the main trick will be working out how to keep the speed within minimum and maximum limits for any given motion. After which the question of how to hold position, atleast when the load is small (at large loads the torque control method looks to balance pretty well against them), is still likely to be present.

I think one of the extra complexities will be how to measure torque when in torque control mode? When controlling via speed, one can look at the angle between the rotor and stator, and judging by whether it is leading or lagging, as compared to its expected position, get a sense of which way a load torque is acting upon the motor. Once one is always setting the stator’s fiedl to be 90 degrees ahead of the rotor, this can no longer work. Is there therefore a common way in which a brushless motor in torque control mode can estimate the torque being applied, or atleast which direction that torque is applied in? Thanks

The torque is proportional to the current in the Q axis when using FOC.

That’s the torque being generated by the motor, the torque the motor is experiencing from external loadsthough, and the sum of the external torque plus the motor’s own torque can’t be measured as a current can it?

It is quite difficult to determine the torque from external loads accurately. If you really want to do this, you needs a highly accurate model of the motor. The external torque can be calculated with physics formulas using the motor acceleration and inertia from the motor model, the applied torque (current), and the internal friction of the motor from the motor model.

Yes, this is physically necessary as you have to turn the motor either one way or the other, and this requires opposite forces. It’s the same on any motor, I think. On a DC motor you’d have to completely reverse the polarity, on a BLDC you have to change the direction of the Q axis current.

I don’t think its a problem per se, unless of course you’re making these swings at max current, which will stress your system electrically.

You would limit the speed by limiting the amount of current, i.e. the amount of Q axis current.

Why not keep the current vector at exactly 90° electrical?

Setting aside advanced topics like field weakening and MTPA, in principle you are setting a current vector in the D-Q space. Any current you send in the Q direction causes torque. Any current you set in the D direction is simply wasted (and causes heating).
So why set any D current at all? Of course you can use the current vector angle to “control” the torque - by changing the angle away from 90° you get less torque. But if you think about why, it is simply because you’re making the projected length of the vector onto the Q axis shorter in length. So by keeping the vector at 90° and just making it shorter you get the same reduction in torque without wasting power on the D axis.

The 85 should be 90°, probably, but whatever. Aside from this, if this pseudocode is executed at a constant rate it would be similar to a PID with P gain only, and a threshold.
So it is like a non-cascaded position control with only P gain.

You could hold a position using a velocity controller and commanding 0 velocity. The problem with this approach is that it would “drift” over time, and you’d have to add some additional control logic to correct for the drift. At that point you’d probably wind up more or less with a cascaded position controller, which is what SimpleFOC does in position mode. SimpleFOC by default uses a PI controller for velocity and a P controller for position.

But as you have noted, this setup will tend to oscillate around the target position, or it’s hard to tune just this control layer to execute “long motions”.
This is because this is just a basic motor control layer, it has no application-specific knowledge and isn’t suitable for solving complicated problems around motion trajectories.

Or as Andrew pointed out, you aren’t taking inertia into account. So when the controller tries to suddenly stop the motor at the target position, it can’t stop it quickly enough, and you get overshoot - correcting the overshoot, the same thing happens, etc… - oscillation.

I think you will have more success if you add further control layers specific to your application on top of the normal motor control, rather than combining it all. For example, take a look at the trapezoidal motion code in our drivers library: Arduino-FOC-drivers/src/utilities/trapezoids at master · simplefoc/Arduino-FOC-drivers · GitHub
It adds a super simple layer to limit accelleration and deceleration to chosen values, and builds on top of the standard velocity controller. This would already be enough, I assume, to much more reliably “hit” your target position.
And if you search for trapezoidal motion planner in this forum, you’ll find more posts and more advanced code examples.

Thank you for a most detailed reply.

Using torque control I’ve been able to get something an awful lot closer to holding a position. Now, in most circumstances it holds a position, including when any amount of load up to the maximum torque that the motor can stand against, closely enough to be visually indistinguishable from ideal position holding.

However when you put a finger against the motor, or simply listen to it, it is vibrating like crazy in its effort to maintain the position. The good news is these vibrations are no longer using much current, despite them the current draw when holding position is what one would expect (from T( Nm) = 8.3*I(A) / Kv_rating(rpm/V) )for the torque the motor is holding against at that moment, and sometimes its a little better than that too. With the current during the vibrations being well within the specifications of the half-bridges, electrically I should guess everything is alright, however I can’t imagine these vibrations can be good for it mechanically. They give off a really distinct audible groan, it can’t be good for whatever load the motor is going to be powering (shaking back and forth, however minutely, surely wears on gearboxes faster…) and it can’t be good for things like multi-layered ceramic caps when on circuit boards mounted to the same piece of printed plastic as the motor is screwed to.

I’ve monitored my controller software and found that, when the vibrations related to holding a position are particularly bad there is a slight, but detectable, ripple in the “amount of current to use” parameter (I called it beta) which controls the scaling factor of the PWM signals by the following pseudocode:

PWMDutyCylePinA=beta*SVMSineWaveFunction(angle);
PWMDutyCylePinB=beta*SVMSineWaveFunction(angle+120);
PWMDutyCylePinC=beta*SVMSineWaveFunction(angle+240);

where the SVMSineWaveFunction gives an output of 0 to 256 according to the modified SVM sine wave shape with the third harmonics added to boost the differences between the three phases.

The angle part of the commands doesn’t seem to be rippling at all when holding a position using a torque-proportional-to-deviation method, but the scaling factor “beta” does.

Due to the way I designed my circuit, I thought I’d have an easier time with pre-made IFX007T half-bridge chips than with building my own MOSFET driving circuits, I can’t run at PWM frequencies higher than 7.8KHz (the next PWM freq up for a 16MHz AVR is 15.6KHz which is above the 10KHz limit than infineon warn of for the IFX007). And because I’m driving a low resistance motor, even at the absolute maximum torque level I only turn the PWM on for a maximum duty cycle of 10%, “beta” never gets above 0.1. This works to move the motor, and at these currents and this PWM frequency the half-bridges stay comfortably cool, but I suspect it means the resolution of the PWM levels I can provide to the motor is quite poor. When your PWM level is 0 to 256, and you never use torques greater than 26, and much of the time use torque levels well below 26, you don’t have too many levels available. They become more like discrete than continuous. I am wondering if this means that my beta ripple is because the software would love to hold a beta level somewhere between two available duty cycle regimes, but it can’t as no such intermediate level exists as an integer amount. Hence perhaps, beta falls, the motor drops away from where it should be and succumbs, by a tiny amount, to the load torque. Then once the motor has deviated far enough from ideal, beta is raised to the next level available, which is high enough that it perhaps over-corrects. beta is then dropped again and the cycle repeats. The numbers within the code which cumulatively modify beta are floating point, but the duty cycles for the pins are integers, so the change in the PWM duty cycle would be a “blocky” step even when beta’s value in code is rising and falling more smoothly. I suspect these “blocky” waveforms might account for why the noise is such a groan like sound, rather than a shrill whine or a pure tone of one frequency.

Might moving from a proportional control to a full PID help this, or could it be the case that with discrete levels of torque available even a PID might not be able to minimise this vibration-inducing ripple?

I have already tried low pass filtering the beta value, but that only makes the vibration worse, I guess it ends up acting like the controller code is running at a lower frequency of control loop cycles per second, and therefore the overshoots when trying to hold position get worse as they can continue for longer before the revised value for beta is available to correct for them. Clearly what I would need a PID loop to be able to do will have to be quite unlike a simple low pass filtering operation.

P.S. a system model for a particular application, knowledge of inertia… really isn’t much of an option for me. I’m trying to operate a small brushless motor, and the design of the circuitry and software which controls it, to serve as a pretty generic “servo” for a wide variety of future projects I might try with it. I’m really trying therefore to only build in “knowledge” to the software I’m writing which will always be applicable for this motor and circuit. So the pole count will never change, the method of angular position tracking will stay the same, the maximum PWM frequency my half-bridges can take won’t change… but anything related to the nature of the load being moved, and whether it is directly on the motor’s shaft or after a reduction gearbox of various different designs, is all potentially variable. Most of the testing so far I’ve been doing with a pulley of fixed radius mounted on the shaft, lifting and lowering various different weights in both directions. On account of this generic-ness in design I’m not so concerned about initial overshoots when settling to a position immediately after a long move, tackling those would need full knowledge about a specific application. It’s the vibration which continues once at the position which I hope I can damp out.

P.P.S. I have changed from 85 degree lead to 90 degrees, not much change to anything. The reason I had used 85 beforehand was in the thought that it would give a bit of “headroom”, so if the torque from a load suddenly rose a little then there would still be those 5 degrees before it crossed past the 90 degree optimum for torque, after which point torque decrease as the angle between the stator and rotor grows.

Thank you