3-Hall-Sensor position estimation: algorithm?

I tried to create a position estimator based on the 3-hall-sensor signals.

My first try was to estimate the rotor angular velocity based on the hall-sensor changes. But this gives very poor results, because the hall-sensors in common rc-bldc-motors are very poorly aligned. There are variations in hall-sensor change intervalls up tp 80%! This leads to very unprecise measurements.

Then I read some papers and got the idea to first do a setup on each power-up of the esc: the motor is doing a slow rotation with a strong sinusoidal 3-phase signal recording the hall-sensor-changes for a full rotation.
Afterwards the esc always tracks the hall-sensor-changes, so it can determine the mechanical hall-position (e.g. a hall-section of 42 possible sections for a 7-PP motor).
With this technique, it can estimate the rotor velocity much better and I get smooth operation and very low rpm.
But incresing the rpm, the current increases very rapidly. In my understanding this must be because the electrical field is not rotating correctly (perhaps the angle between rotor and field isn’t 90° or the rotational velocity of the field isn’t correct).

Is there anybody with experience in this position estimation technique?

P.S.: my code is not based on SimpleFOC.

When doing the calibration, rotate both directions and average the sensor change angle, because the rotor always lags behind the field in open loop.

I think you will need velocity extrapolation in addition to the corrected sensor transition angles. Otherwise your 6 step waveform will be too deformed due to the varying sensor intervals. Knowing the true intervals will allow calculating an accurate velocity. It also helps to low-pass filter it (SimpleFOC uses a basic running average type filter https://github.com/simplefoc/Arduino-FOC/blob/master/src/common/lowpass_filter.cpp). A good value for the time constant is generally around 0.01 to 0.05.

With extrapolation, you’ll probably need to offset the electrical angle 30 degrees (pi/6 radians) backward from the direction of rotation, as I did with my SmoothingSensor https://github.com/simplefoc/Arduino-FOC-drivers/blob/master/src/encoders/smoothing/SmoothingSensor.cpp (the phase_correction value).

If the hall sensors are placed correctly for 6 step commutation, then the extrapolated angle is always too far ahead. If 6 step has the angle equal to 60 degrees for 5 milliseconds and then ticks to 120 degrees at the next sensor change, then extrapolation would have been gradually increasing from 60 to 120 degrees during that time, giving an average angle of 90 degrees. So offset back 30 and the average works out equal to the 6 step value.

1 Like

Thank you for your effort to explain the things in SimpleFOC. That guided me to my problem here. I had to change the phase of the electrical field to advance only 45° instead of 90°.

I do not fully understand why this is neccessary: in the calibration the hall-sector angles and(!) the electrical angles of hall-changes are recorded. In my understanding during calibration the electrical field should be aligned with the rotor, because it is very slow rotating and a high-phase current is used, so that the effect of BEMF which would induce a phase difference should be neglectable. So, there should be a 90° phase-advance for maximum torque. But 45° gives the best results for all velocities. That I do not understand.

Indeed confusing. The more I think about it, that calibration procedure shouldn’t need the 30 degree correction. When the hall state changes, the true angle is known via the lookup table, and extrapolation from there will continue to track the true angle until the next sensor change. Driving at 90 degrees from there should produce maximum torque. 6 step commutation is what would need the 30 degree correction, but you can ignore that since it wouldn’t work well anyway due to the varying sensor intervals.

If anything I would expect greater than 90 degree advance to work better at high speed, to compensate for current lag due to inductance. 45 should be way too low.

Well, after some further investigation I think, the calibration produces an error. If I choose a higher current during the calibration the needed phase-advance angle must be increased.

One solution to this observation could be that during calibration there is already a phase difference of e.g. 45°, so adding 45° in the open-loop should be fine. Increasing the current in calibration would reduce the lag behind, so the phase advance has to be increased in open-loop.

Are you running the calibration in both directions and averaging the result like I suggested before? That should eliminate such errors.

Arrgh, yes, I missed that. I’ll try that again.
Thank you very much for sharing your experience!

(Have not fully read the whole thread)
We are having good results with SmoothingSensor on our 3-hall bldc 350 Watt hoverboard motors: #include <encoders/smoothing/SmoothingSensor.h>

At low speed we do not really need max efficiency and at already moderate speed, the simple smoothing with a low pass on shaftVelocity (if i remember correctly) is good enough with a linear prediction as the time intervals of the hall steps are very short.


Hi @robo_durden . welcome to SimpleFOC!

Thanks for sharing your experiences and code, it will be helpful to other users :slight_smile:

Thanks Deku for the smoothing sensor thing, a lot of people seem to be using it.

Don’t forget, code execution time has an impact on motor timing too in some cases. The code doesn’t run instantly, sometimes by the time you use the sensor angle the rotor has moved on, basically.

Yeah, it’s amazing how well it works with such a simple calculation. I think we can use the same technique to effectively eliminate the execution time with a simple modification to the last line of loopFOC:
setPhaseVoltage(voltage.q, voltage.d, electrical_angle + shaft_velocity * loopFOC_execution_time);

Could go well with this

I have ramp up/down code in my main loop, which will only happen after start or direction changes.
Will your idea of “calculate TS only once” work with such extra code?
Does it “calculate once per loop”?

Ah sorry what I shared now is not the loopfoc execution time, it’s the loop duration.

My idea was to measure the elapsed time only once per loopfoc and use it for all those functions.
It would reduce the calculation and prevent some discrepancies due to interrupts running in the middle.
That doesn’t give you the loopfoc duration but the main loop duration(loopfoc+move+other stuff), that you can use to derive main loop frequency.

So sorry I am a off topic again.
What @dekutree64 is talking about is different, it’s to measure the time between sensor.update and setting the pwm duty cycle (more or less the loopfoc duration) and extrapolate the angle.