Skip to content
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

HallSensor interpolation/smoothing/prediction #9

Open
RoboDurden opened this issue Jul 25, 2023 · 27 comments
Open

HallSensor interpolation/smoothing/prediction #9

RoboDurden opened this issue Jul 25, 2023 · 27 comments

Comments

@RoboDurden
Copy link
Contributor

RoboDurden commented Jul 25, 2023

continuing from #6

@Candas1 applying https://github.com/dekutree64/Arduino-FOC-drivers/blob/dev/src/encoders/smoothing/SmoothingSensor.cpp#L11 wrote: The extrapolation seems to work at constant speed at least now.

Nice visualization tool you have: https://user-images.githubusercontent.com/20670049/256005760-0de8c63f-a36a-48b2-a815-bd4770bc186d.png

You may also want to check max speed with this new inline function HallSensor.h :

float getRpm(){return direction * (60000000.0f / pulse_diff) / (cpr); };

and in main.cpp

OUT2T("KV",sensor.getRpm() / driver.voltage_power_supply )

@Candas1
Copy link
Owner

Candas1 commented Jul 26, 2023

[Moving the message here]
Some signs were wrong in my case:
image

The extrapolation seems to work at constant speed at least now.
image

I need to do more tests this evening.

@Candas1
Copy link
Owner

Candas1 commented Jul 27, 2023

I did some tests, it seems the zero_electrical_angle was still off by 0.17 something (should be 1.92 instead of 2.09), it needed to be tweaked.
The way the sensor alignment procedure is done in simplefoc, it cannot be precise, it's meant for encoders mainly, so for hall sensors it has to be tweaked by hand. I believe you also played around with this parameter.
That's what I meant when I said the hoverboard firmware are already tweaked for hoverboard wheels, those aspects are probably hardcoded or baked into the firmware.

I might have to work on a better sensor alignment for hall sensor.
With bad alignment, FOC will probably be bad even if the current sensing is done right.

@RoboDurden
Copy link
Contributor Author

With my 2.0 board the sensor alignment procedure never did work at all. Only with the Gen1 stm32F103 did it succeed.
That is why i never understood how you obtained the 2.09.
Yes i did try to optimize your 2.09 but did not find a better value:

unsigned long iOptimize = 0;
float fOptimize = 0;
..
  OUT2T(fOptimize*1000, motor.zero_electric_angle*1000)
  fOptimize = -0.2  * (ABS(	(float)((iOptimize++ + 20) % 80) - 40) - 20)/20;
  motor.zero_electric_angle = 2.09 + fOptimize;

I might have to work on a better sensor alignment for hall sensor.

Might be good if you would be more prescise. We have used the word "alignment" in so many contexts by now, so i do not really understand what you mean with "better sensor alignment".
Only parameter is this zero_electric_angle. So you want to write a better auto zero_electric_angle detection ?

@Candas1
Copy link
Owner

Candas1 commented Jul 27, 2023

Sensor alignment is what happens here.

This will work well with an encoder, but with a hall sensor it just gives you a hint.

I need to try this as well.

@Candas1
Copy link
Owner

Candas1 commented Jul 27, 2023

the motor is blocking in the other direction if I am using 1.92.
I need to investigate more.

@Candas1
Copy link
Owner

Candas1 commented Jul 27, 2023

Ok I think I found the solution, and I have learned more, I summarized it all here
I am curious how Eferu compares with those number but I wouldn't be able to check before this weekend.
Eferu doesn't have such compensation (or I couldn't find it in the model), probably it's optimized only for some of the speed range.

@RoboDurden
Copy link
Contributor Author

Nice. I do not really understand a lot of your post over there.
But i am working with my Arduino-FOC that i forkerd from your Arduino-FOC.
So if you publish your local code changes to your repo, i can sync my fork to test your work with my test setup :-)

(today i worked on a 20A 80V ESP32 OLED mppt charger..)

@Candas1
Copy link
Owner

Candas1 commented Jul 27, 2023

I updated the dev branch.

@RoboDurden
Copy link
Contributor Author

Now that is a bit confusing (for me). You seem to work on three different branches and the latest _configure6PWM is in main but smoothedSensor in another branch ?

@Candas1
Copy link
Owner

Candas1 commented Jul 28, 2023

I am talking about the dev branch of this repository:
image

It uses the dev-gd32 branch of my Arduino-foc fork, and the dev branch of my arduino-foc-drivers fork.

@Candas1
Copy link
Owner

Candas1 commented Aug 1, 2023

I did a pull request to simplefoc dev branch with the direction fix and they accepted it.
So I think the smoothing is complete.
I will work on improving the autodetect only after the current sensing.

@RoboDurden
Copy link
Contributor Author

Okay @Candas1 here my test comparison between EFeru and your firmware here on a Gen1 board:

  driver.voltage_power_supply = 26; // 3.6 * BAT_CELLS; // power supply voltage [V]
  motor.voltage_limit = driver.voltage_power_supply * 0.58; // should be half the power supply

STM32 Gen1 board:

bin	target	[rpm/V]	[A]

smFOC	T5+E	-5.4	0.17
smFOC	T-5+E	+5.3	0.17
EFeru	+390	-5.4	0.25
EFeru	390	+5.4	0.23

smFOC	T15+E	-11.4	0.31
smFOC	T-15+E	+11.1	0.30
EFeru	+760	-11.3	0.38
EFeru	-760	+11.3	0.37

smFOC	T26	-13.9	0.68
smFOC	T-26	+12.9	0.48
EFeru	1000	-14.6	0.50
EFeru	-1000	+15.0	0.51

no SmoothingSensor:

smFOC	T5+E0	-5.5	0.19
smFOC	T-5+E0	+5.3	0.19
smFOC	T15+E0	-11.8	0.49
smFOC	T-15+E0	+11.1	0.38

This inital nasty "push" might be because some class variable is not initialized to 0..

To make the Commander class work nicely with the short uart cable of the gen1 board, it would be nice if you would update your config.h to

// Define to prevent recursive inclusion
#ifndef CONFIG_H
#define CONFIG_H


#if defined(PLATFORMIO)
  #ifdef STM32F103RC
    #define HOVER_GEN   1
    #define HOVER_LAYOUT	0

    // too late here, has to be a build flag #define SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH false

    // call motor.initFOC() without parameters to auto align sensor and copy values from debug log
    #define MOTOR_zero_electric_offset  4.19  
    #define MOTOR_sensor_direction  Direction::CCW

    // log debug output over master/slave uart
    //#define DEBUG_UART  Serial
    HardwareSerial oSerialSteer(PB11, PB10);  // short cable 5VT EFeru USART3 GPIO Configuration
    #define DEBUG_UART oSerialSteer

  #else
    #define HOVER_GEN   2
    #define HOVER_LAYOUT	0

    // call motor.initFOC() without parameters to auto align sensor and copy values from debug log
    #define MOTOR_zero_electric_offset  2.09
    #define MOTOR_sensor_direction  Direction::CCW

    // SEGGER RTT Debug
    #define DEBUG_STLINK rtt              // Uncomment to enable DEBUG over stlink dongle

  #endif
#else
  // LAYOUT_x_y is used in defines.h
  #define HOVER_GEN   2
  #define HOVER_LAYOUT 0
  //  2_0	// https://github.com/flo199213/Hoverboard-Firmware-Hack-Gen2
  //  2_1	// https://github.com/krisstakos/Hoverboard-Firmware-Hack-Gen2.1
  //  2_2	// 2023/05/11 only MASTER and TEST_SPEED: motor is spinning but needs a push to startup :-/
  //  2_4	// NOT READY !!! https://github.com/RoboDurden/Hoverboard-Firmware-Hack-Gen2.x/issues/3
  //  2_5	// NOT READY !!! https://github.com/RoboDurden/Hoverboard-Firmware-Hack-Gen2.x/issues/11

  //  1_0	// old Gen 1 boards with two motors

    // call motor.initFOC() without parameters to auto align sensor and copy values from debug log
    #define MOTOR_zero_electric_offset  2.09
    #define MOTOR_sensor_direction  Direction::CCW

    // SEGGER RTT Debug
    #define DEBUG_STLINK rtt              // Uncomment to enable DEBUG over stlink dongle
    // log debug output over master/slave uart
    //#define DEBUG_UART  Serial2

#endif


#define BLDC_POLE_PAIRS   15    // all hoverboard motors have 15 pole pairs ?
#define BAT_CELLS         7     // battery number of cells. mostly 10 = 10s = 36V. Sometimes 7 = 7s = 25V

#ifdef DEBUG_UART
  #define DEBUG_UART_BAUD   115200    // [-] Baud rate for HoverSerial (used to communicate with the hoverboard)
#endif

#define TIME_SEND           10000         // [ms] Sending time interval


#endif //  CONFIG_H

https://github.com/Candas1/Split_Hoverboard_SimpleFOC/blob/dev/src/main.cpp#L88 would be changed to

Commander command = Commander(SERIALDEBUG);
The auto calibration did again not work for me.
I got this MOTOR_zero_electric_offset 4.19 from an older version of our code when the auto calibration did work.
By now i have the same identical bldc motors installed in the gen1 test setup and the gen2.0 setup.

Lower case comands were more suitable for me:

  command.add('t', doTarget, "target voltage");
  // add smoothing enable/disable command E (send E0 to use hall sensor alone, or E1 to use smoothing)
  command.add('e', enableSmoothing, "enable smoothing");
  command.add('p', doPhaseCorrection, "phase correction");
  command.add('z', doZero, "zero electrical angle");
  command.add('f', doLPF, "LPF");

I also added a low pass to target:

    fSpeed = 0.999 * fSpeed + 0.001 * target;

    //GPIO_BOP(GPIOB) = (uint32_t)GPIO_PIN_7;
    motor.move(fSpeed);
    //GPIO_BC(GPIOB) = (uint32_t)GPIO_PIN_7;

maybe this line is missing your main.cpp:

driver.voltage_limit = motor.voltage_limit; // stupid bug to have two voltage_limit in different places

@Candas1
Copy link
Owner

Candas1 commented Aug 3, 2023

Thank you.
Great so SFOC is more efficient, but not as fast.

It could be the inductance value has to be tweaked on different motors. I am using one with 5 coil wires and 30mm magnets. I am thinking about buying a LCR meter to measure it.

I tried the low pass filter class from SFOC some time ago but couldn't get it to work for the target.

No driver and voltage limit are equal by default.

@RoboDurden
Copy link
Contributor Author

I don't think so:

void BLDCMotor::init() {
...
  // sanity check for the voltage limit configuration
  if(voltage_limit > driver->voltage_limit) voltage_limit =  driver->voltage_limit;

motor::voltage_limit can be smaller than driver::voltage_limit

But i do no longer want to think about that stupidity to have two different voltage_limit
and on top both (seemingly to me) being used at random in BLDCMotor.cpp :-(

@RoboDurden
Copy link
Contributor Author

LowPassFilter LPF_target(0.5);  //  the higher the longer new values need to take effect

void loop()
{
...
    motor.move(LPF_target(target));

@Candas1
Copy link
Owner

Candas1 commented Aug 3, 2023

If you uncommented the curent sense, maybe the current sense alignment is running

@RoboDurden
Copy link
Contributor Author

Your last update to Candas1/Arduino-FOC/tree/dev-gd32 was two weeks ago.
So i do not understand what you mean with "... maybe the current sense alignment is running"

@RoboDurden
Copy link
Contributor Author

I have tested the smoothed sensor with 25V sine (stm Gen1 board):
stmstudio smooth Sine25V

    float fTarget = LPF_target(target) * (ABS((float)(((millis()-iLoopStart)/10 + 250) % 1000) - 500) - 250)/250;
    motor.move(fTarget);

    fAnglePred = fmod(smooth.angle_prev * 57.295779513082320876798154814105 + 360.0 , 360.0);
    fAngleReal = fmod(sensor.angle_prev * 57.295779513082320876798154814105 + 360.0 , 360.0);

Looks like i need to also set an optimized phase correction for SmoothingSensor::update():

  // Apply phase correction if needed
  if (phase_correction != 0) {
    if (_motor.shaft_velocity < -0) angle_prev -= _motor.sensor_direction * phase_correction / _motor.pole_pairs;
    else if (_motor.shaft_velocity > 0) angle_prev += _motor.sensor_direction * phase_correction / _motor.pole_pairs;
  }

I am not too happy with this more general SmoothingSensor approach that can wrap any kind of rotation-position sensor as it can not make use of the interrupt driven HallSensor method HallSensor::updateState().
Instead it must rely on the Sensor:.update() function which is called in motor.loopFOC() far more often (every single blue dot in the curve of fAnglePred) than HallSensor::updateState() (the coarse 4° steps of the grey curve fAngleReal).
And SmoothingSensor does not implement its own lowpass but cleverly uses _motor.shaft_velocity which has a lowpass:

float FOCMotor::shaftVelocity() {
  // if no sensor linked return previous value ( for open loop )
  if(!sensor) return shaft_velocity;
  return sensor_direction*LPF_velocity(sensor->getVelocity());
}

This method is called in void BLDCMotor::move(float new_target) and therefore motor.move(target) has to be called at the same rate as motor.loopFOC().

I think this is NOT intuitive as people might want to call motor.move only a few times per second to update a new target speed.

So yes, SmoothingSensor is working but all it basically does is adding a low pass to the angle and does a linear prediction

  float dt = (_micros() - angle_prev_ts) * 1e-6f;
  angle_prev += _motor.sensor_direction * _constrain(_motor.shaft_velocity * dt, -_PI_3 / _motor.pole_pairs, _PI_3 / _motor.pole_pairs);

Implement that "two lines" directly into HallSensor might be the better way...

@RoboDurden
Copy link
Contributor Author

SmoothingSensor.h

    // For hall sensors, the predicted angle is always 0 to 60 electrical degrees ahead of where it would be without
    // smoothing, so offset it backward by 30 degrees (set this to -PI_6) to get the average in phase with the rotor
    float phase_correction = 0;

Setting it to +0.1 already had a bad effect.
Setting it to -0.52 = -Pi/6 was okay but does not really help:
stmstudio smooth Sine25V with phase correction

@RoboDurden
Copy link
Contributor Author

RoboDurden commented Aug 4, 2023

here the downside of a low pass: it takes some time the direction change is noticed:

stmstudio smooth Sine25V 0 5Hz with phase correction

@Candas1
Copy link
Owner

Candas1 commented Aug 4, 2023

I already discussed with the contributor, I told him it's not optimal for hall sensors. But that's the best solution we have now and it works most of the time.

EFeru would stop predicting at slow speed or direction change, this is easy to do.

@RoboDurden
Copy link
Contributor Author

I guess i will spend half a day to test my direct HallSensor impelementation against these SmoothedSensor results here :-)

@Candas1
Copy link
Owner

Candas1 commented Aug 4, 2023

You could have checked already weeks ago.
Feel free to talk to him. I don't want to be a proxy between you and him.

@RoboDurden
Copy link
Contributor Author

Again, i do not want to pose problems when i can not offer solutions.

And i already figured out that linear prediction with a speed average is sufficient.
The inaccuracies i showed in the pics above are only at direction changes, so low speed an we do not really have to care for.

It is possible to give SmoothingSensor.h its own speed low pass

    float shaft_velocity;//!< current motor velocity 
    LowPassFilter LPF_velocity{DEF_VEL_FILTER_Tf};//!<  parameter determining the velocity Low pass filter configuration     

but as the speed will not change when motor.move() is not called, it is okay to use the lowpass filtered motor.shaft_velocity which gets updated by motor.move().

And when i only call motor.move at 10 Hz and some heave torque on the motor affects the shaft_velocity we have a lowpass anyway and do not see that when smoothing.

So everything is okay with SmoothingSensor :-)

@Candas1
Copy link
Owner

Candas1 commented Aug 4, 2023

OK then we are aligned.
I know it's not a perfect solution but it's good enough for now.
When all works we can check what is worth improving.

@RoboDurden
Copy link
Contributor Author

I think it is already perfect for us.
In the beginning i thought that a higher grade polynomial would better predict future speed changes like in my screenshots..
(That is why i did not want to wait for SmoothingSensor...)

But this is only needed at very slow speeds and we do not need to care for that.
At normal speed when noise and efficiency is important, the refresh rate is so high that a linear prediction is sufficient.
(That is why i stopped with my HallSensor-prediction.)

Now seeing that SmoothedSensor is more silent than my linear prediction, i happily close that chapter.

I guess when you have finished your channel3 timer0 adc, you have successfully ported SFOC to GD32F130.

I would still like to have a callback when adc has finished to trigger motor.loopFOC() every second call :-)

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

No branches or pull requests

2 participants