PID implementation in arduino

0

I came across some code online in which the PID is implemented for arduino. I am confused of the implementation. I have basic understanding of how PID works, however my source of confusion is why the hexadecimal is being used for m_prevError? what is the value 0x80000000L representing and why is right shifting by 10 when calculating the velocity?

// ServoLoop Constructor
ServoLoop::ServoLoop(int32_t proportionalGain, int32_t derivativeGain)
{
    m_pos = RCS_CENTER_POS;
    m_proportionalGain = proportionalGain;
    m_derivativeGain = derivativeGain;
    m_prevError = 0x80000000L;
}

// ServoLoop Update 
// Calculates new output based on the measured
// error and the current state.
void ServoLoop::update(int32_t error)
{
    long int velocity;
    char buf[32];
    if (m_prevError!=0x80000000)
    {   
        velocity = (error*m_proportionalGain + (error - m_prevError)*m_derivativeGain)>>10;

        m_pos += velocity;
        if (m_pos>RCS_MAX_POS) 
        {
            m_pos = RCS_MAX_POS; 
        }
        else if (m_pos<RCS_MIN_POS) 
        {
            m_pos = RCS_MIN_POS;
        }
    }
    m_prevError = error;
}
c
arduino
controls
pid

2 Answers

0

Shifting a binary number to right by 1 means multiplying its corresponding decimal value by 2. Here shifting by 10 means multiplying by 2^10 which is 1024. As any basic control loop, it could be a gain of the velocity where the returned-back value is converted to be suitable to re-use by any other method.

The L here 0x80000000L is declaring that value as long. So, this value 0x80000000 may be an initial value of error or so. Also, you need to revise the full program to see how things work and what value is assigned to something like error.

answered on Stack Overflow Dec 11, 2018 by Gamal Othman
0

Contrary to the other answer, shifting to the right has the effect to divide by a power of two, in this case >> 10 would divide by 1024. But a real division would be better, more clear, and optimized by the compiler with a shift anyway. So I find this shift ugly.

The intent is to implement some float math without actually use floating point numbers - it is a kind of fixed point calculation, where the fractional part is about 10 bits. To understand, assuming to simplify the derivative coefficient=0, an m_proportionalGain set to 1024 would mean 1, while if set to 512 it would mean 0.5. In fact in the case of proportional=1024, and error=100, the formula would give

100*1024 / 1024 = 100

(gain=1), while proportional=512 would give

100*512 / 1024 = 50

(gain=0.5).

As for previous error m_prevError set to 0x80000000, it is simply a special value which is checked in the loop to see if "there is already" a previous error. If not, i.e. if prevError has the special value, the entire loop is skipped once; in other words, it serves the purpose to skip the first update after creation of the object. Not very cleaver I suppose, I would prefer to simply set the previous error equal to 0 and skip completely the check in ::update(). Using special values as flag has the problem that sometimes the calculations result in the special value itself - it would be a big bug. If absolutely needed, it is better to use a true flag.

All in all, I think this is a poor PID algorithm, as it lacks completely the integrative part; it seems that the variable m_pos is thought for this integrative purpose, it is managed quite that way, but never used - only set. Nevertheless this algorithm can work, but all depends on the target system and the wanted performances: on most situations, this algorithm leaves a residual error.

answered on Stack Overflow Dec 28, 2018 by linuxfan

User contributions licensed under CC BY-SA 3.0