Robotic arm PID tuning

Thread Starter

Saviour Muscat

Joined Sep 19, 2014
187
Dear All,

I am currently doing final year thesis of my degree course, through these past years I found here tangeble help, support and goodwill.

The robotic arm consists of five RC servo motors(excluding gripper), which their internal control circuitry is removed and both motor and internal pot are connected to the microcontroller board(TEENSY 4.0). The motor is drived by breakout board DVR8871 by two PWM signals for forward and reverse, which their position is known from the pot connected to the main shaft of the servo. The position of each joint is adjusted by five pots connected to ADS7828 I2C and then to the TEENSY 4.0 microcontroller.

I am trying to tuning the pid for 2nd and 3rd joints of my robotic arm. These joints needs to do more lifiting effort then the others, If the supply is switched off, due to their moment, they drop down but the others remain in the same position. For these joints I did PI (integral kept negligible) controller for both joints which dont remain stable but go little bit up and down. To remain stable I used Proportional controller only, but the steady state was little bit large. Please can you suggest ammendemnts/hints of my PID code so I will be able to settle down the arm with reduced steady state error.

Free fall of joint 2 and joint 3 without supply due to the weight and moment
https://drive.google.com/file/d/1O43cWxjCpEPZyvWelgheXY1SSeEL6WrO/view?usp=sharing

Unstable PI controller for joint 2 and joint3
https://drive.google.com/file/d/1O3vB_VTnIQFbePR5dmyiMa4jns3aRw-8/view?usp=sharing

Thank you very much,

Saviour Muscat
 

Attachments

crutschow

Joined Mar 14, 2008
34,680
The integrator (integral) part of the PID loop has zero error steady-state error, so you should add that if you want that error reduced.
 

Thread Starter

Saviour Muscat

Joined Sep 19, 2014
187
Dear Crutschow,

Thank you for your advice,

The problem is when introduce the integral, it little bit goes up and down( as shown in the video), I think due to its weight and moment. I need amendemnt/hint in the programm(attached) to retain the integral(eliminate steady state) and to overcome this flactuation.
 

crutschow

Joined Mar 14, 2008
34,680
Tunning a PID loop can be difficult, and I'm not familiar with how to do that.

Have you considered a Fuzzy Logic control loop?
It uses a relatively simple series of If-Then-Else loops to provide control, which a simple microprocessor can readily execute.
The simple commands make it easier to understand what part of the program needs to be tweaked to improve the loop response, as compared to trying to determine which part of the PID equations needs adjustment.
It has the further advantage that it more readily handles a non-linear system as compared to PID, which assumes a linear system.

A Fuzzy Logic program is written the way a (fast) human operator might tweak knobs or switches to control a system (e.g.
If the temperature is slightly high Then slightly turn down the heater power
Else
If the temperature is slightly low Then slightly turn up the heater power
Else
If the temperature is correct Then maintain the heater power
Else
If the temperature is moderately high Then moderately turn down the heater power
Else ........................)

Here's a reference tutorial on Fuzzy Logic from the Seattle Robotics Society.

I corresponded with someone some time ago on one of the electronic websites who was designing a control system for an Expresso Machine to control the sequence of operations for the steam, etc., and was having a problem with his PID loop.
I suggested trying Fuzzy Logic, and he got better results within just a first few iterations of the program using a Fuzzy Logic loop, then he previously had using the PID loop.

I know you already have a PID loop programmed, but it still might be easier to generate a Fuzzy-Logic loop than trying to get the PID loop to work.
 
Last edited:

nsaspook

Joined Aug 27, 2009
13,418
Sounds to me the motor are underpowered for the job and you're trying to move too fast, at too large a step. A normal PI control loop should be fairly easy to tune with stable mechanical linkages. Start with low values and work up until you start to get tracking oscillation then back down.

A simple C routine I use for PI basic control.
C:
#ifndef PID_H
#define PID_H

#ifdef __cplusplus
extern "C" {
#endif

    struct SPid {
        double dState; // Last position input
        double iState; // Integrator state
        double iMax, iMin; // Maximum and minimum allowable integrator state
        double iGain, // integral gain
        pGain, // proportional gain
        dGain; // derivative gain
    };

    double UpdatePI(volatile struct SPid * const, const double);
    void ResetPI(volatile struct SPid * const);


#ifdef __cplusplus
}
#endif

#endif /* PID_H */
C:
#include "pid.h"

/*
* converted to PI for solar power control
*/
double UpdatePI(volatile struct SPid * const pid, double const error) {
    double pTerm, iTerm;

    pTerm = pid->pGain * error; // calculate the proportional term
    // calculate the integral state with appropriate limiting
    pid->iState += error;

    if (pid->iState > pid->iMax) {
        pid->iState = pid->iMax;
    } else if (pid->iState < pid->iMin) {
        pid->iState = pid->iMin;
    } else {

    }

    iTerm = (pid->iGain * pid->iState); // calculate the integral term

    return pTerm + iTerm;
}

void ResetPI(volatile struct SPid * const pid) {
    pid->dState = 0.0; // not used but cleared
    pid->iState = 0.0;
}
https://apmonitor.com/pdc/index.php/Main/ProportionalIntegralControl
 

Jerry-Hat-Trick

Joined Aug 31, 2022
574
The condition you describe, where the arms drop down when power is removed, then go a little bit up and down, sounds similar to a position control system “hunting”, where the current needed to make the motor move will cause an overshoot which is why control systems often include a “dead band” - a small position window where no corrective action is taken.

I wonder if you could add a constant value to the position variable which, when there is no position error, that value continues to send a position holding current to the motor. Then incorporate a small dead band where the integral term doesn’t integrate, in other words it doesn’t increase if the position is close enough.
 

nsaspook

Joined Aug 27, 2009
13,418
IMO, the OP problem isn't with hysteresis that might require some sort of anti-hunting dead band.

With correctly designed closed loop control (accurate high resolution feedback, direct or low backlash motor/gearing, and sufficient motor power) you don't need dead bands. Servo brakes to prevent for power loss movements are a good idea.
This motor system uses two PI loops, one for current (torque) and the other for positioning (3 phase servo motor drive SPWM created from the PIC32 controller).
https://forum.allaboutcircuits.com/...hase-motor-and-controller.183667/post-1690092

As for fuzzy logic type control. It works but has its own problems.
https://forum.allaboutcircuits.com/threads/p-only-fuzzy-logic.147705/post-1262110
 

Jerry-Hat-Trick

Joined Aug 31, 2022
574
IMO, the OP problem isn't with hysteresis that might require some sort of anti-hunting dead band.
Just to clarify, deadband is not the same as hysteresis - see https://en.wikipedia.org/wiki/Deadband - I had to check myself!

I applaud your suggested improvements to the overall system, but my suggested fix may be sufficient to overcome, if only hide the problem with a few lines of code, rather than modify the hardware.

My simplistic explanation of the "hunting" phenomenon is that a stationary motor needs sufficient current to overcome stiction but as soon as it starts to move the system needs to respond blisteringly fast to prevent overshoot. Combining so called "fuzzy logic" with conventional PID control can improve performance; for example, if the position is well away from the target position, turning the motor full on and measuring the acceleration can provide useful information about the position/time to turn the motor off so that it stops at the target position and this algorithm can continually improve itself. Multi axis robots trying to reach a combination of target posititions simultaneously for each axis have to deal with the varying optimal gain values for the PID variables which vary according to the position of other axes which in turn affect the angular momentum of the arm hanging off each axis - not a trivial task.
 

nsaspook

Joined Aug 27, 2009
13,418
Just to clarify, deadband is not the same as hysteresis - see https://en.wikipedia.org/wiki/Deadband - I had to check myself!

I applaud your suggested improvements to the overall system, but my suggested fix may be sufficient to overcome, if only hide the problem with a few lines of code, rather than modify the hardware.

My simplistic explanation of the "hunting" phenomenon is that a stationary motor needs sufficient current to overcome stiction but as soon as it starts to move the system needs to respond blisteringly fast to prevent overshoot. Combining so called "fuzzy logic" with conventional PID control can improve performance; for example, if the position is well away from the target position, turning the motor full on and measuring the acceleration can provide useful information about the position/time to turn the motor off so that it stops at the target position and this algorithm can continually improve itself. Multi axis robots trying to reach a combination of target posititions simultaneously for each axis have to deal with the varying optimal gain values for the PID variables which vary according to the position of other axes which in turn affect the angular momentum of the arm hanging off each axis - not a trivial task.
You're right but a deadband is a typical quick 'fix' for mechanical hysteresis caused by external forces like gravity "Hysteresis is the difference in a variable depending on the direction of travel" like up or down here.

The real fix here IMO is a more robust mechanical drive system.
 

Thread Starter

Saviour Muscat

Joined Sep 19, 2014
187
Dear Jerry-Hat-Trick, Crutchow and Nsaspook,

Thank you for your hints and suggestions,

In my opinion will try to amend the code as suggested above by Jerry-Hat-Trick, if the problem persists, as last resort I will try to adapt Fuzzy Logic code as suggested by Crutchow and Nsaspook.

"I wonder if you could add a constant value to the position variable which, when there is no position error, that value continues to send a position holding current to the motor. Then incorporate a small dead band where the integral term doesn’t integrate, in other words it doesn’t increase if the position is close enough."

Please any code snippet how might apply to the below code

error2=analog_channel_2-Servo_ADC_2;//joint two pid
integral2=integral2+error2;
derivative2=error2-errorBefore2;
errorBefore2=error2;
GAIN2=(Deadband??) + Kp2*error2+Kd2*derivative2+Ki2*integral2;
GAIN2=abs(GAIN2);
if (GAIN2>4000)GAIN2=4000; //max PWM value


if((error2)>20)
{
//if (GAIN2>4000){GAIN2=4000;}// up
analogWrite(11,0);
analogWrite(12,GAIN2);


}
else if((error2)<-20)
{
//if (GAIN2>1700||GAIN2<4000){GAIN2=1700;}//down

analogWrite(12,0);
analogWrite(11,GAIN2);

}
else if((error2>-20)||(error2<20))// position reached, no control effort
{
analogWrite(11,0);
analogWrite(12,0);
integral2=0;
GAIN2=0;

}



Thank you for your effort to help
 

nsaspook

Joined Aug 27, 2009
13,418
You really don't want to go full Fuzzy. :eek: The code can easily become a mess of ifs when you have several types of motors and drive speed/force requirements.

This horror (to the OP, don't use it as an example other than to show how much better PI(D) control can be) of C code works but is almost impossible to modify in a structured way.
C:
int16_t track_motor(void)
{ // move motor feedback pot/encoder to setpoint
    static float deadband = 0.0;
    static int16_t was_running = FALSE, old_error = 0;
    uint8_t track_knob_to_pot, PERFECT = FALSE;
    uint8_t menu_pos;

    if (mode.free || mode.on_off_only || (mode.info_only)) return MERR_INV;

    track_knob_to_pot = knob_to_pot; // set the local version
    motordata[track_knob_to_pot].hunt_count = 0;
    checktime_track(TRACK_TIMEOUT, TRUE);
    if (motordata[track_knob_to_pot].pot.cal_failed) {
        if (!is_led_blinking(TEST_LED)) blink_led(TEST_LED, TRUE);
    } else {
        if (is_led_blinking(TEST_LED)) blink_led(TEST_LED, FALSE);
    }

    do { // move the assy/motor into the setting position
        check_cable(&menu_pos); // check DIPSW and set the menu_pos if needed
        if (menu_pos == cable_type) { // exit do loop if control cable is disconnected
            LED_3 = LOW;
            mode.free = TRUE;
            update_lcd_menu(menu_pos);
            return 2;
        }
        ADC_read();
        nav_menu();

        if (motordata[track_knob_to_pot].hunt_count > HUNT_MAX) { // adjust position deadband
            deadband = DEADB_HUNT;
        } else {
            if (motordata[track_knob_to_pot].run) {
                deadband = DEADB_RUN; // narrow deadband  when motor running
            } else {
                deadband = DEADB_STOP; // normal deadband when stopped
                if (knob2.movement != STOP) {
                    deadband = DEADB_KNOB;
                }
            }
        }

        if (mode.qei) { // shift gears in encoder mode
            if (ABSL(motordata[track_knob_to_pot].pot.error) > QEI_FAST) {
                if (DRIVE_V24 == LOW) {
                    POWER_X = LOW;
                    wdttime(RELAYRUNF); // wait for .5 seconds.
                    motordata[track_knob_to_pot].v24 = TRUE;
                    DRIVE_V24 = HIGH;
                    mode.v24 = TRUE;
                    if (TLOG) putrs2USART("\x1b[7m 24 volt drive \x1b[0m\r\n");
                    POWER_X = HIGH;
                }
            } else {
                if (DRIVE_V24 == HIGH) {
                    POWER_X = LOW;
                    wdttime(RELAYRUNF); // wait for .5 seconds.
                    motordata[track_knob_to_pot].v24 = FALSE;
                    DRIVE_V24 = LOW;
                    mode.v24 = FALSE;
                    if (TLOG) putrs2USART("\x1b[7m 5 volt drive \x1b[0m\r\n");
                    POWER_X = HIGH;
                }
            }
            if (ABSL(motordata[track_knob_to_pot].pot.error) > QEI_VERY_FAST) {
                if (POWER_S == LOW) {
                    if (DRIVE_V24 == HIGH) {
                        mode.slow_bypass = TRUE;
                        if (TLOG) putrs2USART("\x1b[7m Slowing resistor is off \x1b[0m\r\n");
                        POWER_S = HIGH;
                    }
                }
            } else {
                if (POWER_S == HIGH) {
                    if (DRIVE_V24 == HIGH) {
                        mode.slow_bypass = FALSE;
                        if (TLOG) putrs2USART("\x1b[7m Slowing resistor is on \x1b[0m\r\n");
                        POWER_S = LOW;
                    }
                }
            }
        }

        if ((motordata[track_knob_to_pot].pot.error > (int16_t) ((float) TRACK_DB_L / deadband)) && (motordata[track_knob_to_pot].pot.error < (int16_t) ((float) TRACK_DB_H / deadband))) {
            if (PERFECT && mode.qei) {
                if (was_running) {
                    deadband = DEADB_STOP; // normal deadband when stopped
                    if (TLOG) {
                        sprintf(bootstr2, " In QEI deadband, Error %3i, SLOW Flag %1i, SLOW Relay %i, Total Hunts %i \r\n", motordata[track_knob_to_pot].pot.error, motordata[track_knob_to_pot].slow, (int16_t) POWER_S, motordata[track_knob_to_pot].hunt_count);
                        puts2USART(bootstr2);
                    }
                }
                motor_control(&motordata[track_knob_to_pot]);
                if (qei1.movement == STOP) {
                    motordata[track_knob_to_pot].run = FALSE;
                    if (was_running) {
                        deadband = DEADB_STOP; // normal deadband when stopped, so adjust it now
                        if (knob2.movement != STOP) {
                            deadband = DEADB_KNOB;
                        }
                        if (TLOG) {
                            sprintf(bootstr2, " QEI stopped, Error %3i, SLOW Flag %1i, SLOW Relay &i, Total Hunts %i ", motordata[track_knob_to_pot].pot.error, motordata[track_knob_to_pot].slow, (int16_t) POWER_S, motordata[track_knob_to_pot].hunt_count);
                            puts2USART(bootstr2);
                            putrs2USART("\x1b[7m Motor Stopped. \x1b[0m\r\n");
                        }
                        was_running = FALSE;
                        LED_3 = LOW;
                    }
                }
            } else {
                motordata[track_knob_to_pot].run = FALSE;
                motor_control(&motordata[track_knob_to_pot]);
                if (was_running) {
                    deadband = DEADB_STOP; // normal deadband when stopped, so adjust it now
                    if (knob2.movement != STOP) {
                        deadband = DEADB_KNOB;
                    }
                    if (TLOG) {
                        sprintf(bootstr2, " %i Error %3i, SLOW Flag %1i, SLOW Relay %i, Total Hunts %i ", track_knob_to_pot, motordata[track_knob_to_pot].pot.error, motordata[track_knob_to_pot].slow, (int16_t) POWER_S, motordata[track_knob_to_pot].hunt_count);
                        puts2USART(bootstr2);
                        putrs2USART("\x1b[7m Motor Stopped. \x1b[0m\r\n");
                    }
                    was_running = FALSE;
                    LED_3 = LOW;
                }
            }
        }

        if (motordata[track_knob_to_pot].pot.error > (int16_t) ((float) TRACK_DB_H / deadband)) { // check knob position
            motordata[track_knob_to_pot].run = TRUE;
            was_running = TRUE;
            if (!motordata[track_knob_to_pot].cw) {
                motordata[track_knob_to_pot].hunt_count++;
                if (motordata[track_knob_to_pot].hunt_count > 1) V.hunt_count++;
            }
            motordata[track_knob_to_pot].cw = TRUE;
            LED_3 = HIGH;
            motor_control(&motordata[track_knob_to_pot]);
            if ((old_error != motordata[track_knob_to_pot].pot.error) && (ABSL(motordata[track_knob_to_pot].pot.error) > TRACK_DISPLAY)) {
                if (TLOG) {
                    sprintf(bootstr2, " %i Error %3i, SLOW Flag %1i, SLOW Relay %i, Hunts %i ", track_knob_to_pot, motordata[track_knob_to_pot].pot.error, motordata[track_knob_to_pot].slow, POWER_S, motordata[track_knob_to_pot].hunt_count);
                    puts2USART(bootstr2);
                    putrs2USART("\x1b[7m Motor CW. \x1b[0m\r\n");
                }
                old_error = motordata[track_knob_to_pot].pot.error;
            }
        }
        if (motordata[track_knob_to_pot].pot.error < (int16_t) ((float) TRACK_DB_L / deadband)) { // check knob position
            motordata[track_knob_to_pot].run = TRUE;
            was_running = TRUE;
            motordata[track_knob_to_pot].cw = FALSE;
            LED_3 = HIGH;
            motor_control(&motordata[track_knob_to_pot]);
            if ((old_error != motordata[track_knob_to_pot].pot.error) && (ABSL(motordata[track_knob_to_pot].pot.error) > TRACK_DISPLAY)) {
                if (TLOG) {
                    sprintf(bootstr2, " %i Error %3i, SLOW Flag %1i, SLOW Relay %i, Hunts %i ", track_knob_to_pot, motordata[track_knob_to_pot].pot.error, motordata[track_knob_to_pot].slow, POWER_S, motordata[track_knob_to_pot].hunt_count);
                    puts2USART(bootstr2);
                    putrs2USART("\x1b[7m Motor CCW. \x1b[0m\r\n");
                }
                old_error = motordata[track_knob_to_pot].pot.error;
            }
        }
        if (motordata[track_knob_to_pot].hunt_count > HUNT_LOW) motordata[track_knob_to_pot].slow = TRUE;
        if (motordata[track_knob_to_pot].hunt_count > HUNT_HIGH) motordata[track_knob_to_pot].slow_only = TRUE;
    } while (motordata[track_knob_to_pot].run && !button.B0 && !button.B1 && !button.B2 && !checktime_track(TRACK_TIMEOUT, FALSE));
    if (mode.qei) {
        mode.slow_bypass = FALSE;
        DRIVE_V24 = LOW;
        mode.v24 = FALSE;
    }
    if (checktime_track(TRACK_TIMEOUT, FALSE)) {
        if (TLOG) putrs2USART("\x1b[7m Motor movement TIMED OUT, setting knob position to motor position. \x1b[0m\r\n");
        if (mode.qei) knob2.c = qei1.c; // sync stopped position for timeout
        buzzer_ticks(1);
        return 4;
    }
    if (button.B0 || button.B1 || button.B2) {
        if (TLOG) putrs2USART("\x1b[7m Motor movement Button Interrupt, setting knob position to motor position. \x1b[0m\r\n");
        if (mode.qei) knob2.c = qei1.c; // sync stopped position for INTERRUPT
        if (button.B0) {
            button.B0 = LOW;
            mode.free = TRUE;
            LED_3 = LOW;
        }
        //        Clear_All_Buttons();
        return MERR_INT;
    }
    return MOK;
}
This code and controller was designed to emulate a very old voltage, relay and resistance controller to evaluate motorized assemblies before being installed in som 1980's era semi production equipment.

 
Last edited:

Thread Starter

Saviour Muscat

Joined Sep 19, 2014
187
Is there an easy way out?, what I need code snippe or indication when the joint reaches its desination(error<20 || error>-20) the integral stops and the PWM effort is retained so it remains in its position and does not goes down due to gravity. I controlled it by proportional controller only it worked fine but the steady state was little bit out of bounds.

On the video, serial data parameters of the adc values LHS shows pots position and RHS is shown motor shaft pot value,Error is LHS minus RHS
https://drive.google.com/file/d/1OSZoCbBHoqS-0FqDaPBo3PYYZt8RpH28/view?usp=sharing


Porportional control with little out of bounds position target
https://drive.google.com/file/d/1OWFuyinpvX4X4KYk9-qOkVAwuu_Pua_m/view?usp=sharing

TY
Saviour
 
I’ll have a closer look at your code tomorrow and see if I can suggest where you might be able to add just a few lines to try the “quick fix” approach I suggested. I’ve never been much good at understanding someone else’s code, or indeed my own when I go back to it a month later!
 

nsaspook

Joined Aug 27, 2009
13,418
Is there an easy way out?, what I need code snippe or indication when the joint reaches its desination(error<20 || error>-20) the integral stops and the PWM effort is retained so it remains in its position and does not goes down due to gravity. I controlled it by proportional controller only it worked fine but the steady state was little bit out of bounds.

On the video, serial data parameters of the adc values LHS shows pots position and RHS is shown motor shaft pot value,Error is LHS minus RHS
https://drive.google.com/file/d/1OSZoCbBHoqS-0FqDaPBo3PYYZt8RpH28/view?usp=sharing


Porportional control with little out of bounds position target
https://drive.google.com/file/d/1OWFuyinpvX4X4KYk9-qOkVAwuu_Pua_m/view?usp=sharing

TY
Saviour
One easy thing to try (as a movement stability test) is to reduce the distance of long movements into sequence of shorter movements. You can also Integral windup limits in the PID code.
https://www.mathworks.com/videos/un...g-beyond-a-simple-integral-1528310418260.html
 
Last edited:
Here we go!

error2=analog_channel_2-Servo_ADC_2;//joint two pid
Okay
integral2=integral2+error2;
Since this updates every loop it could get very large very quickly. You only need it to be
contributing when the error is relatively small so maybe replace with:
if(error2 < Ke2) integral2 = min(i2max, integral2+error2);
where ke2 and i2max are constants (this may not be valid code)
derivative2=error2-errorBefore2;
errorBefore2=error2;
okay
GAIN2=(Deadband??) + Kp2*error2+Kd2*derivative2+Ki2*integral2;
Yes, I'm thinking you need to add a "constant" value in that line which effectively holds the link from
dropping down without actually moving it. But it needs to vary according to the position of all the links.
If the links are all vertical the value should be zero. So it's a formula based on the moment about joint 2
as a result of the position of all the links. Slightly laborious, but it depends on the Servo_ADC values for
all the joints and the weight, length and CG of the links. Strictly, it also depends on the weight of the item held
by the gripper which can vary. You may get away with just a constant value for now

But this is not the "deadband" which is a band of error where the integral term has no effect, maybe:
holding_variable = [function of Servo_ADCs, weights and lengths]
GAIN2 = holding_variable + Kp2*error2 + Kd2*derivative2 + Ki2*integral2*(Abs(error2)< deadband);
GAIN2=abs(GAIN2);
if (GAIN2>4000)GAIN2=4000; //max PWM value

You may get away with just a deadband constant and ignore the holding variable - so maybe try it first.
With your existing code, can you measure the servo_ADC value change when it currently hunts up and down and adjust your deadband constant accordingly?

Hope this helps.
 

nsaspook

Joined Aug 27, 2009
13,418
And this code isn't? :eek:
His overall code structure or lack of it, really isn't the problem with PID (the digital equivalent to analog opamp circuit integrators and differentiators) as a basic control method. What can become messy with fuzzy control is the need to code Perturb & Observe (P&O) events into a Inference system, to computer logic that then needs to be coded in some computer language. Most non-trivial control system programs use a combination of methods as dictated by what's good, at that point of the code.
Fuzzy logic can linearize some really weird and shady nonlinear parts of systems so those parts can be incorporated into overall system PID control.
https://www.iosrjournals.org/iosr-jeee/Papers/Vol10-issue4/Version-1/H010415362.pdf

My code?
Note from my post. Absolutely it's a mess because the logic evolved from emulation of old undocumented but mechanically stable with precise movement and feedback hardware that I was designing testing devices for. That tester and software are still being used today on machines making semiconductors. The more fuzzy the plant control constraints, the more fuzzy the logic will be usually unless you have some custom hardware system designed specifically for a fuzzy logic controller.
https://www.omega.co.uk/technical-learning/pid-fuzzy-logic-adaptive-control.html

Here, what's needed first, is a stable mechanical system. Without that no control system will work properly.
https://www.tutorialspoint.com/fuzzy_logic/fuzzy_logic_control_system.htm

  • The plant is observable and controllable − It must be assumed that the input, output as well as state variables are available for observation and controlling purpose.
  • Existence of a knowledge body − It must be assumed that there exist a knowledge body having linguistic rules and a set of input-output data set from which rules can be extracted.
  • Existence of solution − It must be assumed that there exists a solution.
  • ‘Good enough’ solution is enough − The control engineering must look for ‘good enough’ solution rather than an optimum one.
  • Range of precision − Fuzzy logic controller must be designed within an acceptable range of precision.
  • Issues regarding stability and optimality − The issues of stability and optimality must be open in designing Fuzzy logic controller rather than addressed explicitly.

This horror (to the OP, don't use it as an example other than to show how much better PI(D) control can be) of C code works but is almost impossible to modify in a structured way.
https://www.electronicdesign.com/te...57343/whats-all-this-fuzzy-logic-stuff-anyhow
https://www.electronicdesign.com/te...-all-this-ball-on-beam-balancing-stuff-anyhow

This man understood control theory.
 
Last edited:
If we are going to debate the relative merits and demerits of PID and "fuzzy" - which is by nature defined in a fuzzy way - let's not forget that PID was invented and used to stabilise ships more than 100 years ago, long before the availability of high speed processors. Even today, makers of temperature controllers proudly state that they use PID which is frankly ridiculous. Temperature control is mostly adequately achieved by simply turning the heater (or cooler) fully on or fully off for a set time depending on the temperature error. The measured temperature lag can be observed on the fly so the turn-off time can be accurately determined to have the temperature come to rest at the exact target temperature whilst pwm of the heater can keep it there with minimal error. No doubt, as manufacturers who have until now boasted about PID will start to boast about AI which of course it isn't either, it's just a relatively simple state machine.

PID mathematics really only works for linear systems. Given the speed of modern processors with speeds far higher than the electromechanical bandwidth of systems they are controlling, the combining of PID principals with fuzzy logic, adjusting variables on the fly makes sense to me. In the case of this project, for high speed movements, the weight being held by the gripper needs to be estimated based on angular acceleration of the limbs with a given drive signal, or you just have to go slower to accommodate this unknown.
 

crutschow

Joined Mar 14, 2008
34,680
that PID was invented and used to stabilise ships more than 100 years ago
Yes, because analog was the only way you could control things from that time until small digital processors became available.
Now we are trying to emulate an analog approach in a digital world.
PID mathematics really only works for linear systems
Yes, so trying to use PID for a non-linear mechanical system, which many are, and dealing with PID problems such as integrator windup can be problematic, so adding or using Fuzzy logic makes sense.
 
Top