Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

A Fuzzy-Logic Torque Servo


March 1994/A Fuzzy-Logic Torque Servo/Listing 1

Listing 1 C declaration code for rule table and membership functions

/**************************************************************
       File: fuzzy.h
       Date: 4/3/93
       Author: Jack J. McCauley
       Header file for fuzzy.c
**************************************************************/

/* membership function size */
#define TORQUE_MEMBERS 5
#define DER_MEMBERS 5

/* loop frequency */
#define LOOP_HZ 1

/* integrator constant z-1*/
#define SKIP 1

/* normalized value */
#define NORMAL 255

/* Current ma */
#define MAX_TORQUE 1000
#define MIN_TORQUE <B>-</B>1000

/* derivative */
#define MAX_DERI LOOP_HZ*MAX_TORQUE
#define MIN_DERI LOOP_HZ*MIN_TORQUE

/* pwm 1/10 % */
#define MAX_PWM   255
#define MIN_PWM   0

/* Max and Min */
#define MIN_ERROR   0
#define MAX_ERROR   255

/* size of all arrays */
#define ARRAY_SIZE   256

/* represent a normalized 0 -> 1000 = 0.0 -> 1.0 */
#define FUZZY_RADIX NORMAL

/* fuzzy max (ternary) operator */
#define FUZ_MAX( x, y )    ((x>y) ? x : y)

/* fuzzy min (ternary) operator */
#define FUZ_MIN( x, y )    ((x<y) ? x : y)

/* fuzzy AND operator */
#define FUZ_AND( x, y )    FUZ_MIN( x, y )

/* fuzzy OR operator */
#define FUZ_OR( x, y )     FUZ_MAX( x, y )

/* fuzzy compliment operator */
#define FUZ_NOT( x )       ( FUZZY_RADIX - x )

/* create the membership function space */
/* first is the torque error  fuzzy set */
struct s_torq_members {
      int neg_large[ARRAY_SIZE];
      int neg_med[ARRAY_SIZE];
      int zero[ARRAY_SIZE];
      int pos_med[ARRAY_SIZE];
      int pos_large[ARRAY_SIZE];
      float slope;
      float intercept;
}  torq_members;

/* next is the rate of change fuzzy set torque error */
struct s_deri_members {
      int pos_large[ARRAY_SIZE];
      int pos_med[ARRAY_SIZE];
      int zero[ARRAY_SIZE];
      int neg_med[ARRAY_SIZE];
      int neg_large[ARRAY_SIZE];
      float slope;
      float intercept;
} deri_members;

/* last is the PWM fuzzy set ouput */
struct s_pwm_members {
      int neg_large[ARRAY_SIZE];
      int neg_med[ARRAY_SIZE];
      int zero[ARRAY_SIZE];
      int pos_med[ARRAY_SIZE];
      int pos_large[ARRAY_SIZE];
      float slope;
      float intercept;
} pwm_members;

/* define output rule table members */
#define L            pwm_members.pos_large
#define M            pwm_members.pos_med
#define Z            pwm_members.zero
#define NM           pwm_members.neg_med
#define NL           pwm_members.neg_large

/* finally the fuzzy sets */
/* torque feedback error */
int *dTerr_dt[] = {
              deri_members.pos_large  ,
              deri_members.pos_med    ,
              deri_members.zero       ,
              deri_members.neg_med    ,
              deri_members.neg_large  ,
              };
/* torque (OZ-in) */
int *Terror[] = {
              torq_members.neg_large  ,
              torq_members.neg_med    ,
              torq_members.zero       ,
              torq_members.pos_med    ,
              torq_members.pos_large  ,
              }

/* create the rule table and allocate space */
struct  s_rule {
       int *table[TORQUE_MEMBERS];
} rule[DER_MEMBERS] =
       /* */
       {{Z,   NM,  NM,  NM,  NL },
       {M,    Z,   NM,  NM,  NL },
       { L,   M,   Z,   NM,  NL },
       { L,   M,   M,   Z,   NM  },
       { L,    M,   M,   M,   Z }};
       
The Fuzzy membership functions are stored in a look up table for all input
and output fuzzy sets. Fuzzy variables with three digits of precision are
normalized between 0 and 255 integer corresponding to an 8-bit radix converter
and a fuzzy set 0.0 - 1.0 normalized value. Look up tables increase bandwidth
and allow the servo to run with few floating point calculations.

/****************************************************
       File: fuzzy.c
       Date: 4/3/93
       Author: Jack J. McCauley
       fuzzy torque controller for motor
****************************************************/

#include "stdio.h"
#include "stdlib.h"
#include "conio.h"
#include "proto.h"
#include "fuzz.h"      /* the above file */

/****************************************************
       Routine: calc_slope
       Date: 4/3/93
       Author: Jack J. McCauley
       calcs line slope of two points in a plane
*****************************************************/
float calc_slope( float x1, float x2, float y1, float y2 ) {

       float slope;

       if ( x1 == x2 )
             slope = 10000000.0;
       else if( y1 == y2 )
             slope = 0.0;
       else {
             slope = (y1 - y2)/(x1 - x2);
             if( slope > 100000000.0 )
                    slope = 100000000.0;
       }
       return( slope );
}
/*****************************************************
       Routine: calc_intercept
       Date: 4/3/93
       Author: Jack J. McCauley
       calcs line intercept of two points in a plane
*****************************************************/
float calc_intercept( float x1, float x2, float y1, float y2 ) {

       float intercept;
       
       if ( x1 == x2 )
             intercept = 100000000.0;
       else {
             intercept = (y2*x1 - y1*x2)/(x1 - x2);
             if( intercept > 100000000.0 )
                    intercept = 100000000.0;
       }
       return( intercept );
}
/* end print_array */

/**********************************************************
   Routine: down_load()
   Date: 4/3/93
       Author: Jack J. McCauley
initialize fuzzy membership function tables from serial port DRIVER not shown
***********************************************************/
void down_load( int *membership_function, int len )
{
       int k;
       char buff[32];
       
       /*short, tight loop */
       for( k=0; k< len; k++ ) {
             /* get ascci string from driver */
             gets( buff, 12 );
             *membership_function++: atoi( buff );
       }
}
/* end down_load() */
/********************************************************
   Routine: fuzzy_init
   Date: 4/3/93
       Author: Jack J. McCauley
       initialize fuzzy membership function tables
*********************************************************/
void fuzzy_init( void )
{
   int k;
   long slope;
       long intercept;
       int val;
       
/*
There are several ways to generate these tables:

1) The easiest is to simply use ROM space and store them permanently in memory. FUZZ.H
would need to be modified to reflect the static ROM delecerations for each fuzzy
set and the values would be initialize directly attaching them to the data arrays.

2) Down load the fuzzy sets through the serial port as I did in the
tuning of this servo. In which case I've included that code here (of
course in the ROM version of the system you'll need 1)).

3) Store the slope-intercept form of each membership function and
calculate the line slopes and intercepts
*/

/*
Membership functions are downloaded through the serial port from a file in ASCII
format one set member at a time. In this system I used a spreadsheet and
graphical interface to actually draw the membership functions with a mouse.
This aided in tuning the servo substantailly. In the ROH version of the system,
I wrote a small programm to append the RDM statics memberships to fuzz.h.
*/
       down_load( deri_members.neg_large, ARRAY_SIZE);
       down_load( torq_members.neg_large, ARRAY_SIZE);
       down_load( pwm_members.neg_large, ARRAY_SIZE);
       down_load( deri_members.neg_med, ARRAY_SIZE);
       down_load( torq_members.neg_med, ARRAY_SIZE);
       down_load( deri_members.zero, ARRAY_SIZE);
       down_load( torq_members.zero, ARRAY_SIZE);
       down_load( pwm_members.zero, ARRAY_SIZE);
       down_load( deri_members.pos_med, ARRAY_SIZE);
       down_load( orq_members.pos_med, ARRAY_SIZE);
       down_load( pwm_members.pos_med, ARRAY_SIZE);
       down_load( deri_members.pos_large, ARRAY_SIZE);
       down_load( torq_members.pos_large, ARRAY_SIZE);
       down_load( pwm_members.pos_large, ARRAY_SIZE);
       
       /* slope intercept line calculation */
       

       /* these line equations are used for scaling the
         crisp values from the above arrays */
       deri_members.slope = calc_slope(MIN_DERI, MAX_DERI, 0, ARRAY_SIZE-1 );
       deri_members.intercept = calc_intercept(MIN_DERI, MAX_DERI, 0, ARRAY_SIZE-1
       
       torq_members.slope: calc_slope( MIN_TORQUE, MAX_TORQUE, 0, ARRAY SIZE-1 );
       torq_members.intercept = calc_intercept(MIN_TORQUE, MAX_TORQUE, 0, ARRAY_SIZE-1);
       
       pwm_members.slope = calc_slope(0, ARRAY_SIZE-1, MIN_PWM, MAX_PWM);
       pwm_members.intercept = calc_intercept(0, ARRAY_SIZE-1, MIN_PWM, MAX_PWM);
/*
       for( k=0; k<ARRAY_SIZE; k++ )
             printf("%d %4d %4d %4d %4d
%4d\n["],k,deri_members.pos_large[k],deri_members.pos_med[k],
deri_members.zero[k],deri_members.neg_med[k],deri_memb
ers.neg_large[k]);
*/
}
/* end FUZZINIT */

/***********************************************
       Routine: defuzzify_COA
       Date: 4/3/93
       Author: Jack J. McCauley
       defuzzify using COA
**********************************************/
float defuzzify_COA( int *output ) {
       
       static long k;
       static long numerator, denominator, val;
       
       numerator = 0;
       denominator = 0;
       
       for ( k=0; k<ARRAY_SIZE; k+=SKIP ) {
       
             val = (long)*output;
             output+=SKIP;
             /* look for non-zero values and include in our COA calc */
             if( val ) {
                    denominator += val;
                    /* ROM based later */
                    numerator += val *(long)k;
             }
       }
       /* divide if non-zero x/D */
       if( denominator ) {
             /* return crisp value */
             /* Could be converted from float if desired */
             numerator = (long)((float)(numerator/denominator) * pwm_members.slope +
       pwm_members.intercept);
             return( numerator );
       } else
             return( 0 );

}
/* end COA calc */

/******************************************************
       Routine: fuzzify
       Date: 4/3/93
       Author: Jack J. McCauley
       fuzzifier for our servo
******************************************************/
int fuzzify(    int derr_dt, int err,
             int *p_Terror, int *p_dTerror,
             int *p_out, int *resultant ) {

       static int k, val, alpha_cut, temp, *moe;

              /*         COA method         */
       /*
Get the alpha cut for error and derivative. Use the AND (min) operator and cut the
output , a non-zero fuzzy MIN indicates a rule has fired , write the cut to the result
array by "shadowing" the exisiting using the MAX operator
       */

       /* normalize the output derivative */
       moe: resultant;

       /* Find out if the rule fired. If the rule fired then get the alpha cut */
       if( (alpha_cut = FUZ_AND( p_dTerror[derr_dt], p_Terror[err] )) != 0 ) {
             for ( k=0; k<ARRAY_SIZE; k+=SKIP ) {
             /*
             An interesting effect will be noticed if the skip is set greater than
              1. In this system setting skip to lets say 2 or three will yield
             in most circumstance yield the same defuzzified crisp output if the
              slope of the membership functions are not too steep.
             The other benifit is that the execution speed is increased greatly
             */
                    /* create shadow */
                    val = *p_out;
                    /* don't get bit by shortcuts *./
                    val = FUZ=MIN( alpha_cut, val );
                    temp = *resultant;
                    * resultant = FUZ_MAX( temp, val );
                    resultant+=SKIP;
                    p_out+=SKIP;
             }
             /* rule fired */
             return( 1 );
       } else
             /* rule didn't fire */
             return( 0 );
}
/* end FUZZIFICATION calc */

/********************************************************
       Routine: servo_torque
       Date: 4/3/93
       Author: Jack J. McCauley
       fuzzy servos to torque set point
********************************************************/
/*
Routine would run as an ISR attched to a timer interrupt passed values are read
from and A/D convertor and command torque (serial port etc..)
*/
long servo_torque( int error, int derr_dt )
{
       /* statics for speed */
       static int row, column;
       
       /* pointers to tables */
       static int *p_out, *p_dTerr_dt, *p_Terror;
       
       /* COA container class */
       static int resultant[ARRAY_SIZE];
       
       /* zero fill the array */
       for( row = 0; row<ARRAY_SIZE; row++ )
             resultant[row] = 0;
       
       /* normalizing the error derivative will allow the error to ff
       /* normalize error derivative to look up table RADIX*/
       error = (long)((float)error * torq_members.slope + torq_members.intercept);
       
       /* normalize error derivative*/
       derr_dt = (long)((float)derr_dt*deri_members.slope + deri_members.intercept);
       
       /* MAX and MIN error */
       if( error > MAX_ERROR )
             error = MAX_ERROR;
       else if( error < MIN_ERROR )
             error = MIN_ERROR;
       
       /* MAX and MIN derivative */
       if( derr_dt > MAX_DERI )
             derr_dt = MAX_DERI;
       else if( derr_dt < MIN_DERI )
             derr_dt = MIN_DERI;
       
       /* traverse the rule table and evaluate the rules */
       for( row = 0; row < DER_MEMBERS; row++ ) {
       
             /* get pointers to tables from data structures */
             p_dTerr_dt = dTerr_dt[row];
             for( column = 0; column < TORQUE_MEMBERS; column++ ) {
             
                    /* get pointers to tables from data structures */
                    /* get the output membership function for that rule evaluation */
                    p_out = rule[row].table[column];
                    p_Terror = Terror[column];
                    /* fuzzify the rule */
                    fuzzify( derr_dt, error, p_Terror, p_dTerr_dt, p_out, resultant );
             }
       }
       
       /* defuzzify using COA */
       return( defuzzify_COA( resultant ) );
       
       /* return the fired rules */
}

/*END */


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.