Zero_electric_angle differences among modulation types

The zero_electric_angle, as computed by InitFOC(), is very different depending on the chosen foc_modulation. Is this to be expected?

My zero_electric_angle:

  • FOCModulationType::SinePWM → 3.761319
  • FOCModulationType::SpaceVectorPWM → 3.752118
  • FOCModulationType::Trapezoid_120 → 4.273671
  • FOCModulationType::Trapezoid_150 → 4.191604

Anyone on this?

Not sure if this is related, but it seems there’s a sector computation issue in the Trapezoid_150 routine:

m.foc_modulation = FOCModulationType::Trapezoid_150;

m.setPhaseVoltage(m.voltage_sensor_align, 0, 0);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

m.setPhaseVoltage(m.voltage_sensor_align, 0, _2PI);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);
-0.500000 5.500000 -0.500000
2.500000 5.500000 -0.500000

Shouldn’t the outputs be the same?

I’m also very intrigued by this behavior (my voltage_sensor_align is 3V):

const float electricalAngle = 0;

m.foc_modulation = FOCModulationType::Trapezoid_120;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

m.foc_modulation = FOCModulationType::Trapezoid_150;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

m.foc_modulation = FOCModulationType::SinePWM;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);
2.500000 5.500000 -0.500000
-0.500000 5.500000 -0.500000
2.500000 5.098076 -0.098076

Why are the outputs all different?

@runger, @Antun_Skuric:
In this line and this line, I believe the +_PI_6 prevents finding the correct zero_electric_angle in both trapezoid modes.

Hey @quentin,

I’ll have to look into it a bit more in detail to give you a more complete answer. But, what I can tell you right away is that the trapezoidal modes don’t need the same precision of the electrical angle as the sine and space vector modes.
Trapezoidal modulations effectively have discrete electrical angle measured in 6 and 12 sectors. Additionally, they only can apply certain voltage vecltors (only 6 or 12 vectors).
So that could be the source of difference in the measured angle.

There might be an error in the library somewhere too, we need to investigate this a bit further.
These modulations are not much used, so there might be some issues. :smiley:

Thanks @Antun_Skuric.

To be frank, I looked into trapezoidal modulations only because it somewhat plays a role in finding the zero_electric_angle in other modes (as it’s easy to use a trapezoidal step at startup to align the sensor). But the third point below might relate to sin mode.

To summarize this thread, there are three potential issues/questions:

  1. Potentially incorrect zero_electric_angle in trapezoidal modes, probably due to the additional +_PI_6.
  2. Sector computation issue in setPhaseVoltage() in trapezoidal modes: setPhaseVoltage(0) not setting the same Ua, Ub, Uc as setPhaseVoltage(_2PI).
  3. setPhaseVoltage(0) setting different values for Ua, Ub, Uc when trapezoidal or sin modes are used, even when issue 1 is fixed.

The pi/6 offset rounds to the nearest sector rather than truncating, which I think is correct so it won’t tip to the next sector as soon as the angle goes from 0 to negative, but I’m not 100% sure. Have you tried removing it and seeing what zero angle you get?

If we remove it, then HallSensor will need to add a similar offset to the angles that it returns, else we’ll get numerical accuracy problems on the float to int conversion being near integer boundaries.

After some investigation, that numerical accuracy problem is why 0 and 2pi are giving different results in trapezoid150. But the reason we’re ending up near integer boundaries is because trapezoid150 should offset by pi/12, not pi/6, because its sectors are half the size. Either that or we could do the rounding just before integer conversion like sector = 12 * (_normalizeAngle(angle_el) / _2PI) + 0.5f;
Should be the same result either way.

EDIT: On second thought, the result will not be the same either way. The offset must be applied before normalizing the angle, else it may round up to sector 12 and read off the end of the array.

1 Like

Yes, removing the +_PI_6 mostly fixes the zero_electric_angle discrepancy between trapezoidal and sin modes. However, there might remain an additional issue, see my point 3 above.

To put the facts in context, I want to add that offsetting or rounding angles in setPhaseVoltage() is probably not an issue most of the time. But we need to be sure that, when alignSensor() calls setPhaseVoltage(voltage_align, 0, _3PI_2) to find the zero electric angle, we really end up setting the phases to 3PI/2 and not to something else.

I am not sure how does the trapezoidal modulation play a role for other modes?
Is it because you use trapezoidal mode in startup and then use the sine modulation afterwards?

Hmm, I never tried something like that. But in theory I do agree that it’s strange that it does not work.

The reason I got interested in trapezoidal modulation is that I need to rewrite the initFOC() function because my motor has limits. Those limits prevent me from using only 3PI/2 to do the zeroing, as this angle might be unreachable (depending on the motor initial position). Because I potentially need to use other multiples of PI/6 or PI/12 to do the zeroing, I came to investigate the trapezoidal modes as a way of learning.

So my concern is not really the trapezoidal modulation, but whether using setPhaseVoltage() with multiples of PI/6 or PI/12 sets an electrically accurate angle, i.e. without averaging/rounding/offsetting. I think I’ve found an issue with trapezoidal modes, but maybe even one in sin mode (see above, my point 3).

You can use any angle in the initFOC whichever you like really.
The -pi/2=3pi/2 is used for convenience, as it will result in the motor’s alignment with the electrical angle equal to -pi/2+pi/2=0.

You can apply any angle a at the init and the motor will end up at at that angle a+pi/2.

I’m not really sure that I understand the point 3.

Maybe it’s me not understanding what’s going on, but I don’t understand why this code:

const float electricalAngle = 0;

m.foc_modulation = FOCModulationType::Trapezoid_120;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

m.foc_modulation = FOCModulationType::SinePWM;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

gives this output:

2.500000 5.500000 -0.500000
2.500000 5.098076 -0.098076

My understanding is that, with angles multiple of PI/6 (and even PI/12?), both method should return the same phase values. Am I missing something?

Hmm, I wonder if we should be using +.866,-.866 in the trapezoid tables rather than +1,-1. Otherwise trapezoid modes are running about 15% higher voltage, and the effect of voltage-based current limiting won’t match between the two.

Has anyone actually measured the phase currents when using voltage-based limiting? It actually looks more like trapezoid will be correct and sine and SVPWM will limit to .866 times the requested current.

In any case, trapezoid120 should always jump the motor to the physical cogging step positions, because the voltage values it outputs are based on the sector number, which will always be an integer regardless of whether the float value was truncated or rounded.

The pi/6 rounding offset only impacts how the sector number relates to the sensor reading and zero angle. Judging by your results with the zero angle, the comment “adding PI/6 to align with other modes” is wrong, and in fact causes it NOT to align with the other modes.

But as said in my previous post, if we remove the rounding, then we will have numerical trouble with the angles output by HallSensor. Maybe the best solution for that would be to change the pi/6 offset to something small like .001 to tip it over the integer boundary without actually rounding.

Trapezoid150 does half-stepping. Even numbered sectors are cogging steps, odd numbered are half way between and don’t hold reliably on my little motor with strong cogging torque.

1 Like

I now understanding why this code:

const float electricalAngle = 0;

m.foc_modulation = FOCModulationType::Trapezoid_120;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

m.foc_modulation = FOCModulationType::SinePWM;
m.setPhaseVoltage(m.voltage_sensor_align, 0, electricalAngle);
printf("%f %f %f", m.Ua, m.Ub, m.Uc);

gives this output:

2.500000 5.500000 -0.500000
2.500000 5.098076 -0.098076

The discrepancy is actually not that bad: both modulation types give a Ua phase of 2.5V, with Ub and Uc symmetrically distributed around this center. Only the amplitude varies:

  • in Trapezoid_120 the amplitude is 5.5 - 2.5 = -05 - 2.5 = 3V
  • in SinePWM the amplitude is 5.098076 - 2.5 = -0.098076 - 2.5 = 2.598076V.

The reason why the amplitude is different is because Trapezoid_120 uses the full motor.voltage_sensor_align value, whereas SinePWM uses _SQRT3_2 * motor.voltage_sensor_align instead.