Zero electrical angle tuning and max speed

I have the following hardware:

gimbal motor with a phase resistance around 3 ohm and KV around 100 rpm/v
B-G431B-ESC1 discovery kit
AMT103-v position sensor.

I’m trying to drive this motor (unloaded) at max speed, but I’m seeing strange behavior. If I slightly over-estimate motor.zero_electrical_angle, the velocity in the positive direction increases but the velocity in the negative direction decreases. Specifically, at the initFOC() calibrated zero_electrical_angle and 7 volts, the speed in either direction is around 82 rad/s. With zero_electrical_angle + 0.5, the speed in one direction is 59 rad/s and 97 rad/s in the other direction.

What causes this behavior?

If the motor turns faster at the same voltage_limit, does that mean the motor is being driven more efficiently? Or is it more efficient to increase motor.voltage_limit to obtain higher speeds?

Test code:

void test_electrical_angle() {
  // not shown: motor / encoder initialization
  //

  const float desired_voltage = 7;
  const float test_duration = 3; // seconds

  motor.controller = MotionControlType::torque;
  motor.torque_controller = TorqueControlType::voltage;
  motor.foc_modulation = FOCModulationType::SinePWM;

  const float original_angle = motor.zero_electric_angle;

  
  Serial.printf("starting electrial angle measurements...\r\n");
    
  for (float angle_offset = -1.0; angle_offset <= 1; angle_offset += 0.1) {
    motor.zero_electric_angle = original_angle - angle_offset;

    int iters = 0;
    float end_ts = _micros() + test_duration * 1e6;
    float max_speed = 0;
    LowPassFilter lpf(.2);
    while (_micros() <= end_ts) {
      iters++;
      motor.move(desired_voltage);
      motor.loopFOC();
      max_speed = max(max_speed, lpf(abs(encoder.getVelocity())));
    }

    end_ts = _micros() + test_duration * 1e6;
    float min_speed = 0;
    lpf = LowPassFilter(.2);
    while (_micros() <= end_ts) {
      motor.move(-desired_voltage);
      motor.loopFOC();
      min_speed = max(min_speed, lpf(abs(encoder.getVelocity())));
    }

    Serial.printf("zero electrical angle: %6.3f %+4.2f, +velocity: %7.3f -velocity: %7.3f. %7.1f foc iterations/s\r\n", original_angle, angle_offset, max_speed, min_speed, float(iters / test_duration));
  }
  
  motor.move(0);
  motor.loopFOC();
  delay(1000 * 9999);
}

output:

zero electrical angle:  0.930 -1.00, +velocity:   2.325 -velocity: 102.791. 10957.7 foc iterations/s
zero electrical angle:  0.930 -0.90, +velocity:  37.211 -velocity: 102.600.  7657.3 foc iterations/s
zero electrical angle:  0.930 -0.80, +velocity:  45.145 -velocity: 102.112.  7296.0 foc iterations/s
zero electrical angle:  0.930 -0.70, +velocity:  50.514 -velocity: 101.180.  6944.7 foc iterations/s
zero electrical angle:  0.930 -0.60, +velocity:  55.401 -velocity:  99.664.  6485.0 foc iterations/s
zero electrical angle:  0.930 -0.50, +velocity:  59.233 -velocity:  97.343.  6052.0 foc iterations/s
zero electrical angle:  0.930 -0.40, +velocity:  64.450 -velocity:  93.969.  5732.3 foc iterations/s
zero electrical angle:  0.930 -0.30, +velocity:  68.398 -velocity:  90.029.  5379.0 foc iterations/s
zero electrical angle:  0.930 -0.20, +velocity:  72.916 -velocity:  86.935.  5060.0 foc iterations/s
zero electrical angle:  0.930 -0.10, +velocity:  77.711 -velocity:  85.847.  4812.7 foc iterations/s
zero electrical angle:  0.930 +0.00, +velocity:  82.050 -velocity:  82.579.  4666.7 foc iterations/s
zero electrical angle:  0.930 +0.10, +velocity:  85.652 -velocity:  78.561.  4513.3 foc iterations/s
zero electrical angle:  0.930 +0.20, +velocity:  86.619 -velocity:  73.090.  4377.3 foc iterations/s
zero electrical angle:  0.930 +0.30, +velocity:  89.765 -velocity:  69.055.  4200.7 foc iterations/s
zero electrical angle:  0.930 +0.40, +velocity:  93.383 -velocity:  64.732.  4049.3 foc iterations/s
zero electrical angle:  0.930 +0.50, +velocity:  96.874 -velocity:  59.851.  3941.0 foc iterations/s
zero electrical angle:  0.930 +0.60, +velocity:  99.382 -velocity:  55.736.  3837.3 foc iterations/s
zero electrical angle:  0.930 +0.70, +velocity: 100.954 -velocity:  50.830.  3739.7 foc iterations/s
zero electrical angle:  0.930 +0.80, +velocity: 101.886 -velocity:  45.949.  3670.3 foc iterations/s
zero electrical angle:  0.930 +0.90, +velocity: 102.209 -velocity:  38.086.  3646.7 foc iterations/s

Similar results are obtained with other FOCModulationType.

You have (re)discovered the phenomenum known as field weakening! By placing the magnetic field slightly ahead of the normal 90degrees you get a bit of extra speed whilst losing a bit of torque. If you move zero offset the other way your reverse direction will be faster!

Thanks, that was helpful.

I did some research and found a paper which details the required calculations (equation 15 through 17), it seems straightforward to implement.

It is not clear what the advantages of field weakening are. If I’m not yet limited by my power supply voltage, it doesn’t seem to make much sense to use field weakening. Is that right?

It most certainly makes sense when designing for a geared setup. Multiplying the gear-ratio means multiplying the torque while maintaining target top speed if utilizing this phenomenon. A simple lookup table with velocity vs. angle offset should do the trick. I wonder if the extra torque will make up for the lost torque? @Owen_Williams

hello.
I have implemented a bldc motor with angle control that works through pulses. as if it were a stepper motor.
With the same number of pulses in one direction it reaches more speed than in the opposite direction.
Could it be the fault of the value of zero_electrical_angle?

When starting the engine I get PP check:fail error.
MOT: Enable driver.
MOT: Align sensor.
MOT: sensor_direction==CW
MOT: PP check: fail - estimated pp: 2.25
MOT: Zero elec. angle: 2.39
MOT: Align current sense.
MOT: Success: 3
MOT: Ready.
Engine ready.

The motor has 2 pairs of poles and a 1000ppr encoder.

Why do it estimate 2.25 instead of 2ppr?

Thanks for your time.