sim- / tgy

tgy -- Open Source Firmware for ATmega-based Brushless ESCs
http://0x.ca/tgy/
688 stars 387 forks source link

Bad behavior with comp_pwm #93

Closed Luisonson closed 8 years ago

Luisonson commented 9 years ago

Hello, I was doing some tests with the esc sn20a, the motor turnigy 1230, and a little encoder. The first test was for comparing max rpms, and the acceleration of the motor with different parameters. And the first thing I tested (comp_pwm), was a complete failure :( The motor starts accelerating and at 20-25% of the speed stops accelerating for about 30ms and then continues accelerating till the 100%. The sound was super clear, but the encoder was the perfect prove.

I have attached the two graphics where you can see the issue clearly. x-> total time in microseconds y-> time to count 8 encoder-pulses in microseconds (speed of the motor).

0-100 comp_pwm

0-100

Thanks a lot for your great work!!

sim- commented 9 years ago

Hello! Interestingly enough, I was just looking at something like this for another reason. Is it 100% reproducible?

Could you try if it does NOT occur with COMP_PWM=0?

Also, could you try if it goes away with lower POWER_RANGE (higher PWM frequency), like when setting POWER_RANGE to 400?

You may also want to lower MIN_DUTY to 6, or closer to 0, to reduce the minimum power for your testing, but this should be unrelated.

If it is the same as in my case, I need to hook up the scope with MOTOR_DEBUG and probably ZC sense visualization, and figure out what is happening.

Luisonson commented 9 years ago

Hello! Thanks for your answer. 1.- Yes, it is 100% reproducible. I'm new so before asking, I double checked everything. I have used rapidflash and also kkflashtool. In rapidflash I flashed the default, and everything is ok, and just checking the comp_pwm and the "bad-behavior" appears. The same with kkflashtool... if I revert the comp_pwm (COMP_PWM=0), then everything is ok again.

2.- for the power_range... Do I have to change this line? 400 is fine? (as I was testing the firmware for a robot... i prefer to increase than decrease the power_range... hehehe) ;.equ POWER_RANGE = 800 * CPU_MHZ / 16 + MIN_DUTY .equ POWER_RANGE = 400 * CPU_MHZ / 16 + MIN_DUTY

3.- Is ok to change this value from 56 to 6? ; Minimum PWM on-time (too low and FETs won't turn on, hard starting) ;.equ MIN_DUTY = 56 * CPU_MHZ / 16 .equ MIN_DUTY = 6 * CPU_MHZ / 16

It's 1am, so with your answer, I will check it tomorrow points 2 and 3 ^_^

sim- commented 9 years ago

Yes, the POWER_RANGE can be set to nearly anything. It is the PWM interval in ticks, stored in 16-bit registers. PWM is implemented in software and so there are some additional cycles, but the PWM frequency is mostly CPU_HZ / (POWER_RANGE + MIN_DUTY), and so works out normally to about 16000000 / (800 + 56) or ~18.7kHz. Lowering it to 400 will mostly double the frequency. I have tested intervals around 4000 - 200. Don't set it too low or high. I'm mostly curious if it stops the issue you are seeing.

sim- commented 9 years ago

By the way, is that the Turnigy 1230 4500kv inrunner?

sim- commented 9 years ago

...and which encoder, if I wanted to get the same? :)

dragonet80 commented 9 years ago

I can tell you, yes, it is an inrunner motor. But better if Luison shows you a photo of motor and encoder. El dia 29/09/2015 2:31, "Simon Kirby" notifications@github.com va escriure:

...and which encoder, if I wanted to get the same? :)

— Reply to this email directly or view it on GitHub https://github.com/sim-/tgy/issues/93#issuecomment-143910434.

Luisonson commented 9 years ago

Hello, In my robots I use the as5040, it's one of the bests, but have the limitation of 30.000rpm and is not cheap. As right now I'm just checking if brushless motors can fit in the robot, I have made one DIY, and the best is to use a qre1113, a piece of paper and an Arduino. (not the as5040, a special magnet and the cortex-m4 I use on the final designs).

img_20150929_180847

Yes, it is the turnigy 1230

I will check now the new configuration you ask me yesterday.

Luisonson commented 9 years ago

I made two new firmwares, both with 400 power_range and 6 min_duty. But one with comppwm=1 and the other =0... Here the results :(

400 0-100

Luisonson commented 9 years ago

With the power_range in 1000 or 2000 the result is the same or very similar.

sim- commented 9 years ago

Very interesting! Thank you for this data. I will have to do the same. I've done similar things before on outrunners with an LED held in an oscilloscope probe and a flashlight aimed beside it, both pointing at a motor bell with white tape applied to half of the bell. This works until the tape flies off. :)

I was going to ask if the red LED blinks when this happens, but that ESC doesn't have any. You could add one to the SCK or MISO pads or something and define it in the .inc file, but that's not going to fix it for you, only show that it's taking the demag_timeout or wait_timeout recovery paths.

What is your ESC input command for this test? Idle to full power? Or slow speed for some time, then full power?

Luisonson commented 9 years ago

I have a dso-nano... so I can "see" those pins without a led ^_^ Which lines I have to add?

The code goes from Idle to full power My code is very simple, here it is:

include

unsigned long tiempo=0;

Servo miservo; //Para el control del esc, con el firmware modificado lo cambiaré a analogWrite q es más óptimo boolean estado_ant=false;

unsigned int revo[600]; //Array donde se guardarán los resultados int z=0;

void setup() {

tiempo2=millis();

miservo.attach(6); //Inicializo el pin para la señal de servomotor y que genere la señal miservo.write(45); //Coincide con el 0 por defecto del esc delay(5000); //Me doy margen para alimentar el esc y que detecte la señal de servomotor

Serial.begin(115200);

}

void loop() {

for(z=0;z<600;) { miservo.write(155); //Aproximadamente 100% del esc

tiempo=micros();
if(analogRead(5)>100) estado_ant=true;      //Inicializo el estado del sensor
else estado_ant=false;

/*El siguiente bucle deberá de ser lo más optimizado que se pueda, ya que cada microsegundo cuenta y arduino no va precisamente sobrado*/

while(z<600)
{
  pulso=analogRead(5);              //Leo el sensor del "encoder" casero

  if(pulso<70 && estado_ant==true)
  {
    estado_ant=false;
    revo[z]=(micros()-tiempo);      //revo es unsigned int (0-65000) y tiempo es unsigned long (32bits), pero al ser la resta
    tiempo=micros();                //y ser solo el tiempo entre cambio de blanco a negro del encoder... irá bien, más rápido en operar
    z++;                            //y nos permitirá guardar 600 valores en vez de solo 150.
  }
  else if(pulso>100 && estado_ant==false)
  {
    estado_ant=true;
    revo[z]=(micros()-tiempo);
    tiempo=micros();
    z++;
  }
}

}

miservo.write(0); //Para el motor y saco por pantalla el resultado for(z=0;z<600;z++) { Serial.println(revo[z]); } delay(5000); //Me doy tiempo para desconectar el esc

}

Luisonson commented 9 years ago

Good point!! I did more tests... and the problem is when the motor goes from idle to some high speed (not just full throttle, 90,80,70% have the same issue... but some how, cushioned... till disappears with 50% aprox... BUT, if the motor is running (even at "minimum" speed [2100rpm]) and goes to full throttle [31000rpm], there is no issue ^_^

Don't know if that points you to the solution.

sim- commented 9 years ago

For the LED, it would just be something like defining a pin named "red_led" in the dys_nfet.inc file, and creating a macro to turn it on/off. Similar to what you might see in afro_nfet.inc. Note that the afro_nfet LEDs are wired such that grounding the MCU pin turns them ON, so "on" is implemented by "sbi DDRC, red_led", which translated to english means "set bit in I/O register 'data direction for port C' for bit 'red_led'". Set means to set the bit to 1, which enables output mode on the pin. With the PORTC bit being 0 already (set by the INIT_PC value), enabling output mode effectively grounds the pin.

For an example of the other way around, see afro_pr1.inc, which starts with the port set to output mode, but toggles the PORT value to drive the pin high. Either way will work, just use which way you'd like.

The miso, mosi, and sck pins are usually free since they are used for programming, and are documented already in dys_nfet.inc. Just pick one. Note also that the MOTOR_DEBUG feature uses the MISO and MOSI (or SCK) pins if not assigned elsewhere. So you might want to use SCK for the LED.

Luisonson commented 9 years ago

Ok, Please, confirm the modifications... I have to change dys_nfet.inc and add the next line:

;***** ; PORT B definitions * ;***** ;.equ = 7 ;.equ = 6 ;.equ = 5 (sck) .equ red_led = 5 ;.equ = 4 (miso) ;.equ = 3 (mosi) ;.equ = 2 .equ CnFET = 1 ;o .equ rcp_in = 0 ;i r/c pulse input

And add also:

.MACRO RED_on sbi DDRB, red_led .ENDMACRO .MACRO RED_off cbi DDRB, red_led .ENDMACRO

Is it ok? Have I to change something else in tgy.asm or the .inc?

sim- commented 9 years ago

I captured some traces last night with a SunnySky X2204-16 2300KV motor at 25V from a 6S LiPo, and I think I understand what is happening.

There is an initial start ramp that cushions the acceleration on first start, as you see. This was originally in place to smooth out the hard steps of speed-based duty limiting, which are in place to limit current intentionally at stopped or very slow speeds, where otherwise many hundreds of Amperes might flow, and also to increase the chances of catching any misalignment by limiting the switching noise that could be present.

However, with complementary PWM enabled, feedback of the "undriven" phase can be less useful when in complementary state (two FETs enabled on the same side of the bridge) because the undriven phase's natural voltage may be clamped by the FET body diodes. If you intentionally bypass the power ramp by letting the motor spin at low speed for a short period to let the ramp limit reach 100%, then jump to 100%, there will be a direct connection from power source to the motor, with no complementary period at all. This is effectively the same result as with COMP_PWM=0.

Now, the first part of the commutation loop is a blanking period, followed by a demagnetization wait. This waits for the comparator output to be in the expected state before the zero crossing. With complementary PWM, this may not actually see the expected state except when PWM is turned on (non-complementary state where one phase is driven high, one phase is driven low, letting the undriven phase float between them based on motor alignment). This is OK in most cases because there are at least a few PWM cycles between the end of the blanking phase before the mandatory demagnetization is detected.

However, with higher voltage and higher KV motors, the motor can spin so fast that there is no PWM ON cycle between the end of the blanking period and the time that the zero crossing occurs. This leads to the demag_timeout path being taken, which turns off the power entirely for the next step, allowing alignment to occur again. This can then loop for several periods, limiting the acceleration of the motor.

A higher PWM frequency should improve the situation by allowing ON cycles to occur between the blanking period and the zero crossing. However, your motor and/or source voltage might still be too high. Maybe try a POWER_RANGE of 248 or something? (Closer to 55kHz.)

Other than increasing the frequency further, some other PWM drive method (symmetric PWM) is really the only way around this. This also explains why the rotation speed aligns so easily to harmonics of the PWM frequency. Maybe it would be possible to step to a commutation-aligned variable-length PWM period at lower duties when the rotational speed permits it, but this starts to get tricky.

So, what is the goal of this project? Do you need the output power to also include braking / regeneration? The higher PWM frequency you use with that ESC will cause less braking, because the high-side turn-on is fairly slow. Setting HIGH_SIDE_PWM to 1 will invert the whole drive (high and low are swapped), which would restore the braking power again at the expense of a less linear output power as a result of the slow high-side turn-on. You could possibly bypass the limit entirely by removing sys_control from the code.

Here's a capture from the 'scope, but it's very tricky to read.

image

CH1-3: Motor phases. CH4: MOTOR_DEBUG flag output (MISO pin) showing commutation steps and ZC sampling visualization. trigger: MOSI pin output modified to go high when demag_timeout path is taken (like red LED) math: The calculated voltage offset that the comparator should be comparing for the particular step where the demag timeout occurred. Note that 0V is where the crossing should occur, and it is very difficult to actually see the slope.

Since this is higher voltage, you have to look very carefully to see the difference between high-side ON state and the phase clamped by diode conduction (~1V higher).

Luisonson commented 9 years ago

Wow!! :-0 Thanks for your effort and explanation! I wrote to you some months ago... obviously you don't remember. I was trying to do a line follower. Here is my last one: img_20150417_164432

comp_pwm I think it is 100% necessary, brake option also, because as it is a differential robot, I need super-fast reactions and also, similar reactions between accelerations and decelerations. This afternoon I was going to check the decelerations...

As I'm just testing the hardware, I don't mind change the esc. You say that sn20a is not fast in the high-side turn-on... another option? (lightweight, tiny and cheap). Afro 12amp?

On the other hand I don't know if lowering the power_range to 248 will affect the overall performance of the robot (less precise with the motor speed [ability to follow precisely the line-track])... and if my high-side turn-on is slow... it will be counter-productive, will not?

IF this "error" only happens when the motor is stopped and I accelerate fast... I think is something I can handle... but not sure of this...

PS: No one is using brushless motors for that kind of robots... maybe not even today with the custom-firmwares are an option and I'm just fighting in a lost battle... :(

Luisonson commented 9 years ago

Two questions... 1.- MOTOR_ADVANCE option has something to say in all of this?? If it is "greater" the motor will react faster? 2.- If STOP_RC_PULS= 50 and FULL_RC_PULS=250, and my MOTOR_RANGE is 800... then, will each step be 0.25 milliseconds? or will I have just 200 steps? Saying in other words. one step can be less than a millisecond?

tracernz commented 9 years ago

Cool project! :D I had to do the same thing in 1st pro year (2nd year) of my engineering degree. It was quite a lot of fun but not nearly as sophisticated as yours looks.

sim- commented 9 years ago

Hello! Oh, I remember, but not by your name...sorry! Did you email previously, or was there another github issue? I can't remember where we had previous discussions.

MOTOR_ADVANCE affects how long of a wait will occur after zero crossing is detected before the commutation step is advanced. The wait is defined as 30 - MOTOR_ADVANCE. This should make little difference, except that it could be possible that ah higher advance (less waiting) will give more time for the PWM cycles to occur before the zero crossing.

However, do have some significant step-down gearing or something? What speed are you actually achieving with those motors? Do you know the number of poles? I'd be surprised if you are hitting this issue except with no load on the motor.

Steps are measured in CPU ticks at 16MHz and scaled in 16x16bit math to the output power. So, POWER_RANGE sets the number of steps, and a scaling factor is calculated to just reach maximum power at FULL_RC_PULS. This means that with a real range of 200µs and POWER_RANGE of 800, each quarter microsecond will change the output duty.

Luisonson commented 9 years ago

The generic motor that is used is: pololu HP 5:1 That is a super tiny dc-motor, 30000rpm, 5:1 gear and 1.6A in stall. The turnigy 1230 is quite bigger (more than twice), 4500KV (that means 32000-36000rpm and with load <30000rpm), I used a 4.5:1 gear and 6A. It should have 2-4 poles... and by hand, I would say 3.

So... just in numbers, these turnigy have to be WAY better than the pololu. Reaction time accelerating are similar... And I thought brushless are faster in reactions... :(

I'm still testing the motor!! So I don't have one mounted. As you can see, the chassis is the PCB and are unique. If in the test the motor looks nice... then I have to design the whole PCB-chassis, make the 3D model to print the gearbox... etc.

PS: I talked to you by email ^_^

PS2: Are you saying that with load, the motor will not have this issue?

sim- commented 9 years ago

How do you check the Turnigy 1230 in stall? No ESC I know of will hold 100% duty cycle in a rotor stall. My code drops down to PWR_MIN_START (1/6th of 100%), building up to PWR_MAX_START (1/4th of 100%), then introducing some modulation to avoid overheating.

Anyway, yes, if it is 2 or 4 poles, that is still WELL under the recent cases of 300,000 electrical RPM that I was testing. So, maybe your issue is entirely different. I may need to get some of those inrunners. I tested with a HobbyWing EZRUN 5.5T 6000KV at 24V :) and it reached maximum speed without issue (other than crazy screaming and huge current draw). What voltage are you using when you see the stumble during acceleration?

Luisonson commented 9 years ago

Sorry, I'm accustomed to work with DC motors and with those motors we use the current in stall because it is the maximum current (in normal conditions). About this motor... I just know what HobbyKing says... and is that it's a 6A motor.

The voltage I'm using is 8V. Not even close to those 24... hehehe

At the end, seems I have to build the whole robot and test them "in ground". But, before that, I'm going to check if the motor brakes nice and decelerates fine... and if it does ok, I will move forward.

I will keep you informed ^_^

Luisonson commented 9 years ago

I have done some tests, and the motors seem to decelerate better than accelerate. Approximately, decelerates three times faster than accelerates.

There is some trick or change I can do to improve the acceleration?? For example: PWR_COOL_START, PWR_MIN_START, PWR_MAX_RPM1... maybe START_MOD_INC? I don't want to start changing parameters without knowing what i'm doing... hehe I'm sure you have some tricks to squeeze the esc. The esc is way oversized for this motors, so I'm not worry with the heat.

sim- commented 8 years ago

Ok, are you measuring from stopped or from running at a lower speed? This makes sense...A few things are biased in this direction. Probably you will want to bypass sys_control for your application, and maybe timing_duty. For example, at run6_3, remove the three instructions so that sys_control always gets MAX_POWER. That alone will remove the ramp, but there will still be a start phase and limit based on timing. The start power limits are defined near the top, and may work if set all to 100%, but this is untested. :) The timing-based limits (and also sys_control ramp) can be bypassed by removing the instructions after set_new_duty_l unttil set_new_duty_11. The comments there explain that this is where the limit gets applied.

However, note that this is typically where people use sensored designs. It is difficult to make a sensorless design just immediately apply power and have it accelerate as a brushed motor could, because higher current can expose magnetic properties that are difficult to distinguish on the undriven phase. I try to make it work as well as possible, but there are limits.

I guess you could also reduce ENOUGH_GOODIES to something like 6 or maybe even 4 to reduce the confidence of alignment before allowing to enter into normal running mode.

Luisonson commented 8 years ago

Hi!! With your nice answer, I have done lots of tests... but the first thing I did and improve a LOT the results, was glue my cheap encoder with loctite, hehehe

In the next graph, you are going to see the graphic of the motor going to 1[not 0]-100-25-100-75%, with three different firmwares. One is the stock firmware (of course, with comp-pwm, and brake at 8). Another with the run6_3 removed and the other with enough_goodies=6. The different timing is because the slow pwm servo signal, but look at the ramps:

0-100-25-100-75

Seems there isn't any improvement!! But, I have to say that the motor response, with the encoder properly fixed, is GREAT! just 20-25 milliseconds going from 0 to 100%!! And braking is as good as accelerating!!

I tested also another two firmwares. One with set_new_duty_l deleted, and another with PWR_MAX_RPM1 = POWER_RANGE and PWR_MAX_RPM2 = POWER_RANGE. But in both cases, the results were bad with loss of synchronization.

0-100-25-100-75_b 0-100-25-100-75_c

So, looking at the results, I'm going to use this motors in my new robot, and test how they behave in the ground...

Thanks a lot for your time!!!