[comp.ai] Fuzzy Logic Controller Demo, C language source

wsnell@sdcc13.ucsd.edu (Wesley Snell) (05/27/91)

#if 0
*****************************************************************************

          Title: Truck Backer-Upper Demo

    Description: Fuzzy Control System Demo Program

        This program uses fuzzy control to backup a truck to a given
        position and to arrive at the position (X = 200) at a given angle
        (90 degrees).  The truck may be moved by the user to establish an
        initial truck position and angle.  The program does not take into
        account the Y coordinate of the truck position, therefore starting
        positions for the truck demo should be made accordingly.

        User commands are listed below and the function 'KeyCommand()' can
        be examined for a more detailed explaination of their functions.

    
       Commands: g, G    Enable truck to GO
                 s, S    STOP truck
                 l, L    Turn steering wheel 1 degree to the LEFT
                 r, R    Turn steering wheel 1 degree to the RIGHT
                 f, F    Enable FUZZY Controller
                 t, T    Toggle truck trail
                 c, C    CLEAR demo area
                 n, N    RESET truck demo
                 q, Q    EXIT program


     MODIFICATIONS
        The fuzzy controller may be modified to improve its performance and
        to test it.  The fuzzy controller may be modified by changing its
        rules and membership functions.

        Rules may be modified by changing the consequent in 'gaiRules[]'.
        Membership functions may be modified by changing the values used by
        'gadbPosition[][]', 'gadbAngle[][]' and 'gadbWheelAngle[][]'.



 compiler used: Microsoft C Optimizing Compiler   Version 5.10
   linker used: Microsoft Overlay Linker   Version 3.65

    programmer: Donald Carothers
                3674 Ben St., San Diego CA 92111
          date: 11/06/90

*****************************************************************************
#endif



#include <conio.h>
#include <stdio.h>
#include <graph.h>
#include <math.h>



#define loop while (1)
#define BOOL int
#define RadianToDegree(x)	(x * (180.0 / 3.14159))
#define DegreeToRadian(x)	(x * (3.14159 / 180.0))
#define min(x, y) ((x > y) ? y : x)


#define TRUE  1
#define FALSE 0

#define BLACK   0
#define BLUE	1
#define GREY    8
#define GREEN  10
#define CYAN   11
#define RED    12
#define YELLOW 14
#define WHITE  15


#define CLR_BCKGRND      BLUE
#define CLR_TRUCK        GREEN
#define CLR_TRUCK_TRAIL  WHITE

#define CLR_ACTIVE_FG		YELLOW
#define CLR_INACTIVE_FG     GREY




/*
**	Generic Fuzzy Indexes
*/

#define NL 0		/* Negative Large  */
#define NM 1		/* Negative Medium */
#define NS 2		/* Negative Small  */
#define ZE 3		/* Zero */
#define PS 4		/* Positive Small  */
#define PM 5		/* Positive Medium */
#define PL 6		/* Positive Large  */

/*
**	Steering Wheel Output Indexes
*/

#define RL NL 		/* Right Large  */
#define RM NM 		/* Right Medium */
#define RS NS 		/* Right Small  */
#define CE ZE 		/* Centered */
#define LS PS 		/* Left Small  */
#define LM PM 		/* Left Medium */
#define LL PL 		/* Left Large  */




/*
**	RULES
*/

/*
	Each rule has two antecedents, and one consequent.
		(truck position, truck angle, truck steering wheel angle)
*/


#define NUM_RULES 49

int gaiRules[NUM_RULES][3] =
{	NL, NL, LS,
	NL, NM, LS,
	NL, NS, RM,
	NL, ZE, RM,
	NL, PS, RL,
	NL, PM, RL,
	NL, PL, RL,

	NM, NL, LL,
	NM, NM, LM,
	NM, NS, RS,
	NM, ZE, RM,
	NM, PS, RM,
	NM, PM, RL,
	NM, PL, RL,

	NS, NL, LL,
	NS, NM, LM,
	NS, NS, LS,
	NS, ZE, RS,
	NS, PS, RM,
	NS, PM, RM,
	NS, PL, RL,

	ZE, NL, LM,
	ZE, NM, LM,
	ZE, NS, LS,
	ZE, ZE, CE,
	ZE, PS, RS,
	ZE, PM, RM,
	ZE, PL, RM,

	PS, NL, LL,
	PS, NM, LM,
	PS, NS, LM,
	PS, ZE, LS,
	PS, PS, RS,
	PS, PM, RM,
	PS, PL, RL,

	PM, NL, LL,
	PM, NM, LL,
	PM, NS, LM,
	PM, ZE, LM,
	PM, PS, LS,
	PM, PM, RM,
	PM, PL, RL,

	PL, NL, LL,
	PL, NM, LL,
	PL, NS, LL,
	PL, ZE, LM,
	PL, PS, LM,
	PL, PM, RS,
	PL, PL, RS
};






/*
**	MEMBERSHIP FUNCTIONS
*/

/*
	Each membership function is defined as a piecewise linear triangular
	pulse with a maximum height of 1.  Three values, in order of increasing
	value, are used to specify each	membership function:
		(start of triangular pulse, pulse peak, end of triangular pulse)
*/



double gadbPosition[7][3] =
{		0,   0, 100,		/* NL  Position =   0 */
	   50, 100, 150,		/* NM  Position = 100 */
	  120, 160, 200,		/* NS  Position = 160 */
	  180, 200, 220,		/* ZE  Position = 200 */
	  200, 240, 280,		/* PS  Position = 240 */
	  250, 300, 350,		/* PM  Position = 300 */
	  300, 400, 400			/* PL  Position = 400 */
};


double gadbAngle[7][3] =
{  -100, -45,  10,			/* NL  Truck Angle = -45 */
	-10,  20,  50,			/* NM  Truck Angle =  20 */
	 40,  65,  90,			/* NS  Truck Angle =  65 */
	 80,  90, 100,			/* ZE  Truck Angle =  90 */
	 90, 115, 140,			/* PS  Truck Angle = 115 */
	130, 160, 190,			/* PM  Truck Angle = 160 */
	170, 225, 280			/* PL  Truck Angle = 225 */
};	


double gadbWheelAngle[7][3] =
{	-40, -30, -15,			/* NL  Wheel Angle = -30 */
	-25, -15,  -5,			/* NM  Wheel Angle = -15 */
	-12,  -6,   0,			/* NS  Wheel Angle =  -5 */
	 -5,   0,   5,			/* ZE  Wheel Angle =   0 */
	  0,   6,  12,			/* PS  Wheel Angle =   5 */
	  5,  15,  25,			/* PM  Wheel Angle =  15 */
	 15,  30,  40			/* PL  Wheel Angle =  30 */
};	 


char *gaszInputIndx[7] = { "NL", "NM", "NS", "ZE", "PS", "PM", "PL" };




/*
**	TRUCK Global Variables
*/

double gdbTruckX, gdbTruckY;	/* Position of center of truck */
double gdbTruckAngle;			/* Angle of truck with horizontal */
double gdbTruckWheelAngle;		/* Angle of truck steering wheel */
BOOL gbTruckState;				/* Truck moving (TRUE) */
BOOL gbFuzzyControl;			/* Fuzzy controller on (TRUE) */
int giTruckTrailColor;			/* Color of truck trail */




/*
**	GLOBAL VARIABLES
*/

double gadbPositionMembership[7];	/* Storage for input results */
double gadbAngleMembership[7];

double gadbRuleResult[NUM_RULES];	/* Storage for rule outputs */




/*
**		PROGRAM CODE
*/


main()
{	int x, y;
	char c;

		/* CONVERT degrees to radians */
	for (x = NL ; x <= PL ; x++)
	{	for (y = 0 ; y < 3 ; y++)
		{	gadbAngle[x][y] = DegreeToRadian(gadbAngle[x][y]);
			gadbWheelAngle[x][y] = DegreeToRadian(gadbWheelAngle[x][y]);
		}
	}

	for (x = 0 ; x < NUM_RULES ; x++)
	{	gadbRuleResult[x] = 0.0;
	}


			/* DEFAULT initial truck position */
	gdbTruckX = 200.0;			/* CENTER of truck */
	gdbTruckY = 300.0;
	gdbTruckAngle = DegreeToRadian(45.0);	/* ANGLE of truck body with horizontal */
	gdbTruckWheelAngle = 0.0;	/* Truck steering wheel straight ahead */
	gbTruckState = FALSE;		/* Truck stopped */
	gbFuzzyControl = FALSE;		/* Fuzzy controller disabled */
	giTruckTrailColor = CLR_BCKGRND;

	_setvideomode((short) _VRES16COLOR);	/* VGA 640 X 480 X 16 */
	_setbkcolor(_BLUE);						/* set background color */

	_setcolor(WHITE);
	_rectangle(_GBORDER, 0, 0, 400, 400);	/* Demo area border */
	_settextposition(18, 65);
	_outtext("Position (X)");
	_settextposition(19, 60);
	_outtext("NL NM NS ZE PS PM PL");
	x = 0;
	for (y = 20 ; y < 27 ; y++)
	{	_settextposition(y, 57);
		_outtext(gaszInputIndx[x]);
		x++;
	}



	DisplayTruckStatus();	/* update display of truck info */
	_setcolor(CLR_TRUCK);		/* set color to draw truck */
	DrawTruck();			/* display truck at current position */


	loop
	{	if (kbhit())	/* is key waiting to be processed ? */
		{	KeyCommand();			/* process key */
			DisplayTruckStatus();	/* update display of truck info */
		}

		for (x = 0 ; x < 10000 ; x++)	/* temporary time delay */
			y = x * y;

		if (gbTruckState)	/* Check if truck moving */
		{	
				/* Compute new output with fuzzy control system */
			if (gbFuzzyControl)		/* check if fuzzy control enabled */
				ComputeOutput();	/* determine new output */

			_setcolor(giTruckTrailColor);	/* set color to erase truck */
			DrawTruck();		/* ERASE old truck */

			MoveTruck();	  	/* update truck info */

			_setcolor(CLR_TRUCK);		/* set color to draw truck */
			DrawTruck();	/* display truck at new position */

			DisplayTruckStatus();	/* update display of truck info */
		}

	}	/* End of WHILE */
}	/* End of 'main()' */



ComputeOutput()
{	int i;
	double db, dbWeights, dbOut;

		/* COMPUTE input membership */
	for (i = NL ; i <= PL ; i++)
	{		/* Position Membership */
		if (((double) gdbTruckX >= gadbPosition[i][0]) && ((double) gdbTruckX <= gadbPosition[i][1]))
			gadbPositionMembership[i] = ((double) gdbTruckX - gadbPosition[i][0]) * (1.0 / (gadbPosition[i][1] - gadbPosition[i][0]));
		else if (((double) gdbTruckX >= gadbPosition[i][1]) && ((double) gdbTruckX <= gadbPosition[i][2]))
			gadbPositionMembership[i] = (gadbPosition[i][2] - (double) gdbTruckX) * (1.0 / (gadbPosition[i][2] - gadbPosition[i][1]));
		else
			gadbPositionMembership[i] = 0.0;

			/* Truck Angle Membership */
		if ((gdbTruckAngle >= gadbAngle[i][0]) && (gdbTruckAngle <= gadbAngle[i][1]))
			gadbAngleMembership[i] = (gdbTruckAngle - gadbAngle[i][0]) * (1.0 / (gadbAngle[i][1] - gadbAngle[i][0]));
		else if ((gdbTruckAngle > gadbAngle[i][1]) && (gdbTruckAngle <= gadbAngle[i][2]))
			gadbAngleMembership[i] = (gadbAngle[i][2] - gdbTruckAngle) * (1.0 / (gadbAngle[i][2] - gadbAngle[i][1]));
		else
			gadbAngleMembership[i] = 0.0;
	}

		/* Rules */
	for (i = 0 ; i < NUM_RULES ; i++)
	{	gadbRuleResult[i] = min(gadbPositionMembership[gaiRules[i][0]], gadbAngleMembership[gaiRules[i][1]]);
	}

		/* Compute output */
	dbOut = 0.0;
	dbWeights = 0.0;
	for (i = 0 ; i < NUM_RULES ; i++)
	{	if (gadbRuleResult[i] == 0.0)
			continue;	/* skip computation for non-firing rule */

			/* Weight Value (trapezoidal area) */
		db = (1.0 - gadbRuleResult[i]);
		db = 0.5 * (gadbWheelAngle[gaiRules[i][2]][2] - gadbWheelAngle[gaiRules[i][2]][0]) * (1 - (db * db));

		dbWeights += db;
		dbOut += db * gadbWheelAngle[gaiRules[i][2]][1];
	}	/* End of FOR */

	if (dbWeights != 0.0)
		dbOut /= dbWeights;

	gdbTruckWheelAngle = dbOut;

		/* Impose Wheel limits */
	if (gdbTruckWheelAngle > DegreeToRadian(30.0))
		gdbTruckWheelAngle = DegreeToRadian(30.0);
	else if (gdbTruckWheelAngle < DegreeToRadian(-30.0))
		gdbTruckWheelAngle = DegreeToRadian(-30.0);
}



DrawTruck()
{	int i;
	double dbAngle;
	static struct xycoord sastVertices[7];

	dbAngle = DegreeToRadian(45.0);	/* 45 degrees */

	for (i = 0 ; i < 5 ; i++)
	{	sastVertices[i].xcoord = (int) (gdbTruckX + (4 * 5 * cos(gdbTruckAngle + dbAngle)));
		sastVertices[i].ycoord = (int) (gdbTruckY - (4 * 5 * sin(gdbTruckAngle + dbAngle)));
		dbAngle += DegreeToRadian(90.0);	/* next truck corner */
	}
	sastVertices[5].xcoord = (int) (gdbTruckX + (4 * 4 * cos(gdbTruckAngle - DegreeToRadian(110.0))));
	sastVertices[5].ycoord = (int) (gdbTruckY - (4 * 4 * sin(gdbTruckAngle - DegreeToRadian(110.0))));
	sastVertices[6].xcoord = (int) (gdbTruckX + (4 * 4 * cos(gdbTruckAngle - DegreeToRadian(250.0))));
	sastVertices[6].ycoord = (int) (gdbTruckY - (4 * 4 * sin(gdbTruckAngle - DegreeToRadian(250.0))));

	_moveto(sastVertices[0].xcoord, sastVertices[0].ycoord);
	for (i = 1 ; i < 5 ; i++)
		_lineto(sastVertices[i].xcoord, sastVertices[i].ycoord);
	_moveto(sastVertices[5].xcoord, sastVertices[5].ycoord);
	_lineto(sastVertices[6].xcoord, sastVertices[6].ycoord);

}	/* End of 'DrawTruck()' */




DisplayTruckStatus()
{	int i, x, y;

	static char sszOut[40];
	static char *saszInputIndx[7] = { "NL", "NM", "NS", "ZE", "PS", "PM", "PL" };
	static char *saszOutputIndx[7] = { "RL", "RM", "RS", "CE", "LS", "LM", "LL" };


	_settextcolor(WHITE);
	_setbkcolor(_BLUE);

		/* Display Truck Position */
	_settextposition(3, 53);
	sprintf(sszOut, "Position: X=%5.1lf, Y=%5.1lf", gdbTruckX, gdbTruckY);
	_outtext(sszOut);

		/* Display Truck Angle */
	_settextposition(4, 53);
	sprintf(sszOut, "Truck Angle: %6.1lfx", RadianToDegree(gdbTruckAngle));
	_outtext(sszOut);

		/* Display Truck Steering Wheel Angle */
	_settextposition(6, 53);
	sprintf(sszOut, "Wheel Angle: %6.1lfx", RadianToDegree(gdbTruckWheelAngle));
	_outtext(sszOut);

		/* Display Rule status */
	i = 0;
	for (x = 60 ; x < 81 ; x += 3)
	{	for (y = 20 ; y < 27 ; y += 1)
		{	if (gadbRuleResult[i] == 0.0)
				_settextcolor(CLR_INACTIVE_FG);
			else
				_settextcolor(CLR_ACTIVE_FG);
			_settextposition(y, x);
			_outtext(saszOutputIndx[gaiRules[i][2]]);

			i++;	/* next rule */
		}
	}
	_setbkcolor(_BLUE);
	_settextcolor(WHITE);

}



MoveTruck()
{	
	gdbTruckX += (4 * cos(gdbTruckAngle));
	gdbTruckY -= (4 * sin(gdbTruckAngle));

	if ((gdbTruckY > 380.0) || (gdbTruckX > 380.0) || (gdbTruckX < 20.0))
	{	gdbTruckX -= (4 * cos(gdbTruckAngle));
		gdbTruckY += (4 * sin(gdbTruckAngle));
	}
	else if (gdbTruckY < 20.0)		/* check if simulation complete */
	{	gbTruckState = FALSE;		/* stop truck at boundary */
	}
	else
	{	gdbTruckAngle += gdbTruckWheelAngle;
		while (gdbTruckAngle > DegreeToRadian(270.0))
			gdbTruckAngle -= DegreeToRadian(360.0);
		while (gdbTruckAngle < DegreeToRadian(-90.0))
			gdbTruckAngle += DegreeToRadian(360.0);
	}
}


KeyCommand()
{	int c, x, y;

	c = getch();
	switch(c)
	{	case (int) 'G':			/* GO command */
		case (int) 'g':
				gbTruckState = TRUE;
			break;

		case (int) 'S':			/* STOP command */
		case (int) 's':
				gbTruckState = FALSE;
			break;

		case (int) 'L':			/* LEFT (increase wheel angle 1 degree) */
		case (int) 'l':
				gdbTruckWheelAngle += DegreeToRadian(1.0);
			break;

		case (int) 'R':			/* RIGHT (decrease wheel angle 1 degree) */
		case (int) 'r':
				gdbTruckWheelAngle -= DegreeToRadian(1.0);
			break;


		case (int) 'F':			/* Toggle Fuzzy Control */
		case (int) 'f':
			if (gbFuzzyControl)
				gbFuzzyControl = FALSE;
			else
				gbFuzzyControl = TRUE;
			break;


		case (int) 'T':			/* Toggle truck trail */
		case (int) 't':
			if (giTruckTrailColor == CLR_BCKGRND)
				giTruckTrailColor = CLR_TRUCK_TRAIL;
			else
				giTruckTrailColor = CLR_BCKGRND;
			break;


		case (int) 'N':			/* Reset simulation */
		case (int) 'n':
			_setcolor(CLR_BCKGRND);	/* set color to erase truck */
			DrawTruck();	/* ERASE old truck */

			gdbTruckX = 200.0;		/* CENTER of truck */
			gdbTruckY = 300.0;
			gdbTruckAngle = DegreeToRadian(45.0);	/* ANGLE of truck body with vertical */
			gdbTruckWheelAngle = 0.0;
			gbTruckState = FALSE;
			gbFuzzyControl = FALSE;
			DisplayTruckStatus();

			_setcolor(CLR_TRUCK);		/* set color to draw truck */
			DrawTruck();
			break;


		case (int) 'C':			/* CLEAR demo area command */
		case (int) 'c':
			_clearscreen(_GCLEARSCREEN);
			_setcolor(WHITE);
			_rectangle(_GBORDER, 0, 0, 400, 400);	/* Demo area border */

			_settextposition(18, 65);
			_outtext("Position (X)");
			_settextposition(19, 60);
			_outtext("NL NM NS ZE PS PM PL");
			x = 0;
			for (y = 20 ; y < 27 ; y++)
			{	_settextposition(y, 57);
				_outtext(gaszInputIndx[x]);
				x++;
			}

			DisplayTruckStatus();	/* update display of truck info */
			_setcolor(CLR_TRUCK);		/* set color to draw truck */
			DrawTruck();			/* display truck at current position */
			break;


		case (int) 'Q':			/* QUIT program command */
		case (int) 'q':
				_setvideomode((short) _DEFAULTMODE);	/* TEXT 80 X 25 X 16 */
				exit(0);

		default:
			break;
	}	/* End of SWITCH */

		/* TRUCK steering wheel limits */
	if (gdbTruckWheelAngle > DegreeToRadian(30.0))
		gdbTruckWheelAngle = DegreeToRadian(30.0);
	else if (gdbTruckWheelAngle < DegreeToRadian(-30.0))
		gdbTruckWheelAngle = DegreeToRadian(-30.0);


}	/* End of 'KeyCommand()' */



/*-----------------------------  END OF FILE  -----------------------------*/

pekka@latcs1.lat.oz.au (Pekka Isomursu) (05/31/91)

In article <19792@sdcc6.ucsd.edu> wsnell@sdcc13.ucsd.edu (Wesley Snell) gives 
us C source code for a Fuzzy Control System Demo Program. 

Would someone please point me out other sources of public domain fuzzy 
logic source code? Language doesn't matter, although C is preferable.   

Thanks in advance.

  Pekka Isomursu
  Technical Research Centre of Finland, Oulu, Finland
  visiting La Trobe University, Melbourne, Australia