Easy-to-Understand Learning for Fuzzy Logic Controller with Arduino UNO [EP1]

บทความนี้เป็นเรียนรู้การสร้างตัวควบคุมฟัซซี่ลอจิก (Fuzzy Logic Controller) แบบ Mamdani ที่จะอธิบายการออกแบบการควบคุม คำนวณค่าต่างๆ พร้อมตัวอย่างให้เข้าใจง่ายขึ้น โดยใช้บอร์ด Arduino UNO ในการทดลองการทำงานด้วยการจำลองผลผ่านเว็บไซต์ www.tinkercad.com ซึ่งโค้ดโปรแกรมการทำงานสำหรับบอร์ด Arduino ไม่ต้องใช้ไลบรารีภายนอกเพิ่มเติม โดยในการออกแบบนี้จะรับสัญญาณอินพุตเข้า 1 ค่า (อุณหภูมิ) และส่งสัญญาณเอาพุตออก 1 ค่า (ปรับความเร็วพัดลม) ให้ได้ระดับอุณหภูมิที่เหมาะสมที่ประมาณ 25 องศาเซลเซียส
การออกแบบระบบ (System Design)
ข้อมูลอินพุตเข้าคือ ค่าอุณหภูมิ (0°C ถึง 50°C ช่วงอ่านค่า ADC 0-1023) ด้วยเซนเซอร์รับสัญญาณอะนาลอก ซึ่งถูกกำหนดให้อยู่ใน 3 ฟัซซีเซต (Fuzzy Sets) ได้แก่ เย็น (Cold), อุ่น (Warm) และร้อน (Hot)
ข้อมูลส่งเอาพุตออกคือ สัญญาณ PWM สำหรับควบคุมความเร็วพัดลม (0 ถึง 255) ซึ่งถูกกำหนดเป็นค่าคงที่ฟัซซี (Fuzzy Singletons) 3 ระดับ ได้แก่ ช้า (Slow = 50), ปานกลาง (Medium = 150) และเร็ว (Fast = 250)
กฎฟัซซี (Fuzzy Rules)
- IF Temperature is Cold, THEN Fan Speed is Slow.
- IF Temperature is Warm, THEN Fan Speed is Medium.
- IF Temperature is Hot, THEN Fan Speed is Fast.

ในรูปที่ 1 แสดงกราฟฟังก์ชันความเป็นสมาชิก (Membership Function) ของอุณหภูมิที่ถูกสร้างขึ้นตามโค้ด โดยแบ่งตามกลุ่มตัวแปรภาษา (Linguistic Variables) ทั้ง 3 ระดับ ได้แก่ Cold, Warm และ Hot ซึ่งมีรายละเอียดของกราฟดังนี้
1. รายละเอียดของแต่ละฟังก์ชันความเป็นสมาชิก (Membership Functions)
- Cold (เส้นสีน้ำเงิน) เป็นฟังก์ชันแบบคางหมูฝั่งซ้าย (Left-shoulder Trapezoidal)
- ถ้า temperature≤15: จะมีค่าระดับความเป็นสมาชิก (μCold) เท่ากับ 1.0
- ถ้า 15<temperature<25: ค่าความลาดเอียงจะลดลงแบบเส้นตรงจาก 1.0 ไปเป็น 0.0 ตามสมการ μCold= (25.0−temperature)/(25.0−15.0)
- ถ้า temperature≥25: จะมีค่าเท่ากับ 0.0
- Warm (เส้นสีส้ม) เป็นฟังก์ชันแบบสามเหลี่ยม (Triangular)
- ถ้า temperature<15 หรือ temperature>35: จะมีค่าระดับความเป็นสมาชิก (μWarm) เท่ากับ 0.0
- ถ้า 15≤temperature≤25: ค่าความลาดเอียงจะเพิ่มขึ้นแบบเส้นตรงจาก 0.0 ไปเป็น 1.0 ตามสมการ μWarm=(temperature−15.0)/(25.0−15.0)
- ถ้า 25<temperature≤35: ค่าความลาดเอียงจะลดลงแบบเส้นตรงจาก 1.0 ไปเป็น 0.0 ตามสมการ μWarm=(35.0−temperature)/(35.0−25.0)
- Hot (เส้นสีแดง) เป็นฟังก์ชันแบบคางหมูฝั่งขวา (Right-shoulder Trapezoidal)
- ถ้า temperature≤25: จะมีค่าระดับความเป็นสมาชิก (μHot) เท่ากับ 0.0
- ถ้า 25<temperature<35: ค่าความลาดเอียงจะเพิ่มขึ้นแบบเส้นตรงจาก 0.0 ไปเป็น 1.0 ตามสมการ μHot=(temperature−25.0)/(35.0−25.0)
- ถ้า temperature≥35: จะมีค่าเท่ากับ 1.0
2. ส่วนของผลลัพธ์ (Output Singletons) สำหรับขั้นตอน Defuzzification
ต่อมาจะเป็นการหาค่าเฉลี่ยแบบถ่วงน้ำหนัก (Weighted Average / Sugeno-style) โดยกำหนดค่าผลลัพธ์แบบจุดเดียว (Singletons) ของความเร็วพัดลมไว้ที่
- Slow Speed = 50.0 (เชื่อมโยงกับ Cold)
- Medium Speed = 150.0 (เชื่อมโยงกับ Warm)
- Fast Speed = 250.0 (เชื่อมโยงกับ Hot)
สุดท้ายจะนำค่าที่ได้มาคำนวณหาค่าเฉลี่ยถ่วงน้ำหนักด้วยสมการ
fanPWM=(μCold×Slow)+(μWarm×Medium)+(μHot×Fast)/(μCold+μWarm+μHot)
fanPWM=(μCold×50)+(μWarm×150)+(μHot×250)/(μCold+μWarm+μHot)
// Pin Configurations
const int tempPin = A0; // Analog input (e.g., potentiometer or thermistor)
const int fanPin = 9; // PWM output to motor driver or fan
void setup() {
Serial.begin(9600);
pinMode(fanPin, OUTPUT);
}
void loop() {
// 1. Read input and convert to crisp value (0.0 to 50.0 degrees Celsius)
int rawAnalog = analogRead(tempPin);
float temperature = map(rawAnalog, 0, 1023, 0, 50);
// 2. Fuzzification (Calculate membership values between 0.0 and 1.0)
float mu_Cold = 0.0;
float mu_Warm = 0.0;
float mu_Hot = 0.0;
// Membership function for COLD (Trapezoidal/Left-shoulder)
if (temperature <= 15) mu_Cold = 1.0;
else if (temperature > 15 && temperature < 25) mu_Cold = (25.0 - temperature) / (25.0 - 15.0);
else mu_Cold = 0.0;
// Membership function for WARM (Triangular)
if (temperature >= 15 && temperature <= 25) mu_Warm = (temperature - 15.0) / (25.0 - 15.0);
else if (temperature > 25 && temperature <= 35) mu_Warm = (35.0 - temperature) / (35.0 - 25.0);
else mu_Warm = 0.0;
// Membership function for HOT (Trapezoidal/Right-shoulder)
if (temperature >= 35) mu_Hot = 1.0;
else if (temperature > 25 && temperature < 35) mu_Hot = (temperature - 25.0) / (35.0 - 25.0);
else mu_Hot = 0.0;
// 3. Rule Evaluation & Defuzzification (Weighted Average Method / Sugeno-style simplified)
// Output singletons: Slow = 50, Medium = 150, Fast = 250
float slowSpeed = 50.0;
float medSpeed = 150.0;
float fastSpeed = 250.0;
// Calculate sum of weights and weighted outputs
float totalMembership = mu_Cold + mu_Warm + mu_Hot;
int fanPWM = 0;
if (totalMembership > 0) {
// Defuzzification equation: (Σ μ_i * Output_i) / Σ μ_i
fanPWM = (int)((mu_Cold * slowSpeed + mu_Warm * medSpeed + mu_Hot * fastSpeed) / totalMembership);
} else {
fanPWM = 0;
}
// 4. Actuate Output
analogWrite(fanPin, fanPWM);
// Debugging log to Serial Monitor
Serial.print("Temp: "); Serial.print(temperature);
Serial.print(" C | Memberships -> Cold: "); Serial.print(mu_Cold);
Serial.print(" Warm: "); Serial.print(mu_Warm);
Serial.print(" Hot: "); Serial.print(mu_Hot);
Serial.print(" | Output Fan PWM: "); Serial.println(fanPWM);
delay(100); // Read and adjust twice a second
}
การคำนวณหาค่าผลลัพธ์การทำงานทีละขั้นตอน เมื่อกำหนดค่าอุณหภูมิที่วัดได้เท่ากับ 28 เซลเซียส
1. Fuzzification Stage
อุณหภูมิอินพุตที่วัดค่าได้เท่ากับ 28°C อยู่ประมาณกึ่งกลางระหว่างฟังก์ชันความเป็นสมาชิกของ Warm (อุ่น) และ Hot (ร้อน) จากนั้นจะพิจารณาฟังก์ชันความเป็นสมาชิกตามหัวข้อที่ 1 ที่กล่าวข้างต้น
- Cold Membership: 0.0 (ค่าอุณหภูมิ 28 เซลเซียส ไม่อยู่ในช่วงที่กำหนด)
- Warm Membership: (35.0 – 28.0)/(35.0 – 25.0) = 7.0/10.0 = 0.7
- Hot Membership: (28.0 – 25.0)/(35.0 – 25.0) = 3.0/10.0 = 0.3
2. Defuzzification Math
คำนวณด้วยสมการหาค่าเฉลี่ยแบบถ่วงน้ำหนัก (Weighted Average)
fanPWM=(μCold×Slow)+(μWarm×Medium)+(μHot×Fast)/(μCold+μWarm+μHot)
fanPWM= (0×50)+(0.7×150)+(0.3×250)/(0.7+0.3) = 180/1.0 = 180.
3. Final Controller Output
ผลลัพธ์ที่ได้จากการคำนวณ Defuzzification Math จะเป็นกำลังขับพัดลม 180 (PWM Signal) หรือคิดเป็นประมาณ 70% ของกำลังพัดลมสูงสุด โดยมีการปรับระดับขึ้นอย่างนุ่มนวล แทนการสลับค่าไปมาระหว่างระดับที่กำหนดไว้อย่างรวดเร็ว

ในรูปที่ 2 แสดงลักษณะของวงจรสำหรับจำลองการทำงานในเว็บ www.tinkercad.com โดยใช้โปรแกรม Arduino ข้างบนในการทดลอง สำหรับในส่วนของสัญญาณอินพุตวัดค่าอุณหภูมิเข้าที่ขา A0 จะใช้ตัวต้านทานปรับค่า (VR) แทนเซนเซอร์อุณหภูมิ ซึ่งสามารถปรับค่าได้ในช่วง 0-50 องศาเซลเซียส และมีโวลต์มิเตอร์แสดงผลให้ทราบใกล้กัน ในส่วนสัญญาณเอาต์พุต PWM จะจ่ายออกมาที่ขา D9 ให้กับแอลอีดี และใช้ออสซิลโลสโคปวัดสัญญาณให้เห็นการเปลี่ยนแปลงได้ง่ายยิ่งขึ้น



สำหรับในรูปที่ 3 ถึงรูปที่ 5 เป็นผลการทดลองเมื่อปรับค่าอุณหภูมิอินพุตที่ 16, 28 และ 35 องศาเซลเซียส จะสังเกตเห็นว่าที่อุณอุณหภูมิต่ำ (16 องศาเซลเซียส) ขนาดความกว้างของสัญญาณ PWM จะน้อยลง เพื่อให้พัดลมหมุนด้วยความเร็วต่ำๆ ในทางกลับกัน กรณีที่อุณหภูมิสูงขึ้น (35 องศาเซลเซียส) ขนาดความกว้างของสัญญาณ PWM จะเพิ่มขึ้น เพื่อให้พัดลมหมุนด้วยความเร็วสูง และที่อุณอุณหภูมิสูง 28 องศาเซลเซียส (ในรูปที่ 4) จะมีค่าของสัญญาณ PWM ปานกลางไปค่อนข้างสูง โดยขนาดสัญญาณพัลซ์วิดธ์จะมีค่าเท่ากับ 180 ตามตัวอย่างที่กล่าวข้างต้นแสดงในกรอบสีเขียว สำหรับเนื้อหาในบทความตอนที่ 1 นี้ ยังคงเป็นการควบคุมแบบทั่วไป และหวังว่าจะช่วยให้ผู้อ่านเข้าใจการควบคุมแบบฟัซซี่ลอจิกเพิ่มขึ้นครับ.
Reference
- https://en.wikipedia.org/wiki/Fuzzy_control_system
- https://forum.arduino.cc/t/how-make-fuzzy-logic-coding-for-arduino-uno/282381/4
- https://github.com/alvesoaj/eFLL
- https://github.com/amimaro/FuzzyLibrary
- https://fuzzylite.com/
- https://fuzzylite.com/downloads/FuzzyLite-Libraries.pdf
- https://www.microjpm.com/products/ad52300/
- https://www.tinkercad.com/dashboard
- https://www.circuitbasics.com/arduino-thermistor-temperature-sensor-tutorial/
- https://gemini.google.com/
- https://copilot.microsoft.com