Skip to content

B g431 b esc1 current sense rebase 2 #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 16, 2021

Conversation

sDessens
Copy link
Contributor

@sDessens sDessens commented May 3, 2021

An implementation of current sensing with DMA for the B-G431B-ESC1, with help of scouttman. #if defined(STM32G4xx) include guards are used to ensure the lib still compiles on other STM32 boards (I only tested the bluepill).

The example is copied from bluepill, only a few lines of code were added, the example is mainly there is serve as documentation for the shunt resistor and gain values for this specific board. I feel this is a bit wasteful,

Here is a simple test function that cycles though a full rotation in pi/12 increments and prints the set voltage and read current: This can be used to verify the readings are sensible.

PhaseCurrent_s getPhaseVoltages(BLDCMotor& motor) {
  float Ua = motor.Ua;
  float Ub = motor.Ub;
  float Uc = motor.Uc;
  float mid = (Ua + Ub + Uc) * (1 / 3.f);
  return PhaseCurrent_s{Ua - mid, Ub - mid, Uc - mid};
}

float calculatePower(PhaseCurrent_s phaseCurrent, PhaseCurrent_s phaseVoltage) {
  return
    phaseCurrent.a * phaseVoltage.a +
    phaseCurrent.b * phaseVoltage.b +
    phaseCurrent.c * phaseVoltage.c;
}

void test_current_vs_angle(BLDCMotor& motor, CurrentSense& currentSense) {
  float angle_target = 0;
  const int delay = 3000;

  motor.controller = MotionControlType::angle_openloop;
  motor.torque_controller = TorqueControlType::voltage;

  motor.sensor_direction = Direction::CW;
  motor.zero_electric_angle = 0;

  float one_step = PI * 2 / motor.pole_pairs / 24;

  Serial.printf(    "electrical angle | volts                | amps                 | watt\r\n");
  Serial.printf(    "                 |    U      V      W   |    U      V      W   |\r\n");
  const char *fmt = "%2i/12 pi         | % 6.3f % 6.3f % 6.3f | % 6.3f % 6.3f % 6.3f | % 6.3f\r\n";

  int counter = 0;
  while (counter < (25 * delay)) {
    counter++;
    int step = int(counter / delay);
    angle_target = step * one_step;
    motor.move(angle_target);

    if (counter % delay == (delay - 1)) {
      PhaseCurrent_s voltages = getPhaseVoltages(motor);
      PhaseCurrent_s currents = currentSense.getPhaseCurrents();
      float power = calculatePower(currents, voltages);
      Serial.printf(fmt, step, voltages.a, voltages.b, voltages.c, currents.a, currents.b, currents.c, power);
    }
    motor.loopFOC();
  }

  motor.voltage_limit = 0;
  while (1) {
    motor.move(0);
    motor.loopFOC();
  }
}

Sample output:

electrical angle | volts                | amps                 | watt
                 |    U      V      W   |    U      V      W   |
 0/12 pi         | -0.000  2.598 -2.598 | -0.005  0.917 -0.968 |  4.896
 1/12 pi         | -0.773  2.897 -2.124 | -0.299  1.034 -0.762 |  4.846
 2/12 pi         | -1.493  3.000 -1.507 | -0.534  1.122 -0.498 |  4.914
 3/12 pi         | -2.113  2.901 -0.788 | -0.739  1.005 -0.292 |  4.707
 4/12 pi         | -2.602  2.594  0.008 | -0.974  0.887  0.001 |  4.837
 5/12 pi         | -2.899  2.118  0.780 | -1.033  0.799  0.325 |  4.941
 6/12 pi         | -3.000  1.500  1.500 | -1.092  0.564  0.471 |  4.829
 7/12 pi         | -2.899  0.780  2.118 | -1.062  0.241  0.794 |  4.951
 8/12 pi         | -2.602  0.008  2.594 | -0.945 -0.053  0.941 |  4.900
 9/12 pi         | -2.130 -0.765  2.895 | -0.739 -0.317  1.059 |  4.882
10/12 pi         | -1.493 -1.507  3.000 | -0.475 -0.552  1.059 |  4.717
11/12 pi         | -0.773 -2.124  2.897 | -0.299 -0.787  1.088 |  5.055
12/12 pi         | -0.000 -2.598  2.598 |  0.024 -0.963  0.912 |  4.872
13/12 pi         |  0.772 -2.897  2.124 |  0.318 -1.051  0.794 |  4.979
14/12 pi         |  1.493 -3.000  1.507 |  0.583 -1.081  0.530 |  4.910
15/12 pi         |  2.113 -2.901  0.788 |  0.788 -1.051  0.325 |  4.971
16/12 pi         |  2.602 -2.594 -0.008 |  0.964 -0.993  0.001 |  5.084
17/12 pi         |  2.899 -2.118 -0.780 |  1.082 -0.758 -0.234 |  4.924
18/12 pi         |  3.000 -1.500 -1.500 |  1.111 -0.581 -0.469 |  4.909
19/12 pi         |  2.899 -0.780 -2.118 |  1.082 -0.288 -0.704 |  4.851
20/12 pi         |  2.602 -0.008 -2.594 |  0.906 -0.023 -0.909 |  4.715
21/12 pi         |  2.130  0.765 -2.895 |  0.788  0.241 -0.997 |  4.750
22/12 pi         |  1.493  1.507 -3.000 |  0.524  0.506 -1.085 |  4.800
23/12 pi         |  0.772  2.124 -2.897 |  0.289  0.799 -1.027 |  4.895
24/12 pi         | -0.000  2.598 -2.598 | -0.005  0.946 -0.909 |  4.820

My motor has about 2.8 ohms phase resistance.

@askuric
Copy link
Member

askuric commented May 6, 2021

Hey @sDessens !
Thanks for the awesome work. We are going to merge it to the dev branch as soon as we release v2.1.1.
The release after that one v2.1.2+ will have at lease some families of low side sensing supported.
We will also have the LowSideCurrentSense class implemented.
I'll come back to the PR after the release, probably this weekend.

@conroy-cheers
Copy link

Any update on this?

@askuric
Copy link
Member

askuric commented Sep 3, 2021

We are very slow lately :/
We will definitely integrate this to the library soon, but I cannot promise you when, I'd say end of September.

@conroy-cheers
Copy link

conroy-cheers commented Sep 4, 2021 via email

@sDessens
Copy link
Contributor Author

sDessens commented Sep 4, 2021

The current implementation uses oversampling with ADC_OVERSAMPLING_RATIO_2. My setup seems to run smoother at high speeds with oversampling disabled.

It would be great if the community could test whether performance on their specific motor and use case is better with or without oversampling.

To disable oversampling, locate: Init.OversamplingMode = ENABLE; on line 101 and 178 of stm32g4_hal.cpp and change to DISABLE.

@conroy-cheers
Copy link

@sDessens, I'm currently running into an interesting issue at high speeds, which seems to be affected by the ADC oversampling.
My hardware setup:

  • B-G431B-ESC1
  • MAD5010 IPE (14 poles, 370 KV)
  • 2048PPR optical encoder
  • 24V bench supply

FOC torque control works great at low speeds. At high speed (above ~105rad/s), something related to the current measurement seems to fail, as the output voltage goes straight to the set maximum, and the motor runs very rough.
With oversampling enabled, the same failure seems to happen, but at lower speed (~80rad/s).

Any idea what could be causing this?
I'll try to do some debugging and update with findings.

@sDessens
Copy link
Contributor Author

sDessens commented Sep 9, 2021

I was able to obtain speeds up to 600rad/s with a magnetic encoder and speeds up to 1000rad/s with an optical encoder (7 pole pair motor, so 7000 erad/s). Those speeds were only possible after heavy software optimizations, my software loop runs at 12k FOC iterations per second, about 10 FOC iterations per electrical rotation.

Could you measure your execution speed and calculate, roughly, how much FOC iterations your setup does per electrical rotation at speed? With my setup, motor performance seems to decrease significantly once I go any slower than 20 FOC iterations per electrical rotation, but I am unsure whether this is a software limitation, motor problem or a sensor issue.

Oversampling increases the measurement latency, at high speeds this may cause the FOC algorithm to estimate the current at the wrong electrical angle. I tried modifying the software to calculate the foccurrent with a lagged electrical angle, but found no improvements (possibly related to a sensor issue). Increasing the pwm frequency may also help.

Also try lowering the sensor resolution. All the interrupts from the sensor can slow down FOC calculations.

@conroy-cheers
Copy link

Thanks for the reply! I've just measured the number of calls to loopFOC(); it sits at around 6500 at idle, and decreases (!) to under 2500 as the shaft speed increases. The trouble RPM of 80 gives roughly 2500-3500 Hz, which is less than 3 iterations per electrical rotation. No wonder I'm having problems here.

Assuming the execution time of loopFOC() is unaffected by shaft speed (this seems like a reasonable assumption), there's a LOT of CPU cycles getting eaten up by encoder interrupts. Unfortunately, I can't really decrease the sensor resolution, as it's a code disc with 2048 physical slits.

I wonder if it's possible to use the STM32 timer hardware to read the AB encoder...
any suggestions there before I dive into it?

@sDessens
Copy link
Contributor Author

sDessens commented Sep 9, 2021

That matches my experience with encoders. I have mine set to a low CPR (96) to avoid these issues, although this leads to poor performance at low speeds. You could try to optimize the sensor class, it does more work in the interrupt than strictly necessary.

I wasn't able to figure out how to use hardware timers, magnetic sensors looked like the superior solution.

This board also supports backemf detection, but nobody has tried to implement this yet.

@runger1101001
Copy link
Member

STM32 timers have an "encoder mode". I've never used it, but wondered how hard it would be to interface with SimpleFOC. I haven't had much time lately, and have too many other open topics to take it on at the moment though.

It's described quite well here:
https://deepbluembedded.com/stm32-timer-encoder-mode-stm32-rotary-encoder-interfacing/
but the code examples are for STM32CubeIDE
In general the problem seems to be that there are no Arduino libraries for STM32 timers encoder mode. Nonetheless, it should not be too hard to do it just using the registers following the code examples for other frameworks that are available online.

Note there are also magnetic encoders (like the AS5047 or some MagAlpha models) that have both digital SPI and encoder ABI type interfaces. I've been wondering whether using the quadrature mode of the magnetic encoders would lead to lower latencies.

In any case, I'm following this discussion with great interest, and look forward to learning what your investigation turns up, if you care to share it.

  for this board, use InlineCurrentSense with:
  shunt_resistor = 0.003
  gain = -64.0/7.0
@sDessens sDessens force-pushed the B-G431B-ESC1-current-sense-rebase-2 branch from 34684fd to c1ce9d7 Compare September 9, 2021 14:37
@sDessens
Copy link
Contributor Author

sDessens commented Sep 9, 2021

I disabled oversampling and rebased my changes on the latest dev branch.

You may need the fix described in #99 if the board fails to initialize correctly.

@conroy-cheers
Copy link

@sDessens, thanks for linking that workaround, I was banging my head against the wall for a while before I saw it! After increasing the call rate of loopFOC(), torque control performance seems very good with my setup.

@runger1101001, thanks for the pointer. I've implemented hardware encoder handling with an STM32 timer at #114; it's not yet portable to other devices, but is working great on my B-G431B-ESC1.

@askuric
Copy link
Member

askuric commented Sep 16, 2021

Hey guys,
Thanks again for your work!
I do not really have a way to test the code, so we will let the community to test it 😃

@askuric askuric merged commit 87c520f into simplefoc:dev Sep 16, 2021
askuric added a commit that referenced this pull request Sep 16, 2021
@sDessens sDessens deleted the B-G431B-ESC1-current-sense-rebase-2 branch August 24, 2022 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants