-
Notifications
You must be signed in to change notification settings - Fork 5
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
motor does not reach max speed like old Gen2.x binary #5
Comments
I am still not sure if the sensor alignment is good. One way to check this is described here Do you see/hear differences in how the motor spins in each direction with both binaries? |
you can hear in the video for yourself but no the sound is the same. Good night ! i am tired now and i still want to finish the rewatch of https://en.wikipedia.org/wiki/The_Hunt_(2012_film) |
@RoboDurden can you please do a quick test Add the following to the plaformio.ini file
It compiles the binary with the highest level of optimizations possible, and if my theory is correct (slow interrupt), it should speed up the motor by a small but measurable amount. Meanwhile I'm looking at the interrupt code |
@robcazzaro , I would rather do that tomorrow when I have arrived at my solar warehouse. I have already packed the test setup in a box. And my notebook is occupied with 3d printing.. |
If the effect is small i will need UART rpm logging and I have yet to setup that again. |
Of course, I didn't mean "right now" 😉 whenever you have time, no hurry I saw a few Android apps that can measure RPMs if you stick a white tape to the wheel, maybe that is easier than enabling UART. I mean, UART is good to have, but an RPM meter app might be a quick way to see if there are differences |
@RoboDurden I understand the interrupt handling code pretty well now, and there are a few hacks that would speed things up. But it requires a few changes to a few files and possibly breaking compatibility with the official GD32 core. If, whenever you have time, you can confirm that compiling with -Ofast (the platformio.ini changes above) makes a difference, I will send you the modified files. Otherwise there's no point in having you waste time with the changes. |
Okay that were my changes:
I did not hear a higher speed and android apps do not use camera but inernal IMU and you have to put your phone on the turntable.. But current was sligtly less. before 590mA and 550mA (left/right) and with your requested changes: 550mA and 500mA So i recorded the audio: www.robosoft.de/forums/rpmsound.mp3 So yes, your requested ini changes result in a 476 Hz / 456 Hz = 4.3% speed increase, This was a long sunday for me. Got 3.5 kW old solar panles for free: Good night and good luck :-) |
That is really good news @RoboDurden. We can confirm it's a IRQ overhead issue. I will send you a couple of file to try before my end of day (overwriting the existing one for now). Thanks for doing this, and clever use of the audio, well done! Just FYI: the GD32 Arduino core has some weird behavior with the interrupts, I opened an issue in their repository to be sure CommunityGD32Cores/ArduinoCore-GD32#105. TLDR: Also opened CommunityGD32Cores/ArduinoCore-GD32#106 to see if we can speed up the IRQ handler without having to change the GD32 Arduino files Bottom line: as long as we know what interrupts we need for GPIO pins change, we can easily speed things up, and I have working code. Just want to finish testing it |
Ok, here are 3 files to replace whenever you have time @RoboDurden C:\Users[yourname].platformio\packages\framework-arduinogd32\cores\arduino\gd32\gpio_interrupt.c Please note that one is in the platformio GD32 platform directory, the other 2 in the project directory. And please check that the changes are compiled. Incidentally, the VS Code UI sometimes gets confused by all the nested defines, so in the gpio_interrupt.c file, the portion following `#elif defined(GD32F3x0) || defined(GD32F1x0)" is shown as disabled (greyed out), but it's active and gets compiled (GD32F1x0 is defined). It's a VS code UI bug. That one is the file I hope not to have to change thanks to this Please also keep the -Ofast optimization enabled for now. It will make debugging harder, but a 4% gain is non-trivial. There are further optimizations possible in the Hall sensor code (updateState()), but those require more thinking. The ones I did for now are pretty safe. If anything is unclear, just ask. I tested everything and it seems to work as expected, but it would not be the first time I screw up 😊 |
BTW: I have been looking at other ways to potentially speed things up, but would require changes here and there. If the interrupt enhancements are not enough, I have a few more things to try. But the GD32 at 48MHz is slower than most other processors recommended for SimpleFOC, so we might have to hand-optimize more. To be fair, the FOC algorithms are more compute-heavy than the type of control implemented by most hoverboards |
Don't be pessimistic, I am sure we will get there. We for sure will have to optimize because of the arduino-gd32 and simplefoc overhead. |
Foc is actually slower than trapezoidal or sinepwm, even with EFeru's firmware. |
Bummer. And thanks @RoboDurden for your additional tests. Clearly, even if the interrupt code was inefficient, it did not consume enough cycles to be a meaningful difference. Was a low hanging fruit, but not the right one. There might be other opportunities (for example the sensor.updateState() function is over complex and calls bloated functions (like micros() that is needlessly complex, would be easier to re-implement using DWT_CYCCNT and knowing that the core clock is 48MHz). @RoboDurden if you try following the hall sensor code, you will see that a simple 3-line GPIO read in the gen2.x software is more than 50 lines, called each hall interrupt (open and close). SimpleFOC abstraction makes things more complicated (SimpleFOC can for example use SPI encoders for angle control, and the same code works with hall sensors) As for the speed difference between boards, that is 0.7%. We are running the GD32 using the internal oscillator, which has a 1% accuracy, so that could explain the small difference you are seeing. The next step unfortunately it's profiling the code. The best way is to use SWD/SWO, but if you have a cheap STLink Chinese clone, the SWO pin is not enabled. It can be enabled with https://lujji.github.io/blog/stlink-clone-trace/ and it's well worth it, but time consuming and you need to be good at soldering on the small STM32 pins. With that enabled, you can ten use the STM32CubeIDE from ST to run the SWV Statistical Profiling (it's under Window, Show View, Other That would get you a complete profiling profile, but might be too slow over SWO and pretty hard to get running. Otherwise use macros and DWT_CYCCNT to measure execution cycles by function, which is probably the best way to go for us here. There are even available implementations like https://github.com/Serj-Bashlayev/STM32_Profiler. DWT_CYCCNT is a standard M3 core register, so available on the GD32. That library prints using SWO (once again), but it could also print to a debug output after a fixed time profiling. Or it would be easy to enable DWT_CYCCNT once at the beginning, create a structure for the various functions we use, store the DWT value upon entering the function, subtract the value at the end from the start, and add the instruction count to the right element of the structure for that function
Then stop the code after, say, 1000 loops and print the values in the structure. the DWT counter can go up to 4294967295, so at 48MHx, as long as the profiling session ends within 89 seconds of the DWT->CTRL |= 0x1 instruction, we won't have to worry about handling the counter overflow Alas, I can't profile on the Bluepill dev board. Without a motor and hall sensors, the code won't work as expected. If you think you can run the profiler, I'd be happy to then analyze the code and see where we can gain. If you can't, I'll have to set up my only board and hope for the best |
Well all the many lines you wrote here about such a profiler does not make me wanting to do it at all :-/ If there are some delay parameters we could program an Auto-Tuning that optimizes rpm with a steepest descent over all parameters.. I would prefer some direct approach instead of blindly increase analyzing.. Good night for today's |
Profiling is not blindly increasing analyzing 😉. It's looking for inefficient areas of the code that are holding back performance. If we are performance limited now, it's only going to get worse once we add the more complex elements of the SimpleFOC code. Insufficient processor performance is one of the most common causes of problems reported in the SimpleFOC forums. You can't examine external signals to see where the problem is. Let's assume we find that the PWM waveform is 3% delayed compared to other code. How would we go about changing that, if we don't know what's causing it? The main take away from my previous comment is that measuring performance is as easy as adding 2 instructions, one at the beginning, one before the exit of each function, plus half a dozen at the end of setup(). If we use macros for the beginning/end of each function, we can enable/disable perf measurement during development. And that will benefit everything we do and help optimize in the future. That is the clever way to see what's happening, IMHO. But I'd be happy to be proven wrong, if you can find something else that works. And, yes, it might be just a hall sensor alignment, that would be great. I'll hold on doing anything wrt profiling until you fully evaluate that option. |
Just a reminder. |
I simply am the totally wrong guy to analyze bad code instead of writing good code right from the beginning :-/ Speaking of bad code, putting a time critical function in to loop() should be an absolute no-go. It only needs a delay(1); to crush the motor output:
I do not really understand The Gen2.x code comments. bldc.c:
Which is called by an interrupt handler in it.c:
1s / 31,25us = 32 kHz, not 16 kHz. But nice idea to trigger the
But i do not understand how As all three phase channels should be absolute in sync, i also do not understand why three different timers are needed. But maybe all 4 (or 5 for the C8 variant) 16-bit timers are in sync as they are based on one internal counter.. What i like about the Gen2.x code is that it is plain simple. One I guess we are free to overwrite some simpleFOC classes and replace code the object oriented way. |
okay, max speed is even higher (545 Hz left and 555 Hz right) then Gen2.x when setting these two init values to 20 instead of 26 (Volt):
But the current high rises to 850-900 mA instead of the Gen2.x with less than 500 mA :-/ Update: It think these two parameters get used here:
|
short note to @Candas1 , that add_RTT_task.py needs Nice to have log output without another cable. But the sensor.getVelocity() output is not smooth at all:
|
@RoboDurden the sensor is not filtered in SimpleFOC and very "spiky". There is a new smoothedsensor class being developed for that reason. The folks running the forum recommend using motor.shaftVelocity() instead of sensor.getVelocity(). That is filtered and smoothed As for your question on the clocks, the GD32 (like STM32) have multiple buses and clocks, each with PLLs and dividers, and it's a puzzle trying to understand how fast each bus runs. for STM32, there is a function in the STM32CubeIDE that helps calculate each and every PLL and divisor and gives you an idea in MHz or kHz. I haven't checked, but if Gen2.x is running at 72MHz, all bets are off with clocks and timing accuracy and I doubt it's. But if you want to truly understand the actual values, you need to look at the clock initialization and note each and every value being configured, then you can know the actual speed of a specific bus or component (in some cases, the speed can be half of the clock, depending on the edge polarity). Just to give you a sense, here's the clock tree for the GD32F130 (page 79, User manual) |
@RoboDurden one more comment re:
Adding a delay(1) is really a lot in what should be a tight loop. A loop() with only a single instruction of delay(1) runs at less than 1kHz, which is way too low for SimpleFOC. So adding a huge delay of 1msec in that loop will never work. For a more relevant test you might want to use delayMicroseconds(1); If that causes problems, that means that the processor is not fast enough with the current code. If even something like delayMicroseconds(10) causes no problems, that means that the code is fast enough and the problem is not code efficiency (but settings and hall alignment) The goal is to run the loop at at least 10kHz to have a smooth performance. Alas, SimpleFOC is not timer-driven, but runs in a tight loop which is a bad way to write time-critical code. From the Gen2.x code I can't understand how the GD32 clock is initialized. Usually there is a call to SystemClock_Config() to initialize the GD32 clock, but the Gen2.x code uses a non standard way.
I don't have Keil, so can't quickly test. I could port the code to VSCode, but it will take some time. @RoboDurden If you have spare time, could you print the value of SystemCoreClock ? That variable is the GD32 clock (48000000 or 72000000). Knowing it will help making comparisons to the gen2.x code |
We are free to move loopfoc and move functions to interrupts if we want to https://community.simplefoc.com/t/simplefoc-theory-timing/2310/2?u=candas1 |
Good point @Candas1. Just a note that if we do that, we need to change how the hall sensor code works and disable those interrupts. Basically read the hall sensor upon receiving the timer interrupt like Gen2.x does (which would also make the velocity calculation much simpler and faster compared to the current one using the time between changes to calculate velocity, which as Robodurden noticed is very spiky). And make sure that the ADC code doesn't rely on interrupts either (Gen2.x uses ADC interrupts, but there are ways to start the ADC without interrupts). And look at how USART uses the interrupts in the Arduino code Otherwise we have to deal with re-entrant interrupts and that causes all sorts of problems. |
Yes we will trigger inserted adc at timer update event. |
Some good advices were shared just now |
In honor of @flo199213 who ported the good old Niklas Fauth hoverboard firmware to the split boards, we should refer to "Gen2" as my "Gen2.x" only added odometer, better serial and different board layouts. Our main loop of the simpleFOC fimrware normaly takes about 110 us but max 1122 micro seconds, i guess when debug output gets sent:
So 0.9 kHz some times is not acceptable and normal Arduino users will load the loop() with lots of i2c modules and even wlan or bluetooth anyway. I think we should move on with low-sde current sensing because that will come with better timing than the hall sensors anyway ? |
Yes we can use the adc interrupt, but we don't need the dma. Let's implement, we will see what needs to be optimized. I think segger rtt shouldn't introduce any delay, it's just reading from memory. |
Sounds like a good plan. Please assign to me any low level issue and I'll take care of it. @RoboDurden thanks for verifying that Gen2 actually runs at 72MHz, which is a very high overclocking (50% above rated). While that might be ok for basic core math and some GPIO, more complex functions like PWM and especially ADC will be impacted. We should keep running at the rated 48MHz, even if that has a performance impact @Candas1 RTT does have a small performance impact when sending data, but much less than any other channel (like UART), since it uses the existing SWD protocol. Having no debug is the best option for speed, but if any output is needed, RTT is by far the best option |
Feel free to contribute to simplefoc if you think your interpolation is better. That still doesn't clarify why gen2.x was more efficient without interpolation. |
Well this FOC code is still a black box to me:
But i guess it is very sensitive to the motor angle, whereas Gen2 directly and non-tunable "block commutates" the three phases according to the hall sensors - not much to be done wrong. I still have not undestood why setting
I think that lowering the
But that is just my guess. |
The chatGPT code was a misfire. Did only produce overflow floats.
https://www.codesansar.com/numerical-methods/lagrange-interpolation-method-using-c-programming.htm But adding mor then 10% of that 4th polynomial interpolation did block the motor :-( I also noticed that the execution time of that code affected the motor behavior (additional 30 mA) even without using |
The way I understand it, SimpleFOC needs to know the power supply voltage value when calculating currents (you can set the motor impedance) and to optimize voltage and current in each phase. Then you use motor.voltage_limit and driver.voltage_limit to protect the motor especially when starting to protect the motor and board. Let's say that you have a 10V power supply and set driver.voltage_power_supply to 10 and driver.voltage_limit to 5V. The PWM will never produce more than 50% voltage when the algorithm would output 100%. If you set driver.voltage_limit to 10, now at max phase voltage you will get 10V It looks as if, by setting the power supply value lower than actual, you are driving the PWM more fully. Do you have motor.voltage_limit and driver.voltage_limit set, by chance? Try printing those values and see if they are the same or lower than the power supply value Keep in mind that speed is only one aspect. For my uses, for example, I will need the motor to move very slowly (<10 RPM) with constant torque and be able to hold position with the same torque. The value of using SimpleFOC, for me, would be that once a board and motor parameters are optimized, it can be used in many different ways equally easy, without having to change the core code, just change settings in the setup() |
I would check the 3 voltages with this, maybe we see what happens |
I am wondering if at high duty cycle the values are clamped, so the sinusoidal is flat. |
The best way to verify that would be to use a logic analyzer connected to the processor PWM pins (it's enough to only use 3 pins, like the 3 high phase pins), and once the motor is spinning at constant speed, capture enough pulses to see the PWM signal. It's easy that way to see a clipped signal, as a long sequence of 1s |
Thanks @robcazzaro , having two
The 25.2 Volt is my 7s 3.6 * BAT_CELLS, the 12.0 is still the DEF_POWER_SUPPLY Indeed, in
But I think there should not be a I also do not (want) to undestand why using voltages when in the end we factor and clamp it down to [0..1] anyway. But i think it is too late to change that now. Do not really have the happiness to start arguing with the simpleFOC community.. |
Because I don't aim to maintain my own branch of simplefoc except the GD32 drivers. |
I don't pose a problem if I can't offer any solutions
So no solutions to offer and therefore no happiness to pose questions to other people. |
@robcazzaro i still do not really understand why you don't buy some used hoverboards locally. I do not know all the US websites to buy used items but already facebooks lists multiple items: https://www.facebook.com/marketplace/seattle/hoverboards?maxPrice=20&exact=false |
i am not really making progress with my linear interpolation. Has taken all day to verify that my angle prediction works quite good, but the motor does not like the "better" values. With Gen2 however, the phase pwm are simply directly calculated from the 6 (8?) possible hall combination.
So nothing can get "out of phase" over time. With simpleFOC however, the angle increments with every hall step, is then mapped to 360° = [0.0 .. 2pi], some offset is added I think there should be an ongoing closed loop that corrects the @Candas1 , how did you obtain |
Please check those comments: |
@RoboDurden SimpleFOC is a bit hard to understand and has quite a few quirks. On the other hand, it's solid code that works for a variety of projects and hardware. Trying to hack around its inner workings without understanding why certain things were done a certain way it's a recipe for frustration. Many of the issues we are facing are known by the SimpleFOC team and work is happening to address them. If we think something could be done better, the right approach is to engage with them and suggest changes. Most of the times, their answer will explain why their approach is better and the side effects of doing things differently. Every time I suggested changes, they were incredibly open and responsive. You say So, while I appreciate your desire to quickly get the code to a working state for higher speeds, that is not an approach I think will work long term. As I mentioned before, there is already a solution for a smoothed hall sensor that works at any speed. It might be suboptimal in certain cases compared to your approach, but works in general. IF you think your approach is better, I'm sure that the SimpleFOC people will be interested in hearing about it and discuss it with you. |
Thanks @Candas1 for the links, i had searched for them but did not find them in our too long issues. That would give a "better" result in BLDCMotor:move()
Then maybe the BLDCMotor::alignSensor() will result in a zero_electric_angle that at least will also work like your 2.09 according to https://forum.esk8.news/t/hoverboard-foc-motor-settings-vesc/25343/4 : update2: If these additional 4 parameters are the problem then i guess i will have to code my own optimizer :-/ |
"If the top speed is higher in one direction than the other, use sine modulation and adjust motor.zero_electric_angle until both directions are equal (it’s set automatically by calibration, but sometimes needs manual adjustment to correct for imperfect sensor placement)" |
Spent nearly most of Sunday to find better motor settings but haven't found anything better then the 2.09 that Candas chose.
The
Today i have built myself a Gen1 test setup. Will try to flash our simpleFOC software to it. Then i can compare with the noise and max speed of the EFeru FOC. Would be nice to have at least overall current adc. Then i could compare the power with speed to judge the efficiency of the different firmwares. If i remember correctly, the EFeru firmware outputs speed in rpm. SimpleFOC velocity seems to be angulare frequency ( 2 pu / f ). As Candas will not accept my pull request, @robcazzaro if you want to and know how to start a SimpleFOC/src/current_sense/hardware_specific/gd32/gd32_mcu.cpp , you could download my preperation from my fork: https://github.com/RoboDurden/Split_Hoverboard_SimpleFOC But i guess, Candas will be back from holidays in about a week and if he wants to do it, we can happily wait :-) |
had really bad sleep last night and am very tired today.
with the simpleFOC angular speed.
The EFeru max rpm was 368. Divided by my 26 Volt confirms the 14.15 KV of my old youtube video. But maybe a factor 2 ? But i do not find that in the simpleFOC code, unless we should use pole_pairs = 30 instead of 15:
But that should have the oppsite effect and half the 20.40 to 10.20 for the simpleFOC velocity :-/ |
Maybe there is an issue with time measurement. |
ah my fault. Now at the end of this day my mind gets a bit clearer.
Now with 1.0 i get a max speed of 44 and an average max speed of 38.0 at 1250mA/1200mA:
The EFeru_FOC gave an angular velocity of 38.5. With 90% of my linear interpolation (prediction) i get a max average of 65 at 1000mA/1100 mA:
Now that is a 65/38 = 71 % speed boost with less current. Maybe my current limit was too low at the last tests. P.s. i tried to white list my Arduino_FOC lib source and push it to my fork, so i can link to my code changes.
Github Desktop then already lists the folder as having changed, but they do not get pushed to github and no files are listed/found as having changed. |
If it's higher than rated KV you're probably doing field weakening. |
This probably explains why the motor would slow down at higher target values. |
Well this does not explain why my simple linear prediction makes a 37% speed boost. Here with only
first column = KV [rpm/V] Usually the predict times change by loopFOC[ms] but sometimes my loop() takes up to 1ms. Here when the motor slowed down:
My linear extraplation/prediction works nicely and i don't understand why that should have a field weakening effect.
The My linerar prediction works and i can not see how that should be bad. Here max speed without my linerar prediction:
Of course the "predicted angle" stays at the old angle for all loopFOC calls until the next hall step occurs :-( 17.38 / 12.71 = 37% speed boost Yes i know that we should not get a KV of more than 15 for our bldc motors.
Only by correctly predicting the current hall position :-/ I should test the motor under load.. Don't really know how to do this. P.S. i do not like |
So stm32 test setup is running and i can compare Gen1 vs Gen2 with the same simpleFOC software. And we really need a 16 kHz timer to move loopFOC() out of the main loop(). I can not log more output via UART on the Gen1 without the motor spinning poorly because loopFOC does no longer get called often enough. Gen1 ----------------------
Gen2.0 ----------------------
The Gen2 GD32 code is about as efficient as the STM32 code :-) Will have to do more test. |
I switched back to trapezoidal, sinePWM was a mistake without interpolation, I thought it would just perform like trapezoidal and it was less noisy. |
Good to hear from you Candas :-) |
Feel free to experiment if you want to learn. |
hm, no answer to my question. |
I never said I was going to work on an interrupt, so feel free to experiment if you think what we need now is an interrupt. |
continued from #4 (comment)
I might be easier to slow down the hall sensor reading artifically and see if that has an effect on the motor max speed :-)
The text was updated successfully, but these errors were encountered: