Author Topic: any fuzzy real example in C around ?  (Read 11857 times)

0 Members and 1 Guest are viewing this topic.

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
any fuzzy real example in C around ?
« on: October 14, 2013, 05:56:00 pm »
hi guys
i am trying to learn something about fuzzy logic, but unfortunately i can't find any example of real implementation in C about the fuzzy controller.
could you suggest me something ?

thanks
 

Offline npnlamp

  • Contributor
  • Posts: 19
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #2 on: October 14, 2013, 07:14:53 pm »
 Brubaker, David I. Introduction to Fuzzy Logic Systems. Menlo Park, CA: The Huntington Group, 1991.

Kosko, Bart. Neural Networks and Fuzzy Systems. Englewood Cliffs., NJ: Prentice-Hall, 1990.

Self, Kevin. "Designing with Fuzzy Logic." IEEE Spectrum (November, 1990).

Sibigtroth, James M. "Creating Fuzzy Micros." Embedded Systems Programming (December, 1991).

Sibigtroth, James M. "Implementing Fuzzy Expert Rules." AI Expert (April, 1992).

Williams, Tom. "Fuzzy Logic is Anything but Fuzzy." Computer Design (April, 1992).

 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #3 on: October 14, 2013, 07:16:07 pm »
See Microchip App Noes re: Fuzzy Logic --double pendulum

Also see:

http://ww1.microchip.com/downloads/en/AppNotes/00600b.pdf
« Last Edit: October 14, 2013, 07:19:09 pm by Paul Price »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: any fuzzy real example in C around ?
« Reply #4 on: October 14, 2013, 07:40:56 pm »
Thanks, but any C code around ?
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #5 on: October 14, 2013, 08:07:23 pm »
/*  General-purpose fuzzy inference engine supporting any number of system
inputs and outputs, membership functions, and rules. Membership functions can
be any shape defineable by 2 points and 2 slopes--trapezoids, triangles,
rectanlges, etc. Rules can have any number of antecedents and outputs, and can
vary from rule to rule. "Min" method is used to compute rule strength, "Max"
for applying rule strengths, "Center-of-Gravity" for defuzzification. This
implementation of Inverted Pendulum control problem has: System Inputs, 2
(pendulum angle and velocity); System Outputs, 1 (force supplied to base of
pendulum); Membership Functions, 7 per system input/output; Rules, 15 (each
with 2 antecedents & 1 output). If more precision is required, integers can
be changed to real numbers.*/

#include <stdio.h>
#define MAXNAME 10          /* max number of characters in names           */
#define UPPER_LIMIT  255    /* max number assigned as degree of membership */

void PID();

/* io_type structure builds a list of system inputs and a list of system
outputs. After initialization, these lists are fixed, except for value field
which is updated on every inference pass. */
struct io_type{
  char name[MAXNAME];        /*  name of system input/output       */
  int value;                 /*  value of system input/output      */
  struct mf_type             /*  list of membership functions for  */
    *membership_functions;   /*     this system input/output       */
  struct io_type *next;      /*  pointer to next input/output      */
  };
/* Membership functions are associated with each system input and output. */
struct mf_type{
  char name[MAXNAME]; /* name of membership function (fuzzy set)    */
  int value;          /* degree of membership or output strength    */
  int point1;         /* leftmost x-axis point of mem. function     */
  int point2;         /* rightmost x-axis point of mem. function    */
  int slope1;         /* slope of left side of membership function  */
  int slope2;         /* slope of right side of membership function */
  struct mf_type *next;   /* pointer to next membership function    */
  };
/*  Each rule has an if side and a then side. Elements making up if side are
pointers to antecedent values inside mf_type structure. Elements making up then
side of rule are pointers to output strength values, also inside mf_type
structure. Each rule structure contains a pointer to next rule in rule base. */
struct rule_element_type{
  int *value;                /* pointer to antecedent/output strength value */
  struct rule_element_type *next; /* next antecedent/output element in rule */
  };
struct rule_type{
  struct rule_element_type *if_side;     /* list of antecedents in rule */
  struct rule_element_type *then_side;   /* list of outputs in rule     */
  struct rule_type *next;                /* next rule in rule base      */
  };
struct rule_type *Rule_Base;             /* list of all rules in rule base */


main()
{
 initialize_system();
 while(1){
  get_system_inputs();
  fuzzification();
  rule_evaluation();
  defuzzification();
  put_system_outputs();
  }
}

/* Fuzzification--Degree of membership value is calculated for each membership
function of each system input. Values correspond to antecedents in rules. */
fuzzification()
{
 struct io_type *si;    /* system input pointer        */
 struct mf_type *mf;    /* membership function pointer */
for(si=System_Inputs; si != NULL; si=si->next)
  for(mf=si->membership_functions; mf != NULL; mf=mf->next)
    compute_degree_of_membership(mf,si->value);
}
/* Rule Evaluation--Each rule consists of a list of pointers to antecedents
(if side), list of pointers to outputs (then side), and pointer to next rule
in rule base. When a rule is evaluated, its antecedents are ANDed together,
using a minimum function, to form strength of rule. Then strength is applied
to each of listed rule outputs. If an output has already been assigned a rule
strength, during current inference pass, a maximum function is used to
determine which strength should apply. */
rule_evaluation()
{
 struct rule_type *rule;
 struct rule_element_type *ip;       /* pointer of antecedents  (if-parts)   */
 struct rule_element_type *tp;       /* pointer to consequences (then-parts) */
 int strength;                /* strength of  rule currently being evaluated */
 for(rule=Rule_Base; rule != NULL; rule=rule->next){
  strength = UPPER_LIMIT;                       /* max rule strength allowed */
        /* process if-side of rule to determine strength */
  for(ip=rule->if_side; ip != NULL; ip=ip->next)
      strength = min(strength,*(ip->value));
       /* process then-side of rule to apply strength */
  for(tp=rule->then_side; tp != NULL; tp=tp->next)
      *(tp->value) = max(strength,*(tp->value));
  }
}
/* Defuzzification */
defuzzification()
{
 struct io_type *so;    /* system output pointer                  */
 struct mf_type *mf;    /* output membership function pointer     */
 int sum_of_products;   /* sum of products of area & centroid */
 int sum_of_areas;     /* sum of shortend trapezoid area          */
 int area;
 int centroid;
 /* compute a defuzzified value for each system output */
for(so=System_Outputs; so != NULL; so=so->next){
  sum_of_products = 0;
  sum_of_areas = 0;
  for(mf=so->membership_functions; mf != NULL; mf=mf->next){
     area = compute_area_of_trapezoid(mf);
     centroid = mf->point1 + (mf->point2 - mf->point1)/2;
     sum_of_products += area * centroid;
     sum_of_areas += area;
     }
  so->value = sum_of_products/sum_of_areas;   /* weighted average */
  }
}

/* Compute Degree of Membership--Degree to which input is a member of mf is
calculated as follows: 1. Compute delta terms to determine if input is inside
or outside membership function. 2. If outside, then degree of membership is 0.
Otherwise, smaller of delta_1 * slope1 and delta_2 * slope2 applies.
3. Enforce upper limit. */
compute_degree_of_membership(mf,input)
struct mf_type *mf;
int input;
{
 int delta_1;
 int delta_2;
 delta_1 = input - mf->point1;
 delta_2 = mf->point2 - input;
 if ((delta_1 <= 0) || (delta_2 <= 0))   /* input outside mem. function ?  */
   mf->value = 0;                           /* then degree of membership is 0 */
 else
   mf->value = min( (mf->slope1*delta_1),(mf->slope2*delta_2) );
   mf->value = min(mf->value,UPPER_LIMIT);  /* enforce upper limit */
}
/* Compute Area of Trapezoid--Each inference pass produces a new set of output
strengths which affect the areas of trapezoidal membership functions used in
center-of-gravity defuzzification. Area values must be recalculated with each
pass. Area of trapezoid is h*(a+b)/2 where h=height=output_strength=mf->value
b=base=mf->point2-mf->point1 a=top= must be derived from h,b, and slopes1&2 */
compute_area_of_trapezoid(mf)
struct mf_type *mf;
{
 int run_1;
 int run_2;
 int base;
 int top;
 int area;
 base = mf->point2 - mf->point1;
 run_1 = mf->value/mf->slope1;
 run_2 = mf->value/mf->slope2;
 top = base - run_1 - run_2;
 area = mf->value * ( base + top)/2;
 return(area);
}

//PID code approximates fuzzy control
#include <pic.h>
#include <pic16f684.h>
#include <math.h>
#include <stdlib.h>

void Init();
void PID();
void Set_Constants();

bit flag1,do_PID,int_flag;
signed char en0, en1, en2, en3, term1_char, term2_char, off_set;
unsigned char temp;
short int temp_int;
unsigned short int ki, kd, kp;
signed int SumE_Min, SumE_Max, SumE, integral_term, derivative_term, un;
signed long Cn;

//   __CONFIG   _CP_OFF & _CPD_OFF & _BOD_OFF & _MCLRE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _FCMEN_ON

//***************************************************************************
// Positional PID 256 Hz 
//***************************************************************************

//***************************************************************************
//Main() - Main Routine
//***************************************************************************
void main()
{
   Init();                                 //Initialize 12F629 Microcontroller
   Set_Constants();                  //Get PID coefficients ki, kp and kd
   while(1)                                //Loop Forever
   {   
      if(do_PID){
         PID();
      }
   }
}

//***************************************************************************
//Init - Initialization Routine
//***************************************************************************
void Init()
{
   PORTA = 0;            
   TRISA = 0b00101101;    // Set RA4 and RA2 as outputs         
   PORTC = 0;
   TRISC = 0b00000011;      // Set RC0 and RC1 as inputs, rest outputs
   CMCON0 = 0x07;         // Disable the comparator

   IRCF0 = 1;            // Used to set intrc speed to 8 MHz
   IRCF1 = 1;            // Used to set intrc speed to 8 MHz
   IRCF2 = 1;            // Used to set intrc speed to 8 MHz

   CCP1CON = 0b01001100;   // Full bridge PWM forward
   ECCPAS = 0;            // Auto_shutdown is disabled for now
   PR2 = 0x3F;            // Sets PWM Period at 31.2 kHz
   T2CON = 0;            // TMR2 Off with no prescale
   CCPR1L = 0;            // Sets Duty Cycle to zero
   TMR2ON = 1;            // Start Timer2

   ANSEL = 0b00110101;      // Configure AN0,AN2,AN4 and AN5 as analog
   VCFG = 0;            // Use Vdd as Ref   
   ADFM = 1;            // Right justified A/D result
   ADCS0 = 1;            // 16 TOSC prescale
   ADCS1 = 0;
   ADCS2 = 1;
   CHS0 = 0;            // Channel select AN0
   CHS1 = 0;
   CHS2 = 0;
   ADON = 1;            //Turn A/D on

   en0 = en1 = en2 = en3 = term1_char = term2_char =0;
   ki = kd = 0;
   kp = off_set = 0;
   temp_int = integral_term = derivative_term = un =0;
   SumE_Max = 30000;
   SumE_Min = 1 - SumE_Max;
   do_PID = 1;            // Allowed to do PID function
   T0CS = 0;            // Timer0 as timer not a counter
   TMR0 = 10;            // Preload value
   PSA = 0;            // Prescaler to Timer0
   PS0 = 0;            // Prescale to 32 => 256 Hz
   PS1 = 0;
   PS2 = 1;
   INTCON = 0;
   PIE1 = 0;
   T0IE = 1;            // Enable Timer0 int
   GIE = 1;
   return;
}


void PID()      // The from of the PID is C(n) = K(E(n) + (Ts/Ti)SumE + (Td/Ts)[E(n) - E(n-1)])               
{
   integral_term = derivative_term = 0;
   
// Calculate the integral term
   SumE = SumE + en0;                     // SumE is the summation of the error terms
   if(SumE > SumE_Max){                  // Test if the summation is too big
      SumE = SumE_Max;
   }
   if(SumE < SumE_Min){                  // Test if the summation is too small
      SumE = SumE_Min;
   }                                 // Integral term is (Ts/Ti)*SumE where Ti is Kp/Ki
                                    // and Ts is the sampling period
                                    // Actual equation used to calculate the integral term is
                                    // Ki*SumE/(Kp*Fs*X) where X is an unknown scaling factor
                                    // and Fs is the sampling frequency
   integral_term = SumE / 256;               // Divide by the sampling frequency
   integral_term = integral_term * ki;         // Multiply Ki
   integral_term = integral_term / 16;         // combination of scaling factor and Kp

// Calculate the derivative term
   derivative_term = en0 - en3;
   if(derivative_term > 120){               // Test if too large
      derivative_term = 120;
   }
   if(derivative_term < -120){               // test if too small
      derivative_term = -120;
   }                                  // Calculate derivative term using (Td/Ts)[E(n) - E(n-1)]
                                    // Where Td is Kd/Kp
                                    // Actual equation used is Kd(en0-en3)/(Kp*X*3*Ts)
   derivative_term = derivative_term * kd;      // Where X is an unknown scaling factor
   derivative_term = derivative_term >> 5;     // divide by 32 precalculated Kp*X*3*Ts

   if(derivative_term > 120){               
      derivative_term = 120;
   }
   if(derivative_term < -120){
      derivative_term = -120;
   }
                                    // C(n) = K(E(n) + (Ts/Ti)SumE + (Td/Ts)[E(n) - E(n-1)])
   Cn = en0 + integral_term + derivative_term;   // Sum the terms
   Cn = Cn * kp / 1024;                  // multiply by Kp then scale
 
   if(Cn >= 1000)                        // Used to limit duty cycle not to have punch through
   {
      Cn = 1000;
   }
    if(Cn <= -1000)
   {
      Cn = -1000;
   } 
   if(Cn == 0){            // Set the speed of the PWM
      DC1B1 = DC1B1 = 0;
      CCPR1L = 0;
   }
   if(Cn > 0){               // Motor should go forward and set the duty cycle to Cn
      P1M1 = 0;            // Motor is going forward
      temp = Cn;
      if(temp^0b00000001){
         DC1B0 = 1;
      }
      else{
         DC1B0 = 0;
      }
      if(temp^0b00000010){
         DC1B1 = 1;
      }
      else{
         DC1B1 = 0;
      }   
      CCPR1L = Cn >> 2;      // Used to stop the pendulum from continually going around in a circle   
      off_set = off_set +1;   // the offset is use to adjust the angle of the pendulum to slightly
      if(off_set > 55){      // larger than it actually is
         off_set = 55;
      }   
   }

   else {                  // Motor should go backwards and set the duty cycle to Cn
      P1M1 = 1;            // Motor is going backwards
      temp_int = abs(Cn);      // Returns the absolute int value of Cn
      temp = temp_int;      // int to char of LS-Byte
      if(temp^0b00000001){
         DC1B0 = 1;
      }
      else{
         DC1B0 = 0;
      }
      if(temp^0b00000010){
         DC1B1 = 1;
      }
      else{
         DC1B1 = 0;
      }   
      CCPR1L = temp_int >> 2;   // Used to stop the pendulum from continually going around in a circle   
      off_set = off_set -1;
      if(off_set < -55){
         off_set = -55;
      }
   }
   en3 = en2;      // Shift error signals
   en2 = en1;
   en1 = en0;
   en0 = 0;
   do_PID = 0;            // Done
   RA4 = 0;            // Test flag to measure the speed of the loop
   return;
}

void Set_Constants()
{
   ANS2 = 1;            // Configure AN2 as analog
   ANS4 = 1;            // Configure AN4 as analog
   ANS5 = 1;            // Configure AN5 as analog

   ADFM = 1;            // Right justified A/D result
   CHS0 = 0;            // Channel select AN4
   CHS1 = 0;
   CHS2 = 1;
   temp = 200;            // Gives delay
   while(temp){
      temp--;
   }
   GODONE = 1;
   while(GODONE);{
      temp = 0;         // Does nothing.....
   }
   ki = ADRESH << 8;      // Store the A/D result to Integral Constant
   ki = ki + ADRESL;

   CHS0 = 1;            // Channel select AN5
   CHS1 = 0;
   CHS2 = 1;
   temp = 200;            // Gives delay
   while(temp){
      temp--;
   }
   GODONE = 1;
   while(GODONE);{
      temp = 0;         // Does nothing.....
   }
   kd = ADRESH << 8;      // Store the A/D result to Differential Constant
   kd = kd + ADRESL;

   CHS0 = 0;            // Channel select AN2
   CHS1 = 1;
   CHS2 = 0;
   temp = 200;            // Gives delay
   while(temp){
      temp--;
   }
   GODONE = 1;
   while(GODONE);{
      temp = 0;         // Does nothing.....
   }
   kp = ADRESH << 8;      // Store the A/D result to Proportional Constant
   kp = kp + ADRESL;
   CHS0 = 0;            // Channel select AN0
   CHS1 = 0;
   CHS2 = 0;   
}

void interrupt Isr()
{

   if(T0IF&&T0IE){
      TMR0 = 10;            // Preload value
      T0IF = 0;            // Clear Int Flag

//      flag1 = (!flag1);
      RA4 = 1;

        temp_int = 0;
      temp_int = ADRESH << 8;   // Store the A/D result with offset
      temp_int = temp_int + ADRESL - 512;
      en0 = temp_int + off_set/8;            // Store to error function asuming no over-flow
   
      do_PID = 1;            // Allowed to do PID function
      GODONE = 1;            // Start next A/D cycle
   }
   else
   {
      PIR1 = 0;
      RAIF = 0;
      INTF = 0;
   }
   if(temp_int > 180){       //Check if error is too large (positive)
      
      DC1B0 = DC1B1 = 0;      // Stop PWM
      CCPR1L = 0;   
      en0 = en1 = en2 = en3 = term1_char = term2_char = off_set = 0;       // Clear all PID constants
      Cn = integral_term = derivative_term = SumE = RA4 = 0;
      do_PID = 0;            // Stop doing PID
   }
   if(temp_int < -180){       //Check if error is too large (negative)
      
      DC1B0 = DC1B1 = 0;      // Stop PWM
      CCPR1L = 0;   
      en0 = en1 = en2 = en3 = term1_char = term2_char = off_set = 0;       // Clear all PID constants
      Cn = integral_term = derivative_term = SumE = RA4 = 0;
      do_PID = 0;            // Stop doing PID
   }

}

« Last Edit: October 14, 2013, 09:09:43 pm by Paul Price »
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #6 on: October 14, 2013, 09:03:09 pm »
/*
   
C++ Fuzzy Logic API + Simple DSL
   
CLinguisticVariable is the class used for representing the linguistic variables. There are three class variables. variableName is used for defining the linguistic variable name and should be unique, because we're using a hash map for storing CLinguisticVariable, so we can find specific linguistic variables in time O(1). The next variable, b_output declares if a linguistic variable is output or not. Please note, there can be only one output variable in the FuzzyLogic API. The last variable, h_linguisticValuesHashMap is used for storing linguistic values. One linguistic variable can have one or more linguistic values. There are several class methods. AddLinguisticValue adds LinguisticValue to the selected CLinguisticVariable. In the next image, you can see four linguistic variables: Distance, Approaching, Speed, and Signal.

The next method adds the linguistic value to a hash_map.
*/

//Method add Linguistic value to hash map

void AddLinguisticValue(LinguisticValue* p_linguisticValue)
{
    h_linguisticValuesHashMap.insert(LinguisticValue_Pairs(
        p_linguisticValue->ToString(), p_linguisticValue));
}

//where LinguisticValue_Pairs is a typedef declared in:

//Typedef for linguistic values hash map.
typedef pair <string, LinguisticValue*> LinguisticValue_Pairs;

*/
The method ResetLinguisticValues just resets all linguistic values to -1. A more interesting method is FindLinguisticValueByName. The input parameter of the method is the linguistic value name and the return value is a pointer to the object of the LinguisticValue. Because we are using a hash map, the time complexity is O(1).
*/

LinguisticValue* FindLinguisticValueByName(string name)

{
    //Define iterator
    hash_map <string, LinguisticValue*> :: const_iterator iterator;
    //Fint linguistic value by name
    iterator = h_linguisticValuesHashMap.find(name);
    if(iterator == h_linguisticValuesHashMap.end())
    {
        //SASO, this should never happends!!!!!
        assert(NULL);
        return NULL;
    }
    //Return LinguisticValue
    return (LinguisticValue*)iterator->second;
}

/* CalculateLinguisticValueByName is just a bridge between CFuzzyReasoner and LinguisticValue for calculating the linguistic value. A little bit more complicated is UpdateLinguisticValueByName. At first we have to find LinguisticValue by name. If there is a value stored already, we will perform the fuzzy operation OR, else we will just store newVal. Let's see the code:
*/

//Update Linguistic value by name
void UpdateLinguisticValueByName(string name, double newVal)
{
    //Find LinguisticValue
    LinguisticValue* value = FindLinguisticValueByName(name);

    //If there is value, we should perform operator OR
    if(value->GetLinguisticValue() != -1)
        value->SetLinguisticValue(CFuzzyCalculator::OR(value->GetLinguisticValue(), newVal));
    else
        value->SetLinguisticValue(newVal); //There is no value, just set newVal
}

/*
LinguisticValue

The class LinguisticValue is used for storing linguistic values. Variables A, B, C, and D are "borders" of the trapezium (trapezoid). For example, the linguistic variable Distance has three linguistic values (Low, Average, and Height) and for the linguistic value "Low", the variables A, B, C, and D would be A = 0, B = 0, C = 1, D = 2. Every LinguisticValue has a unique name (like in CLinguisticVariable). The variable linguisticValue is used for storing a numeric value. The default value is -1. This value means, how much input value belongs to this LinguisticValue trapezium. For the linguistic value Average (linguistic variable Speed, input: 0 (km/h)), this value would be equal to 0.5 (see image above). There is only one method (except the constructor, getters, and setters) called CalculateLinguisticValue. We have to take care of the correctness of the inputs, so if inputs are out of range, we should normalize them. Then we need to calculate the trapezoidal function. The result of the trapezoidal function is the linguistic value for a specific variable.
*/

double CalculateLinguisticValue(double input)
{
    //Normalize variables
    if(input < A)
        input = A;
    else if(input > D)
        input = D;
    //Calculating Trapezoid function
    if ((input <= A)
        || (input > D ))
        return 0;
    else if ((A < input) && (input < B))
        return (input - A) / (B - A);
    else if ((B <= input) && (input <= C))
        return 1;
    else
        return (D - input) / (D - C);
}

/*
Probably the simplest class in the project. There are only two static methods. The method AND implements the operator AND, which is equal to returning the minimal value.
*/

//Operator AND
static double AND(double a, double b)
{
    if(a < b)
        return a;
    return b;
}

/* and method OR implements operator OR, returning the maximal value.
*/

//Operator OR
static double OR(double a, double b)
{
    if(a > b)
        return a;
    return b;

}


/*
CFuzzyRule

Fuzzy Logic consists of linguistic variables and fuzzy rules. This class is used for storing a fuzzy rule. A fuzzy rule consists of objects of the class CFuzzyToken. The only functionality of the class is adding CFuzzyRuleToken to a rule. For example, CFuzzyRule could be:
*/

"IF NOT SPEED = SLOW AND DISTANCE = CLOSE, SIGNAL = BRAKE"

/* Every fuzzy rule should have an output variable!
CFuzzyRuleToken

CFuzzyRuleToken is just one part of a CFuzzyRule. Let's describe all variables. operation describes what kind of operation is between the previous and current CFuzzyRuleToken. Supported operators are OR, AND, and EMPTY. EMPTY means there isn't any operator ("VOID"). The EMPTY token is just used in the first and last tokens in the rule. Also tokens can be negated (variable b_negation). Every CFuzzyToken has a pointer to CLinguisticVariable and the name of the linguistic value. Let's see the CFuzzyRule example a little bit closer.

"IF NOT SPEED = SLOW AND DISTANCE = CLOSE, SIGNAL = BRAKE"

What are tokens? There are three token examples:

Token_1
operation = EMPTY
b_Negate = true
p_linguisticVariable= SPEED
linguisticValueName = "SLOW"

Token_2 operation = AND
b_Negate = false
p_linguisticVariable= DISTANCE
linguisticValueName = "CLOSE"

Token_3
operation =EMPTY
b_Negate = false
p_linguisticVariable= SIGNAL
linguisticValueName = BRAKE"

Please note, TOKEN_3 is the output token! Every CFuzzyRule must have a token with an output variable!!!

The method CalculateTokenOutputValue is used for getting an input value from the hash map h_fuzzyInputHashMap and calling a method for calculating the linguistic value. The input variable h_fuzzyInputHashMap is a hash_map filled with objects (class CFuzzyInput). In CFuzzyInput is stored input values for specific linguistic variables. CFuzzyInput will be explained later.
Collapse | Copy Code
*/

//Calculate token value
double CalculateTokenOutputValue(hash_map <string, CFuzzyInput*> h_fuzzyInputHashMap)
{
    hash_map <string,> :: const_iterator iterator;
    //Find linguistic variable
    iterator = h_fuzzyInputHashMap.find(p_linguisticVariable->ToString());
    CFuzzyInput* temp = (CFuzzyInput*)iterator->second;
    double input = temp->GetInputValue();
    //Calculate linguistic value
    return p_linguisticVariable->CalculateLinguisticValueByName(linguisticValueName, input);
}
/*
CFuzzyInput

CFuzzyInput has only two variables, variableName for the linguistic variable name and inputValue (type double) for the value. For example, CFuzzyInput could be variableName = "SPEED" and inputValue = "43" (km/h).
CFuzzyReasoner

CFuzzyReasoner is the heart of the FuzzyLogic system. It's used for calculating fuzzy rule output values and the final defuzzyfication. The CFuzzyReasoner variable v_fuzzyRulesVector is used for storing fuzzy rules (CFuzzyRule). Let's see the most important method in FuzzyLogic, FuzzyRuleReasoner. The method takes as inputs a fuzzy rule and a hash map filled with fuzzy inputs. Every fuzzy rule token is checked if it is an output variable or not. If not, output variables are calculated linguistic values of the current token variable. Then we check if the token is negated or not. If the linguistic variable is not first in the fuzzy rule and it's not an output variable then we should have an operator different than EMPTY. Only the first variable in the fuzzy rule must have the operator EMPTY (for the output value, operator isn't important). For the output value we just set a new value (resetVal) and return a pointer to the output token.
*/

//Start fuzzy rule reasoner
CFuzzyRuleToken* FuzzyRuleReasoner(CFuzzyRule* fuzzyRule,
                 hash_map <string, CFuzzyInput*> h_fuzzyInputs)
{
    double resultVal = 0;
    for(unsigned int i = 0; i < fuzzyRule->GetFuzzyRuleTokens().size(); i++)
    {
        //Get fuzzy rule token
        CFuzzyRuleToken* token = fuzzyRule->GetFuzzyRuleTokens();
            if(token->IsOutput())
        {
            //Update output object
            token->UpdateTokenValue(resultVal);
            //Return result token
            return token;
        }
        else
        {
            double tokenVal = token->CalculateTokenOutputValue(h_fuzzyInputs);
            token->UpdateTokenValue(tokenVal);
            if(token->IsNegated())
                tokenVal = 1 - tokenVal;    //Negate value
            if(i == 0)
                resultVal = tokenVal;        //Set value
            else if(token->IsOrOperator())
                resultVal = CFuzzyCalculator::OR(resultVal, tokenVal); //OR operator
            else if(token->IsAndOperator())
                resultVal = CFuzzyCalculator::AND(resultVal, tokenVal); //AND operator
        }
    }
    //This won't happends saso
    assert(NULL);
    return NULL;
}

// The following method just calls reasoning for every fuzzy rule in the FuzzyLogic API.

//Start reasoner for fuzzy rulles
CFuzzyRuleToken* CalculateFuzzyRules(hash_map <string, CFuzzyInput*> h_fuzzyInputs)
{
    //Reset all values
    CFuzzyRuleToken* outputObject;
    //Calculate all fuzzy rules
    for(unsigned int i = 0; i < v_fuzzyRulesVector.size(); i++)
    {
        //Glede na izhodno vrednost izhodne spremeljivke naredi update izhodnega objekta
        outputObject = FuzzyRuleReasoner((CFuzzyRule*)v_fuzzyRulesVector, h_fuzzyInputs);
    }
    //Return output object
    return outputObject;
}

The last step in Fuzzy Logic reasoning is Defuzzyfication. It's implemented in the next two methods.
Collapse | Copy Code

//Defuzzyfication
double Defuzzy(CFuzzyRuleToken *outputToken)
{
    //For every output value
    CLinguisticVariable* lVar = outputToken->GetLinguisticVariable();
    vector<LinguisticValue*> valuesList = lVar->GetLinguisticValuesList();

    double upEqualation = 0;
    double downEqualation = 0;
    //Calculating defuzzy value
    for(unsigned int i = 0; i < valuesList.size(); i++)
    {
        LinguisticValue* val = valuesList.at(i);
        upEqualation += val->GetLinguisticValue()
            * CalculateTrapezoidBalance(val->GetA(), val->GetB(), val->GetC(), val->GetD());
        downEqualation += val->GetLinguisticValue();
    }
    //Return output value of system
    if(downEqualation == 0)
        return 0;
    return upEqualation / downEqualation;
}

//Calculating surface of trapezoid
double CalculateTrapezoidBalance(double A, double B, double C, double D)
{
    return ((1 / (B - A)) * (2 * pow(B,3) - 3 * A * pow(B,2) + pow(A,3)) +
        3 * (pow(C,2) - pow(B,2)) + (1 / (D-C)) * (2 * pow(C,3) - 3 * D * pow(C,2) + pow(D,3)))
    / (3 * (B - A) + 6 * (C - B) + 3 * (D - C));
}
« Last Edit: October 14, 2013, 09:04:44 pm by Paul Price »
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #7 on: October 14, 2013, 09:03:36 pm »
/*
How to use it?

Probably you are wondering how to use this FuzzyLogic API. It's not so complicated. For making your life easier, I have made three simple examples. The first one is for a Fuzzy Cruise Control, the second for a basketball player, and the last one for a ski jumper. All examples are in the class called CExperiments. Let's see the simple basketball player example. We create three linguistic variables, then we add linguistic values to the linguistic variables. With the linguistic variables, we create two rules and add the rules to FuzzyLogic.
*/

void BuildBasketballPlayersRules()
{
    //Method for building necessary rules for Basketball players
    CLinguisticVariable *height = new CLinguisticVariable(false,"Height");
    CLinguisticVariable *age = new CLinguisticVariable(false,"Age");
    CLinguisticVariable *basketball_player =
              new CLinguisticVariable(true,"Basketball_player");

    //Add linguistic values to variable
    height->AddLinguisticValue(new LinguisticValue("Low_height",150,150,170,180));
    height->AddLinguisticValue(new LinguisticValue("Average_height",170,180,185,190));
    height->AddLinguisticValue(new LinguisticValue("Tall",185,195,210,210));

    age->AddLinguisticValue(new LinguisticValue("Young",10,10,25,30));
    age->AddLinguisticValue(new LinguisticValue("Average_young",25,35,40,45));
    age->AddLinguisticValue(new LinguisticValue("Old",50,60,80,80));
    age->AddLinguisticValue(new LinguisticValue("Average_old",40,45,50,55));
               
    basketball_player->AddLinguisticValue(new LinguisticValue("Suitable",0.5,0.6,1,1));
    basketball_player->AddLinguisticValue(new LinguisticValue("Unsuitable ",0,0,0.4,0.5));

    //Rule1
    //IF age = Young AND height = Tall THEN basketball_player = "Suitable"
    CFuzzyRule* rule1 = new CFuzzyRule();
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, age,"Young"));
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,AND, height,"Tall"));
    rule1->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, basketball_player,"Suitable"));

    //Rule2
    //IF age = "Old" THEN basketball_player = "Unsuitable"
    CFuzzyRule* rule2 = new CFuzzyRule();
    rule2->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY, age,"Old"));
    rule2->AddTokenToRule(new CFuzzyRuleToken(false,EMPTY,
                             basketball_player,"Unsuitable "));

    //Add rules
    fr->AddFuzzyRule(rule1);
    fr->AddFuzzyRule(rule2);

    //Add linguistic variables, if you want to obtain
    //more informations about fuzzy calculations
    AddLinguisticVariables(height);
    AddLinguisticVariables(age);
    AddLinguisticVariables(basketball_player);
}

/*
How to execute the example

With the next sample you should understand how to use the class CExperiment.
*/

int _tmain(int argc, _TCHAR* argv[])
{
    //Create instance of CExperiments.
    CExperiments* experiment = new CExperiments();
    //Create rules
    experiment->BuildCruiseControlRules();

    //Hash_map for storing inputs
    hash_map <string, CFuzzyInput*> h_fuzzyInputs;
   
    //Put some values to inputs
    CFuzzyInput* inputRazdalja = new CFuzzyInput("Distance", 1.8);
    CFuzzyInput* inputPriblizevanje = new CFuzzyInput("Approaching", 15);
    CFuzzyInput* inputHitrost = new CFuzzyInput("Speed", -8);
   
    //Insert inputs to the hash_map
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputRazdalja->GetVariableName(), inputRazdalja));
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputPriblizevanje->GetVariableName(), inputPriblizevanje));
    h_fuzzyInputs.insert(CFuzzyInput_Pairs(inputHitrost->GetVariableName(), inputHitrost));

    //Calculate fuzzy Logic output signal
    double outputSignal = experiment->CalculateFuzzyRules(h_fuzzyInputs);
   
    //Get some additional informations from fuzzy logic
    double t = experiment->GetC_ByName("Distance","Average");
    double v1 = experiment->GetLinguisticVariableValue("Signal","Brake");
    double v2 = experiment->GetLinguisticVariableValue("Signal","Maintain");
    double v3 = experiment->GetLinguisticVariableValue("Signal","Accelerate");

    double v4 = experiment->GetLinguisticVariableValue("Distance","Low");
    double v5 = experiment->GetLinguisticVariableValue("Distance","Average");
    double v6 = experiment->GetLinguisticVariableValue("Distance","High");

    double v7 = experiment->GetLinguisticVariableValue("Approaching","Slow");
    double v8 = experiment->GetLinguisticVariableValue("Approaching","Average");
    double v9 = experiment->GetLinguisticVariableValue("Approaching","Fast");

    double v10 = experiment->GetLinguisticVariableValue("Speed","Slow");
    double v11 = experiment->GetLinguisticVariableValue("Speed","Acceptable");
    double v12 = experiment->GetLinguisticVariableValue("Speed","Fast");

    //YOU HAVE TO RESET TOKEN VALUES BEFORE NEW CALCULATIONS!!!
    experiment->ResetTokenValues();
    delete experiment;

    return 0;
}


/*

Linguistic_Variable_Name Input/Output ValueName_0 (A,B,C,D) ValueName_N (A,B,C,D)

and rules:
*/

IF Linguistic_Variable_Name = Linguistic_Value_Name AND ...
  AND Linguistic_Variable_Name NOT Linguistic_Value_Name THEN
  Linguistic_Variable_Name = Linguistic_Value_Name

void Parse(char* filePath)
{
    ifstream ifs(filePath);
    string line;
    while( getline( ifs, line ) )
    {
        vector<string> tokens = SplitBy(line,' ');
        //Skip lines without text
        if(tokens.size() > 0)
        {
            //Skip comments
            if(!IsComment(tokens[0]))
            {
                if(IsRule(tokens[0]))
                    ParseFuzzyRule(tokens);
                else
                    ParseFuzzyAtribute(tokens);
            }
        }
    }
    //Close stream
    ifs.close();
}

bool IsRule(string token)
{
    //Return true if is rule
    if(token == "IF")
        return true;
    return false;
}

/*
How do we parse rules and attributes? Check the next two simple methods. ParseFuzzyRule just goes over all tokens (we don't need to process the first token, its should be "IF") and check if it is an operator token (AND / OR) and set the appropriate operation. Then we check if there is negation or we are on the "set" (token "="). If this condition is true, we just have to set the previous and next token, create a new CFuzzyRuleToken, and add it to the rule.
*/

void ParseFuzzyRule(vector<string> ruleTokens)
{
    //Rule example
    //IF distance = low AND approaching NOT slow THEN signal=breake
    //SKIP IF token
    bool negated = false;
    string leftSide;
    string rightSide;
    Operation operation = EMPTY;
    CFuzzyRule* rule = new CFuzzyRule();

    for(unsigned int i = 1; i < ruleTokens.size(); i++)
    {
        //Check for operator
        //OPERATORS =, Operator(AND, OR), NOT, THEN
        if((ruleTokens == "AND") || (ruleTokens == "OR"))
        {
            if(ruleTokens == "AND")
                operation = AND;
            else
                operation = OR;
        }
        else if((ruleTokens == "=") || (ruleTokens == "NOT"))
        {
            leftSide = ruleTokens[i - 1];
                //Next token is right side
            if(ruleTokens == "NOT")
                negated = true;
            rightSide = ruleTokens[++i];
           
            //ADD TOKEN TO RULE
            CLinguisticVariable* linVar =
              fuzzyInterface->FindLinguisticVariableByName(leftSide);
            assert(linVar != NULL);
            rule->AddTokenToRule(
              new CFuzzyRuleToken(negated,operation,linVar,rightSide));
            //RESET
            operation = EMPTY;
            negated = false;
        }
    }
    fuzzyInterface->AddFuzzyRule(rule);
}

/*
Now, parsing the attributes. The first token is always the linguistic variable name, the second token should be information of the attribute. There can be N linguistic values. We have to go over all tokens and parsing is Input or Output. With that information, we can create CLinguisticVariable. Then we can start to parse the linguistic values. We have to go over all the remaining tokens. The next token has to be the linguistic value name, followed by the linguistic values (method ParseLinguisticValues). When all linguistic values are parsed, we can add LinguisticVariable to the list (in CExperiment).
*/

void ParseFuzzyAtribute(vector<string> attTokens)
{
    //Distance Input Low (0,0,1,2) Average (1,2,3,4) High (3,4,5,5)
    //Linguistic variable name
    //Input or output
    //Linguistic value name
    //Values A,B,C,D
    string linguisticVariableName = attTokens.at(0);
    bool output = true;
    if(attTokens.at(1) == "Input")
        output = false;
    //Creating new linguistic variable
    CLinguisticVariable *linguisticVariable =
       new CLinguisticVariable(output,linguisticVariableName);

    //Adding linguistic values to variable
    for(unsigned int i = 2; i < attTokens.size(); i++)
    {
        string linguisticValueName = attTokens.at(i++);
        vector<double> linguisticValues = ParseLinguisticValues(attTokens.at(i));
        //Check for error!
        assert(linguisticValues.size() == 4);
        LinguisticValue* linValue = new LinguisticValue(linguisticValueName,
            linguisticValues[0],
            linguisticValues[1],
            linguisticValues[2],
            linguisticValues[3]);
        linguisticVariable->AddLinguisticValue(linValue);
    }
    fuzzyInterface->AddLinguisticVariables(linguisticVariable);
}

vector<double> ParseLinguisticValues(string token)
{
    vector<double> linguisticValues;
    //Remove first and last char
    token = token.erase(0,1);
    token = token.erase(token.size() - 1, 1);
    vector<string> values = SplitBy(token, ',');

    //Values size should be 4!
    for(unsigned int i = 0; i < values.size(); i++)
        linguisticValues.push_back(strtod(values.at(i).c_str(),NULL));
    return linguisticValues;
}

//Input file example


%ATTRIBUTES
Distance Input Low (0,0,1,2) Average (1,2,3,4) High (3,4,5,5)
Approaching Input Slow (0,0,10,20) Average (10,20,30,40) Fast (30,40,50,50)
Speed Input Slow (-50,-50,-10,0) Acceptable (-10,0,0,10) Fast (0,10,50,50)
Signal Output Brake (-5,-5,-2,-1) Maintain (-2,-1,1,2) Accelerate (1,2,5,5)

%RULES

/*
IF Distance = Low AND Approaching NOT Slow THEN Signal = Brake
IF Distance = Average AND Approaching = Fast THEN Signal = Brake
IF Speed = Fast THEN Signal = Brake
IF Distance NOT Low AND Approaching = Average THEN Signal = Maintain
IF Distance = High AND Approaching NOT Fast AND Speed = Acceptable THEN Signal = Maintain
IF Distance = Average AND Speed = Slow THEN Signal = Maintain
IF Distance = High AND Speed = Slow THEN Signal = Acc
*/
« Last Edit: October 14, 2013, 10:58:45 pm by Paul Price »
 

Offline Paul Price

  • Super Contributor
  • ***
  • Posts: 1443
Re: any fuzzy real example in C around ?
« Reply #8 on: October 14, 2013, 09:21:22 pm »
Downlload:   Microchip app note            AN964 --Inverted Double Pendulum Fuzzy logic. Full C-Code

/*
C CODE
When the approximation was made for the derivative
term, it stated that the approximation would be:
EQUATION 7:
By reviewing the code in Figure 6, one can determine
that the approximation, shown in Equation 7, is not the
approximation used. The actual equation that was used
for calculating the derivative term is as follows:
EQUATION 8:
Equation 8 more accurately written would be:
EQUATION 9:
C(n) = KPE(n) + KITs ??
0
E(n) + KD[E(n)- E(n-1)]/TS
C(n) = K(E(n) + (1/TI) ??
0
E(n) + TD[E(n)- E(n-1)]/TS
OR
Where KP = K, KI = K/TI & KD = K TD

*/

//Calculate the differential term
derivative_term = en0 - en3;
if(derivative_term > 120){
derivative_term = 120;
}
if(derivative_term < -120){
derivative_term = -120;
}
derivative_term = derivative_term * kd;
derivative_term =derivative_term>>5;
//divide by 32
}
if(derivative_term > 120){
derivative_term = 120;
}
if(derivative_term> -120){
derivative_term = -120;
}

/*
KDdE(t)/dt = TD [E(n)- E(n-1)]/Ts
Where TD = KD/KP
KDdE(t)/dt = KD [E(n)- E(n-3)]/32
KDdE(t) = TD[E(n)- E(n-3)]/(X•3•Ts)
=KD[E(n) -E(n-3)]/(KP•X•3•TS)

/* The X term allows for a fractional
representation of KD, not just a integer. (Similar scaling
factors are also used when calculating the integral and
proportional terms). The 3*TS comes from the fact that
we are tripling our sampling period by using E(n) – E(n-
3) not E(n) – E(n-1). Why use E(n) – E(n-3) instead of
E(n) – E(n-1)? The main reason for doing this is to limit
the variation in the error angle measurement. There will
always be an amount of uncertainty associated with the
error measurement, some of which can be attributed to
A/D error, Bessel filter throughput, mechanical vibration,
etc. Since the uncertainty of the error measurements
will be the same for all error terms, this
uncertainty can be decreased by effectively tripling the
sampling period. The real key is that the derivative term
is still updated at 256 Hz rate. Doing so gives the benefit
of the slower rate a more accurate derivative term,
but at the desired faster sampling rate. The side affect
of doing this is adding approximately 4 milliseconds of
lag to the derivative term, which in this case is acceptable.
KP*X*3*TS has been precalculated to be 32 to
increase the speed of the PID loop.
TUNING THE PID CONTROLLER
Use the following steps to tune the PID constants:
1. Turn the KP, KI and KD potentiometers counter
clockwise as far as they will turn. This sets all the
constants to zero.
2. Power the device using a 12V (minimum of a 3
amp) power supply.
3. Holding the Reset button down, lift the pendulum
to the vertical position and release the
Reset button and pendulum at the same time.
4. The pendulum should free fall and the base will
not move. This verifies that all constants are
properly read as zeros.
5. Increase the KP constant by turning the potentiometer
counter clockwise and repeat step 3.
6. Keep repeating steps 3-5 until there is a little
oscillation in the base. If the KP term is too small,
the base platform will chase the top of the pendulum
while ? continues to increase. KP will be
too large if the drive wheel breaks free or the
base oscillates at a high rate of speed.
7. Start increasing the KI the same way as KP until
the pendulum can be balanced for several seconds
under a constant oscillating condition.
When the KI is added, the base will now accelerate
faster than the pendulum causing ? to
change from a positive angle to a negative angle
(or vice versa). The pendulum will begin to fall
backwards. The base should change directions
and, again, accelerate faster than the pendulum
until ? changes signs and the whole cycle
repeats. This is known as the Overshoot
condition.
8. Increase KD in the same manner as KP and Ki
until the Overshoot condition is gone and the
pendulum remains balanced.
9. Once all overshoot is gone, the PID controller is
tuned.
*/
« Last Edit: October 14, 2013, 09:23:29 pm by Paul Price »
 

Offline David_AVD

  • Super Contributor
  • ***
  • Posts: 2958
  • Country: au
Re: any fuzzy real example in C around ?
« Reply #9 on: October 14, 2013, 09:32:24 pm »
Paul, there is a CODE button (# symbol ) you can use when posting that keeps code in a nice scrolling box.   :)
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: any fuzzy real example in C around ?
« Reply #10 on: October 15, 2013, 01:14:12 am »
also looking for general-purpose fuzzy-based inference engine written in C =P
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->