bipropellant / bipropellant-hoverboard-firmware

OpenSource Hoverboard firmware based on Niklas Fauth's one https://github.com/NiklasFauth/hoverboard-firmware-hack
GNU General Public License v3.0
169 stars 72 forks source link

BLDC Control Optimization 3 #41

Open btsimonh opened 5 years ago

btsimonh commented 5 years ago

Continuation of https://github.com/bipropellant/bipropellant-hoverboard-firmware/issues/32

Concrete information can be added here: https://github.com/bipropellant/bipropellant-hoverboard-firmware/wiki/Notes-on-BLDC-Drive-and-Hoverboard-motors or as additional pages on the wiki :).

btsimonh commented 5 years ago

@EmanuelFeru -

I just received my new board! :) It has a GD32 on it.

what kind? Did you get nice broken out extra pins? https://github.com/btsimonh/hoverboard-firmware-hack/tree/newprotocol/otherboards see ExtraPins2.jpg ....

EFeru commented 5 years ago

That I didn't check. I will check when I get home. It is a green color board.

Update: It does not have those pins :( IMG_20190614_165615~3

jebc commented 5 years ago

Just wanted to point out that Odrive is using a STM32F4 and has great hoverboard motor control so some of the math might apply.

Code is here: https://github.com/madcowswe/ODrive/tree/master/Firmware

I'm working a robot/rover and am using @EmanuelFeru branch (with some serial checksum addtions), and will switch over here with the serial fixes.

I would like to get to Serial control, low speed, high torque, and great efficiency.

btsimonh commented 5 years ago

@jebc - welcome! yes, low speed with nice control is an ambition. The current PID position control is a little SLOWWW to achieve where we want to be, but works. Give it a try, tune the PID, and let us know how you get on. An above all, propose changes!

btsimonh commented 5 years ago

@EmanuelFeru - hmm....

Update: It does not have those pins :(

not even any empty IC sites :(.

maybe buzz these to the CPU

image

We NEED to have you with serial!

lalalandrus commented 5 years ago

I mentioned this before you can use the led and the buzzer as tx rx

btsimonh commented 5 years ago

yes, I think we should have a std firmware configuration for LED/Buzzer software serial which configures for this, and some advice on wiring/soldering the serial?

lalalandrus commented 5 years ago

i havent had a chance to test your new power button + 15 seconds code, but does it make sense to remove the software serial if this proves to work? we can re-use the software serial timer for independance to the pwm timer. we can then optimze the pwm and the bldc_step frequencies

can we increase the cpu frequency? the cpu is running at 64mhz when the stm can perform at 72mhz?

btsimonh commented 5 years ago

Note to all: PR https://github.com/bipropellant/bipropellant-hoverboard-firmware/pull/39 has been updated with graceful current limit.

If you have ASCII, you can set with e.g. fcl300\n to set a 3A limit. If you ride a hoverboard YOU WILL FALL OFF with a 3A limit :). However, despite the simplistic implementation, the limiting appears to be very graceful.

@juodumas - @lalalandrus - Looking forward to hearing results from those who can easily test under load

btsimonh commented 5 years ago

@lalalandrus

but does it make sense to remove the software serial if this proves to work

nope. 'cause I don't open my board for serial access, it's hard wired to the internal RPiZero :)....

I've done one last commit, setting the PWM GPIO pins back to slow, to try to reduce the electrical noise, and improve my serial connection.

results of current limit set at 3A and holding the board stead at an angle:

Bat: 38656mV(0) Temp:49C(1660) L: Current:0mA Avg:54mA r1:4 r2:4 R: Current:3099mA Avg:2969mA r1:362 r2:11

Bat: 38619mV(0) Temp:48C(1660) L: Current:39mA Avg:48mA r1:5 r2:4 R: Current:3180mA Avg:2998mA r1:359 r2:9

Personally, I think we're in a pretty good state ref @EmanuelFeru 's BLDC implementation.

The current limit stuff takes our IRQ back up to 32-34 us :(.

For me, the next steps would be

1/ to do the predictive thing, where the PWM values for one 60 degree (between hall sensor readings) motion are pre-set outside of the PWM IRQ, and reduce the IRQ time to the predicted <10us.
However, as the software stands, I think we will have solved ALL the ticks/bangs/misfires with this last limiting fix. 2/ understand the Actual current in the coils better, and take dead time into account -> smoother drive because it's the currents we want to be sinusoidal, and the u/v/w drives certainly have a pedestal at the moment, which will lead to a strange current waveform. (see wiki for explanation of dead time and it's effect).

Summary of issues found and resolved:

1/ sensor angle misreads: there originally was no protection from the angles being wrong, we only looked at the 55/AA, and if it was there, trusted the angle. We now ensure that a) the angles match b) the angle change was not 'large'. 2/ (probably only affected me) We did not protect against bad 'roll' angle - so when we got one, the whole board disable based on the end-to-end angle being 'dangerous'. It now checks for multiple 'roll > max' before disable. 3/ current limit was VERY harsh (I like'd @EmanuelFeru 's term 'Rude' :) ). We now have a very graceful limiter.

EFeru commented 5 years ago

@btsimonh

3/ current limit was VERY harsh (I like'd @EmanuelFeru 's term 'Rude' :) ). We now have a very graceful limiter.

How did you implement the current limiter? Just as we discussed an integrator that goes up and down limiting the current? Because I have a concept ready, in which we can set the Continuous current limit (15 A) and A PEAK current limit (for example 20 A). Derating starts when we pass 15A. On top, we can also set: Derating time and Recovery time. I just need to put it is C code. I have it working nicely in Simulink, but this time (since is small code) I will do the code directly in C.

btsimonh commented 5 years ago

It's really simplistic, but works well.

It's basically, for each side:

m->pwm_limiter = 1024; // initialisation

and in the 16khz part:

    int abs_dc = ABS(buf->dcl - adc_buffers.offsetdcl);
    if(abs_dc > electrical_measurements.dc_adc_limit) {
      if (m->pwm_limiter > 1) {
        m->pwm_limiter --;
      }
    } else {
      if (m->pwm_limiter < 1024) {
        m->pwm_limiter++;
      }
    }

    // multiply by limiter, divide by 1024 with shift.
    int pwm_mod = (m->pwm_limiter*pwml) >> 10;

.....

        rtU_Left.r_DC      = -pwm_mod;

like I said, very little finesse, but sometimes simple is good :). But I can't test under rotating load (although I did fall of a 1A and 3A configured hoverboard, just much quieter than before!) - under static load it's very sweet.

Note: buf->dcl is the current read THIS interrupt - i.e. the current present during the last PWM (still need to understand the exact nature of this, exactly when it's read and over what period). This is one reason why our PWM values are set within the DMA interrupt, so we have an Option to influence them dynamically according to the dynamic current....

btsimonh commented 5 years ago

ok, so let's not be happy quite so quickly; i had a typo on left wheel (which was what I was testing) which did not increase the limiter. now i corrected it, it's not limiting as well... :( - will report in a few mins.

btsimonh commented 5 years ago

ok, fixed in PR.

define BLDC_LIMITER_DECREMENT 5

    if(abs_dc > electrical_measurements.dc_adc_limit) {
      if (m->pwm_limiter > BLDC_LIMITER_DECREMENT) {
        m->pwm_limiter -= BLDC_LIMITER_DECREMENT;
      }
    } else {
      if (m->pwm_limiter < 1024) {
        m->pwm_limiter++;
      }
    }

1 was not enough. 10 may be too much, 5 seems ok.

at 1A limit, wheel held stalled: Bat: 38979mV(0) Temp:72C(1632) L: Current:59mA Avg:124mA r1:8 r2:7 R: Current:959mA Avg:912mA r1:-212 r2:221

lalalandrus commented 5 years ago

it works! no slipping and i can feel the phase advance kick in. unfortunately i have not had a chance to test the serial port yet.

few things: 1/ the code doesnt compile as-is dont know how you tricked travis, but looks like BLDC_COUNT_PER_HALL is missing in the version that i downloaded. https://github.com/btsimonh/hoverboard-firmware/tree/sinsquash

2/ the test i did incorporated @EmanuelFeru controller with speed relative phase advance https://github.com/EmanuelFeru/hoverboard-firmware-hack

3/ extern void BLDC_controller_step(RT_MODEL *const rtM) attribute((section (".ramfunctions"))); i thought you mentioned that the execution was slower? this should be removed? https://github.com/btsimonh/hoverboard-firmware/blob/sinsquash/inc/BLDC_controller.h

4/ have you tried ramfunctions or "inline" on just the lookup table? https://github.com/btsimonh/hoverboard-firmware/blob/sinsquash/src/BLDC_controller_data.c

btsimonh commented 5 years ago

@lalalandrus - 1/ it's in the protocol repo. I've moved it's defn into the main repo now. But note that the companion to this PR is: (here https://github.com/btsimonh/bipropellant-protocol/tree/sinsquash), or using the PR https://github.com/bipropellant/bipropellant-protocol/pull/27

2/ integrated @EmanuelFeru controller with speed relative phase advance, + modified for static inline on the few functions,

3/ .ramfunctions removed (but if you check the .ld file, they were in flash anyway!).

4/ I removed const on the tables. This should mean they can't be in flash, and should be in initialised data (ram).

new version travising now.... (and again)...

EFeru commented 5 years ago

@btsimonh I am coming back to the current limitation function... :) Does it work well for you? I was testing similar algorithm on my non-hoverboard fork. It is not exactly what you implemented but very similar on functionality. I tested with a current limit of 2 A.

btsimonh commented 5 years ago

@EmanuelFeru - yes, I get a buzz or hum. You can imagine the waveform - (0.5% because 5 counts in 1024). as it rises and hits max current, it gets reduced by ~0.5%. But the waveform is likely on the way 'up', so this will happen every 60us as the waveform rises. At ZERO speed, we're commutating, so we will peak high and reduce, and we'll hit a stead state of a sawtooth of amplitude 0.5% of our pwm demand?

At high speed (let's say high enough where we have maybe 20 samples in 180 degrees), we will see a similar modulation, modified a little by the periods where the pwml/r drive is lower.

To make it smoother, we'd use an averaged peak current, I guess.

But then in some ways, it's good to have an indication that we're limiting?

p.s. I also get a marked increase in electrical noise (affects my serial), but then i've not done much testing under load before.

EFeru commented 5 years ago

Ok, good to hear, I thought I am doing something wrong.

I tried closed loop current control but it did not work as expected. Still the buzz effect. The high frequency loop 16 kHz, it does not help in this case, it is too fast, and current is too noisy. I logged the current with STM Studio.

lalalandrus commented 5 years ago

Ok, good to hear, I thought I am doing something wrong.

I tried closed loop current control but it did not work as expected. Still the buzz effect. The high frequency loop 16 kHz, it does not help in this case, it is too fast, and current is too noisy. I logged the current with STM Studio.

have you tried filtering the current?

define FILTER_SHIFT = 10

current_filter_reg = current_filter_reg - (current_filter_reg >> FILTER_SHIFT) + current_adc_raw; current_adc_filtered = current_filter_reg >> FILTER_SHIFT;

EFeru commented 5 years ago

yes, I did low pass filtering:

curl_filt = (1.0f - CUR_FILTER) * curl_filt + CUR_FILTER * ABS((adc_buffer.dcl - offsetdcl) * MOTOR_AMP_CONV_DC_AMP);

The filter you described I am not familiar with. What type is it? Even with the filter it was hard to do closed loop control. Because the current is very sensitive to the input pwm. So, 2 draw back, current very sensitve and noisy. If I filter the current, I over-correct, if I don't filter the correction is too noisy (the buzz effect). I have to work it out more. until now, I don't have a feasible solution.

juodumas commented 5 years ago

Note to all: PR #39 has been updated with graceful current limit. Looking forward to hearing results from those who can easily test under load

The updated PR #39 performs fantastically! Thank you @EmanuelFeru and @btsimonh. I have driven 5km with 15A current limit. Got 20kmh average speed and 36kmh max speed and these are great results. I am very eager to test battery use efficiency this week - it is the most important metric for our projects (a tricycle and scooters).

At current limit there is a low buzz and vibrations, but current limiting is now very smooth compared to what it was in the original firmware or the previous version of the PR.

btsimonh commented 5 years ago

nice feedback from real loaded use! - thankyou. Maybe @EmanuelFeru will give us an even nicer idea; what I did was rather raw and 'back of a fag packet' based, so I'm glad it performs so well :).

EFeru commented 5 years ago

@juodumas @btsimonh I am happy to hear that it works nicely! While I was thinking on the current limitations function... I was thinking again to FOC motor control... I am thinking to give FOC a try because it brings so many advantages:

EFeru commented 5 years ago

FOC needs 2 measured phase currents. However, there is a missalignment on pinout in software defines.h vs schematic. sceme

Can someone check this? And tell me which one is right. It is very important for control.

lalalandrus commented 5 years ago

@EmanuelFeru Ill check tonight and also start work on the integration of the sensors.

Also what do you think about DTC as a lower calulation complexity to VOC, it does take more characterization of the motor though and not as precise low speed. https://en.wikipedia.org/wiki/Direct_torque_control

EFeru commented 5 years ago

I see DTC needs both phase voltages and currents. As far as I know, we don't have measurements for the phase voltages, do we?

lalalandrus commented 5 years ago

@EmanuelFeru i only had time to check one but pin 52 hall confirmed right motor and the closest opamp was connected to pc5 which is supposed to be left on the schematic in short it seems that the defines are correct and the schematic at least based on a data point of one

and yes unfortunately we dont have voltage measurements. we only know what the pwm output is and to correlate it would be pretty difficult to characterize

EFeru commented 5 years ago

@lalalandrus thanks for the info. Just to double check. Can you tell me, what phases are measured for each motor? Because in the schematic it says:

Is this correct? Or the sofware is correct which says:

EFeru commented 5 years ago

Finally, I have the correct current mapping, thanks to STM Studio logging (amazing tool). Data below is real measured data (not simulation).

LEFT motor (long wired motor)

Motor_LEFT_SIN3

Mapping: Phase A current: adc_buffer.rl1 (flipped) Phase B current: adc_buffer.rl2 (flipped)

RIGHT motor (short wired motor)

Motor_RIGHT_SIN3

Mapping: Phase B current: adc_buffer.rr1 (flipped) Phase C current: adc_buffer.rr2 (flipped)

Important Note: The sign of all phase current measurements is flipped:

lalalandrus commented 5 years ago

sorry i have not had the time to look at the sensors. the data looks really good what are your plans with the data?

also i tried implementing a few simple integral solutions, but all of them ended up being very unstable near the zero point, i will need some time to think of a better method while avoiding floating point math, maybe i will have to just bite the bullet.

EFeru commented 5 years ago

This data is just to find the correct mapping. It is from the sinusoidal controller that i developed in my repo. This is the way it works.

Edit: Floating point cannot be used for motor control at this frequency since the STM32 does not have a Floating Point Unit and it takes way too much time to compute. In case you need to use float and there is no way to avoid it, they need to be converted to fixed point. Then in works.

Edit2: I was interested in the mapping for FOC control. I want to give it a try.

EFeru commented 5 years ago

@lalalandrus @btsimonh @p-h-a-i-l @TomTinkering @AntumArk I need your support in this.

You saw that that there are some spikes in the current measurement. image

This is because the following reason: "As it is shown in Fig. 7, in order to read the current you need to know then the lower switch is in conduction and more specifically when the current flows thru the MOSFET." Explained in this article: https://community.nxp.com/thread/469445

Do you know how to detect that the MOSFET is conducting and only then perfrom the ADC read? Those spikes are causing me troubles in FOC because I get a 0 reading, which leads to high error as input to PI controller.

btsimonh commented 5 years ago

this is why the BLDC is run from the DMA interrupt. The theory is that the reading of the current is synchronised with the PWM pulses such that it's always read at a known time (maybe the ADC read is started at the mid-point of the center aligned PWM?). The current reads take a small amount of time, then when they are read, the DMA interrupt fires?

EFeru commented 5 years ago

I also found this here ADC read Page 46

image

@btsimonh

this is why the BLDC is run from the DMA interrupt. The theory is that the reading of the current is synchronised with the PWM pulses such that it's always read at a known time (maybe the ADC read is started at the mid-point of the center aligned PWM?). The current reads take a small amount of time, then when they are read, the DMA interrupt fires?

If this is true, we should not see "0" sipkes I think. Is there a way to check if the settings/configurations are correct? I am not an expert in this .. :(

lalalandrus commented 5 years ago

@EmanuelFeru it is what @btsimonh says The lower mosfet is turned on when the pwm "output" is zero (complimentary outputs). Since we intitate the read when the PWM resets, then thus the current measurement happens when the output is zero as the PWM counter has not yet pass the setpoint. Obviously there is the question what if the setpoint is '0'. There is a clamp that sets the limit to the PWM from 10 to 990 (pwmlimit - 10).

I dont know how the center aligned pwm affects the output, pwm counter relationship

EFeru commented 5 years ago

According to your explanation it should be ok then, however measurements say something else. And is really happening that I get "0" readings.

In my opinion, the ADC trigger needs review. In setup.c, for ADC1 trigger I see that there is an ADC_EXTERNALTRIGCONV_T8_TRGO, however for ADC2 trigger is ADC_SOFTWARE_START. I am puzzled on 2 things:

btsimonh commented 5 years ago

not sure, but I think the ADC is in some form of 'dual' mode, so the 2nd trigger is irrelevant? TIM1 and TIM8 are synchronised....

lalalandrus commented 5 years ago

try changing it to TRG0 and see what happens? maybe it is legacy inheritance from the niklaus repo

TomTinkering commented 5 years ago

I might be wrong, but shouldn't we get very high values rather than 0 when the synchronization would be wrong? The measurements uses the low-side fet as a shunt resistor. If this FET is open the voltages should be much higher right? Do you get absolute 0 values for these ADC's? In that case it might be a conversion issue, as in the conversion did not complete when you request the value.

EFeru commented 5 years ago

Can you explain why the value should be high when MOSFET is disconnected? Because in this situation the ground is connected to shunt resistor so 0. I need to check the data if it is absolute 0, but from what I rember (few days ago) when I measured, it was 0 absoulte. I will double check.

TomTinkering commented 5 years ago

There is two current measurements on the STM32 board:

  1. Current Shunt: This gives you the summed current of all the phases, for this the voltage over the Left and Right motor shunt are measured. Apart from some noise, timing is less relevant here (I think?)

  2. Phase Current: Here the low-side Mosfet ON resistance is used when the Mosfet is enabled/conducting. If this mosfet is disabled/not-conducting, the current measurement circuit will see either the positive battery voltage, or the phase voltage, both likely higher than the voltage over the ON-resistance. Although I guess the phase voltage could be negative, in which case you might get a 0 reading. But even in that case, the output of the current measurement opamp will be clipped to 0, but it rarely actually is 0. Typically, even for rail-to-rail opamps, you still see a few mV, which would translate in a very low ADC reading, but not 0.

But again, I might be wrong here, I haven't really looked at it in detail. If you want I can double-check this on my hoverboard this evening.

EFeru commented 5 years ago

That would be nice if you can check. I will also check again this evening on the exact value.

Initially, I thought is the STM Studio aquisition issue, so I did not pay attention. But when I plugged the FOC controller, then these 0's are really there. And they are more frequent at high PWM values.

EFeru commented 5 years ago

I did a new current measurement. It obviously does not look clean... Not 0 absolute though. I don't know what can be the issue.. because my DutyCycle command looks ok. Not many points, but looks ok. The problem could be... pwm frequency, DMA frequency, the ADC read that takes too long... not sure.

One thing is clear, with such current measurement FOC is not possible. The current measurement needs to be fixed first.... and FOC is so nice.. in torque mode at 0 torque, wheel is free spinning with no drag, even at standstill, which I like a lot :)

image

lalalandrus commented 5 years ago

Is your new code checked in. Maybe I can play around with it.

EFeru commented 5 years ago

Nope, I work locally. It is a very basic FOC on one wheel, torque mode. Do you want to try it? I can send you a zip quickly. I am using one potentiometer to drive the wheel.

TomTinkering commented 5 years ago

This is the output of a current measurement channel:

current_out

You can see a small negative dip right at the edge when the lowside mosfet is switched ON.

Also, all PWM channels are in sync and both ADCs are setup to first sample motor A, and then motor B. This means that there is a good chance for motor A that you might be sampling right on the edge of this dip. Especially at higher currents this dip will be more pronounced and potentially cause more problems.

One easy test could be to first choose a non timing critical channel to sample (battery voltage, DC current, temperature, dummy channel whatever), before sampling the phases.

EFeru commented 5 years ago

I will try now. I will change the order of sampling in setup.c

EFeru commented 5 years ago

@TomTinkering , I just changed the order in setup.c like below (for both motors): I put the temperature reading first. The thing is that all the ADC readings are not ok, even my potentiomenters give wrong reading. Do I miss something?

  //temperature requires at least 17.1uS sampling time
  sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;

  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;  // internal temp
  sConfig.Rank    = 1;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
  sConfig.Channel = ADC_CHANNEL_14;  // pc4 left b
  sConfig.Rank    = 2;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  sConfig.Channel = ADC_CHANNEL_0;  // pa0 right a
  sConfig.Rank    = 3;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5;

  sConfig.Channel = ADC_CHANNEL_11;  // pc1 left cur
  sConfig.Rank    = 4;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  sConfig.Channel = ADC_CHANNEL_12;  // pc2 vbat
  sConfig.Rank    = 5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

Edit: Something is fishy here... :)

p-h-a-i-l commented 5 years ago

Did you increase the sample frequency? If you are switching very fast between analog channels, there might not be enough time to charge and discharge the sample and hold capacitor.