simplefoc / Arduino-FOC

Arduino FOC for BLDC and Stepper motors - Arduino Based Field Oriented Control Algorithm Library
https://docs.simplefoc.com
MIT License
2k stars 517 forks source link

About AS5048 #3

Closed raimapo closed 4 years ago

raimapo commented 4 years ago

Hello,

In Readme is mentioned encoder AS5048. You using it through PWM output ? I can't search anything more about it in source code. I am interested on small project to controll smoothly BLDC and want to use Your library, but in my board I am using AS5048A through SPI and BLDC Driver DRV8313. In simply way motor rotates very well, but speed is regulated by delay time function and it has some torque ripples :( In my case I have implemented getAngle (Degree) and get Velocity (deg/s) fuctions.

I can't understood Exactly how work setPhaseVoltage and setPWM. More deeply, what type and range of data is angle (degree or rad) ? What normalizeAngle is purpose if I have accurate angle from sensor ? How to understand negative and positive voltage values if PWM operates using Period from 0 to 255 ?

Thank You for the Answers :)

askuric commented 4 years ago

Hi raimapo,

Thanks for the message! I made a mistake in the readme, I wanted to say as5047. They have the ABI interface, basically the same as the encoder. That's why the same Encoder works for it. I did not have as5048 on my hands to implement it right away but it definitely on my list for the future.

As you have already done, the only thing that you really need is to implement getAngle and getVelocity. They are both in radians :) So radians and radians/s.

Function normaliseAngle, ensures that the angle is always in between 0 and 2PI radians. When using the fixed array sine and cos approximation functions, in the setPhaseVoltage they need the normalised angle as parameter.

setPhaseVoltage function exactly calculates inverse park transformation and Clarke transformation. It is not too complicated and it is the heart of this library. If it doesn't work well, nothing works.

Good question about the positive and negative voltages I had some trouble thinking about it too .:)

So it works like this: You always set positive voltages from 0-255 equivalent to 0-12V for example. This is what your driver puts out and what your motor receives.

But the motor doest have any reference ground point which would tell him what are the exact voltages you are sending him. It only sees the difference in between phases. Ua-Ub, Uc-Ub and Uc-Ua. If you have pit Ua =12V and you put Ub = 6V and Uc=0V. What the motor will see is: Ua=6V, Ub=0V and Uc=-6V. Because it doesn't know what the 0V means for us , the motor sees only relative values. :) I hope it makes sense.

Let me know if you have any more questions, you can do it directly on the email as well if it's easier.

Good luck, Antun

raimapo commented 4 years ago

Hello,

Thank You for answer. It helped me and motor began to rotate by voltage controller :)

From the first impression. at high voltage value, I did't feel so strong torque ripples/ sttuterings comparing with just sinusoidal pwm. But on low voltage (2) I see what at the some angles rotating a little bit slowing down and back to normal. It similar with direct sinusoidal usage but in that case was really often. Similar to this example https://www.youtube.com/watch?v=twKdWr8zl38

Another idea is that You don't have control of spin direction. It's important in case of velocity calculation, because in one case encoder value will increment in other decrement. I just didn't check if in Your encoder library it is done, but in foc I didn't saw such parameter.

And on more question. In velocity control example is set value to 2, so what is range of values ? It's by voltage or else ?

P.S. I rewriting library for STM32 :)

askuric commented 4 years ago

Hey Raimondas,

I am happy to hear from you, that's perfect!

Ok voltage control loop is almost open loop. It only uses the angle to set the sine waves to the motor. Just imagine it as "smart" sinusoidal control. The voltage control loop gives you a possibility to control the bldc motor as a DC motor by changing the voltage value. It doesn't use any control over the direction or the velocity. I am not sure why your motor is stuttering but it might be the dead zone of the motor. The same would happen to the DC motor, if you put voltage low enough on it it just wont move. (It might be something else, but it can be only this as well).

So to test this, you should run the real closed loop algorihtm, either velocity or angle. Where in target you set the desired velocity in radians/second and angle in radians. In velocity control 2 means 2 rad/s or around 120 degree/s. For me as control engineer it seemed perfectly reaosnable to make all the angles in radians, but you are right that I should probably make the buffering functions to degrees and rpm. :D

P.S. I rewriting library for STM32 :)

Very cool, let me know if/how can I help! Antun

askuric commented 4 years ago

I just realized that I was maybe not perfectly clear with the explanation why it stutters with low voltages.

Basically the BLDC motor rotation is caused by the permanent magnets in the rotor of the motor which are pushed by the electricro-magnetic field you set by applying the voltages Ua,Ub and Uc. The voltages Ua,Ub and Uc are calculated using your desired "Uq" voltage and Park and Clarke transformation. What it means is that Ua,b,c will be sinusoidal voltages with the magnitude Uq.

Also one thing that you can notice is that the BLDC motor, when disconnected, holds its position purely by the magnetic force of the magnets in the rotor . It is not too strong but you can definitely feel it. You can also see that if you move the rotor a bit out of these equilibrium positions it will rotate towards it by itself, by the pure magnetic force.

Now when you set a small value of Uq, this means that your sine Ua Ub and Uc waves will have small magnitude. This means that your voltages will not produce a high magnetic force as for the higher Uq values but your induced magnetic force(caused by Ua,Ub and Uc) will be comparable to the magnetic force of the rotor( the one that you feel when the motor is disconnected). This means that when you try to rotate the motor with 2 volts the motor will feel the magnetic force of the rotor a lot and when the motor is in the equilibrium position it will try to stay in it, and once when you push it out of it, it will rotate fast to the new one. Producing the series of slow and fast movements. :D

So in order to spin the motor slowly, you will have to close the loop: measure the actual velocity and set the value of Uq so that the motor spins with the desired velocity. This is already done for you in the Controller::velocity mode. You will just tune the PI parameters of the controller.

Cheers, Antun

raimapo commented 4 years ago

Hello,

Thank You for explanation, my knowledge in automation and motor drivers are poor :(

My problem until explanation what I thinking in positive range :( After I added value, for example -6 V it began to rotate in opposite direction at half speed. So voltage range is from -12 to +12, but voltage in equations are divided by half for equation correctness ?

I got some difficulties with velocity control after each iteration I got identical values. Some values from ControllerPI function:

if target velocity 2

tmp 0.044667
tracking_error 1.652490
voltage 6.000000
voltage_prev 6.000000
tracking_prev 1.652596

and if target velocity 3

tmp 0.045167
tracking_error 2.652660
voltage 6.000000
voltage_prev 6.000000
tracking_prev 2.687375

It is seen, what after increasing target velocity, tracking error increases, but voltage stays identical (voltage_prev). I can't find what I skipped. All iteration gives identical data. All controller configurations are identical to library.

/*
 * contoller configuration based on the controll type
 */
    myBLDC.PI_velocity.K = 0.3;
    myBLDC.PI_velocity.Ti = 0.003;
    myBLDC.PI_velocity.voltage_limit = 6;
    // jerk control using voltage voltage ramp
    myBLDC.PI_velocity.voltage_ramp = 300; 

Maybe I need to change voltage limit ?

I will search further where is a problem, because setPhaseVoltage and setPWM already works. it's somewhere with controller.

askuric commented 4 years ago

Hello, Can it be that you didn't change the controller type from voltage to velocity?

myBLDC.controller = ControlType::velocity;

Is your velocity value 0.045rad/sec for 6 volts? There is something else wrong with the code if that is true.

Are you sure that your velocity calculation is well done?

For me it seams that the controller is saturated in the highest value because it cannot reach the value you have set at the target.

If you really have possibility to put +-12V on your motor. Put the voltage_power_supply to 24V.

 myBLDC.voltage_power_supply = 12;

But I doubt it.

If you want send me an email directly and we can discuss your code in depth. Antun

raimapo commented 4 years ago

Hello,

I think it's good place to discuss, maybe for others whos will want to use Your library it gives some answers :)

I am also critical for my velocity calculation practically I am using this nice library, but rewrited to STM32F4 AS5048 Arduino

My rewrited my github

Also nice library is here MBED

None of this libraries has velocity calculation. So I implement my own, but it depends on spin direction. It's not difficult to change.

/*
 * Returns calculated velocityvalue
 */
float AS5048A::getVelocity(){

    uint16_t val;
    float velocity;

    val = getRawRotation();
    Timestamp = milis();
    angle = read2angle(val);
    angle = normalize(angle);

    if (OldAngle > angle)
        velocity = (angle+(360.0 - OldAngle))/(Timestamp-OldTimestamp);
    if (OldAngle < angle)
        velocity = (angle-OldAngle)/(Timestamp-OldTimestamp);

    OldAngle = angle;
    OldTimestamp = Timestamp;

        return (velocity);
}

It gives in deg/s, but rad/s = deg/s * (pi/180) for milis (micros) I am using specific function which calculates systics, so it must be very accurate.

uint32_t micros(void)
{
    uint32_t us_ticks = 0;
    uint32_t uptime_ticks = 0;
    uint32_t cycle_ticks = 0;

    register uint32_t old_cycle, cycle, timeMs;
    us_ticks = SystemCoreClock / 1000000u;

    do{
        timeMs = __LDREXW(&uptime_ticks);
        cycle = DWT->CYCCNT;
        old_cycle = cycle_ticks;
    }
    while ( __STREXW( timeMs , &uptime_ticks ) );
    return (timeMs * 1000) + (cycle - old_cycle) / us_ticks;
}

uint32_t milis(void)
{
    //return HAL_GetTick();
    return micros() / 1000;
}

So mistakes could be everywhere. It will took some time to solve them and accurate debug.

askuric commented 4 years ago

Hey @raimapo,
I am not sure that I understand well your micros() function but here are some concerns that I see in your code:

1) The velocity caluclation can be ran at higher frequencies than 1kHz what will result in your Timestamp-OldTimestamp = 0. That will kill your mcu process and who knows what else. Make your milis() return float that should solve it. 2) As your velocity function is calculating degree/s. then your Timestamp-OldTimestamp has to be in seconds, not in milliseconds - I cannot tell from the code if that the case. 3) Could you please explain:

   if (OldAngle > angle)
    velocity = (angle+(360.0 - OldAngle))/(Timestamp-OldTimestamp);
   if (OldAngle < angle)
     velocity = (angle-OldAngle)/(Timestamp-OldTimestamp);
 honestly, this does't make sense for me.  Is this the overflow compensation?

Antun

askuric commented 4 years ago

Hey @raimapo, I have just implemented the AS5048 family sensors into this library. Maybe it helps you in your project. It is still in testing phase so you can find the examples, the code and documentation in the dev branch: https://github.com/askuric/Arduino-FOC/tree/dev

Good luck, Antun

raimapo commented 4 years ago

Hello @askuric

Thank You. You Really Helped :) but I think I need to redesign some of my functions and it will took sometime. It must to start to work after I fix my fuctions getVelocity and micros/milis.

from questions

  1. and 2. Yes I realize it and what it's not perfect
  2. just simple recalculation, for example, then old angle is 350 and new 10

Issue could be closed :)

askuric commented 4 years ago

Hey @raimapo, I hope you will sort your troubles soon. Let me know if I can help further.

Regaring the code

if (OldAngle > angle)
 velocity = (angle+(360.0 - OldAngle))/(Timestamp-OldTimestamp);
if (OldAngle < angle)
  velocity = (angle-OldAngle)/(Timestamp-OldTimestamp);

You might have some troubles with this because when your OldAngle is bigger than angle you should have negative velocity. Basically in both cases you should calculate the velocity:

velocity = (angle-OldAngle)/(Timestamp-OldTimestamp);

But, you will have the overflow problem, since the angle value is always 0-360, you need to take care of what happens when OldAngle = 359 and angle = 1 or OldAngle = 1 and angle = 359 I suppose that was the idea behind the code above. Just I am not sure that it will solve it for you. I would use something like this:

if((angle - OldAngle) > 270) velocity = (angle - OldAngle - 360 )/(Timestamp-OldTimestamp);
else if((angle - OldAngle) < -270) velocity = (angle - OldAngle + 360 )/(Timestamp-OldTimestamp);

270 is a threshold value, you can choose the one that makes more sense for your application. It just says what is the maximal change of your angle in one sample time, and if the change is higher than that value you will consider that the overflow happened.

There are many ways to solve this problem, I am sure you will find a way that suites you the best. Just be aware that the code from above might be a bit problematic.

I will close this issue now. But feel free to contact me anytime if you need any assistance. Antun