The source code presented in this section provides a number of patterns for fuzzy logic software development, with a sample application built around them to demonstrate the capabilities. We'll discuss the generic fuzzy logic functions first and then discuss the simulation.
The fuzzy logic API provides two elements to fuzzy logic software developers. The first are the fuzzy operators and the second are the membership helper functions.
The fuzzy operators provide fuzzy versions of the AND, OR, and NOT functions, commonly found in conventional logic. Listing 9.1 shows these functions.
#define MAX(a,b) ((a>b) ? a : b) #define MIN(a,b) ((a<b) ? a : b) fuzzyType fuzzyAnd( fuzzyType a, fuzzyType b ) { return MAX(a,b); } fuzzyType fuzzyOr( fuzzyType a, fuzzyType b ) { return MIN(a,b); } fuzzyType fuzzyNot( fuzzyType a ) { return( 1.0 - a ); }
These functions follow the initially-discussed fuzzy axioms in Figure 9.5.
The next set of APIs provides the ability to create the membership functions very easily. These functions permit the developer to define the outline of the function with a set of values defining the functions profile. These functions are shown in Listing 9.2.
fuzzyType spikeProfile( float value, float lo, float high ) { float peak; value += (-lo); if ((lo < 0) && (high < 0)) { high = -(high - lo); } else if ((lo < 0) && (high > 0)) { high += -lo; } else if ((lo > 0) && (high > 0)) { high -= lo; } peak = (high / 2.0); lo = 0.0; if (value < peak) { return( value / peak ); } else if (value > peak) { return( (high-value) / peak ); } return 1.0; } fuzzyType plateauProfile( float value, float lo, float lo_plat, float hi_plat, float hi ) { float upslope; float downslope; value += (-lo); if (lo < 0.0) { lo_plat += -lo; hi_plat += -lo; hi += -lo; lo = 0; } else { lo_plat -= lo; hi_plat -= lo; hi -= lo; lo = 0; } upslope = (1.0 / (lo_plat - lo)); downslope = (1.0 / (hi - hi_plat)); if (value < lo) return 0.0; else if (value > hi) return 0.0; else if ((value >= lo_plat) && (value <= hi_plat)) return 1.0; else if (value < lo_plat) return ((value-lo) * upslope); else if (value > hi_plat) return ((hi-value) * downslope); return 0.0; }
The first function, spikeProfile , defines the typical triangle-shaped membership function (for example, the center membership function in Figure 9.3). The developer provides the lo and hi values which define base end-points for the triangle. The peak point is defined as hi/2 .
The second function, plateauProfile , defines the trapezoidal-shaped membership function (as illustrated by the Warm membership function in Figure 9.7). The membership functions that extend to the boundaries (such as the Cold and Hot functions in Figure 9.7) are also created using the plateauProfile function.
The purpose of these functions is to identify the degree of membership for a given value and profile vector.
Let's now look at the code specific to the battery simulation. The first set of functions that we'll look at are the membership functions. These functions use the previously defined helper functions to build the shapes as shown in the membership graphs.
The first set of functions are the voltage membership functions (see Listing 9.3).
fuzzyType m_voltage_low( float voltage ) { const float lo = 5.0; const float lo_plat = 5.0; const float hi_plat = 5.0; const float hi = 10.0; if (voltage < lo) return 1.0; if (voltage > hi) return 0.0; return plateauProfile( voltage, lo, lo_plat, hi_plat, hi ); } fuzzyType m_voltage_medium( float voltage ) { const float lo = 5.0; const float lo_plat = 10.0; const float hi_plat = 20.0; const float hi = 25.0; if (voltage < lo) return 0.0; if (voltage > hi) return 0.0; return plateauProfile( voltage, lo, lo_plat, hi_plat, hi ); } fuzzyType m_voltage_high( float voltage ) { const float lo = 25.0; const float lo_plat = 30.0; const float hi_plat = 30.0; const float hi = 30.0; if (voltage < lo) return 0.0; if (voltage > hi) return 1.0; return plateauProfile( voltage, lo, lo_plat, hi_plat, hi ); }
Each of the membership functions in Listing 9.3 utilizes the plateauProfile function to build the required shape. Each function accepts a voltage value. A value is then returned which is the degree of membership to the particular membership function. Each function initially checks to see if the passed value is outside of the limits of the membership function. If so, the respective return value is generated. Otherwise, the value is passed to the plateauProfile function with the profile defined by the [ lo , lo_plat , hi_plat , hi ] vector, and the resulting return value is returned to the caller.
The membership functions in Listing 9.3 are show graphically in Figure 9.6.
Listing 9.4 provides the temperature membership functions for the sample charging application.
fuzzyType m_temp_cold( float temp ) { const float lo = 15.0; const float lo_plat = 15.0; const float hi_plat = 15.0; const float hi = 25.0; if (temp < lo) return 1.0; if (temp > hi) return 0.0; return plateauProfile( temp, lo, lo_plat, hi_plat, hi ); } fuzzyType m_voltage_low( float voltage ) { const float lo = 5.0; const float lo_plat = 5.0; const float hi_plat = 5.0; const float hi = 10.0; if (voltage < lo) return 1.0; if (voltage > hi) return 0.0; return plateauProfile( voltage, lo, lo_plat, hi_plat, hi ); } fuzzyType m_voltage_medium( float voltage ) { const float lo = 5.0; const float lo_plat = 10.0; const float hi_plat = 20.0; const float hi = 25.0; if (voltage < lo) return 0.0; if (voltage > hi) return 0.0; return plateauProfile( voltage, lo, lo_plat, hi_plat, hi ); }
The temperature functions provided in Listing 9.4 are illustrated graphically in Figure 9.7.
Controlling the mode of the battery is now a simple function of implementing the previously defined fuzzy rules. The chargeControl function in Listing 9.5 provides the controller function.
void chargeControl( ) { static unsigned int i = 0; extern float voltage, temperature; if ( (i++ % 10) == 0 ) { if (normalize( m_voltage_high( voltage ) ) ) { chargeMode = TRICKLE_CHARGE; } else if (normalize( m_temp_hot( temperature ) ) ) { chargeMode = TRICKLE_CHARGE; } else if (normalize( fuzzyAnd( fuzzyNot( m_voltage_high( voltage ) ), fuzzyNot( m_temp_hot( temperature ) ) ) ) ) { chargeMode = FAST_CHARGE; } } }
This function changes the charge mode based upon the values of voltage and temperature using the fuzzy rules and membership functions.
Finally, the simulation main loop exercises the simulator and charge control function to properly charge the battery given the environmental parameters of voltage and temperature. The main loop is shown in Listing 9.6.
int main() { int i; extern int simulate(void); extern void chargeControl( float * ); extern float voltage; extern float temperature; extern int chargeMode; for (i = 0 ; i < 3000 ; i++) { simulate(); chargeControl(); printf("%d, %f, %f, %d\n", i, voltage, temperature, chargeMode ); } return 0; }
On the CD | As shown, the simulator simply loops through, calling the simulator and then allowing the charge controller function to identify in which mode the charger should be. The simulator is not shown here in the text, but is provided on the CD-ROM. |
The simulation is visualized in Figure 9.8. This plot shows the voltage, temperature, and charge mode (as emitted by the main function). The presence of input charger voltage is a half sine wave (to simulate 50% sunlight on a set of solar panels). As shown, the battery charge maintains proper charge of the battery given the loading on the battery and presence of input current for battery charging.
While the simulation does not represent a true physical battery simulation, it models some of the basic concepts of loading and charging given the voltage and temperature constraints.