From c35da9a73ffb3ddbec36c8caf70dcbd3aac261bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=C4=83ng=20Nguy=E1=BB=85n?= Date: Thu, 13 Nov 2025 10:02:04 +0700 Subject: [PATCH] update with speed 2.0 m/s --- .../Navigation/Algorithm/FuzzyLogic.cs | 707 ++++++++++++------ 1 file changed, 482 insertions(+), 225 deletions(-) diff --git a/RobotApp/Services/Robot/Simulation/Navigation/Algorithm/FuzzyLogic.cs b/RobotApp/Services/Robot/Simulation/Navigation/Algorithm/FuzzyLogic.cs index 79e6a16..65fb067 100644 --- a/RobotApp/Services/Robot/Simulation/Navigation/Algorithm/FuzzyLogic.cs +++ b/RobotApp/Services/Robot/Simulation/Navigation/Algorithm/FuzzyLogic.cs @@ -2,251 +2,508 @@ public class FuzzyLogic { - private double Gain_P = 0.5; - private double Gain_I = 0.01; - private double DiscreteTimeIntegrator_DSTATE; + // ============================================================================ + // FUZZY LOGIC CONTROLLER - PHIÊN BẢN GỐC ĐÃ TỔ CHỨC LẠI + // Dải đầu ra: 0.0 - 1.0 | Độ phân giải: 5 mức | Số luật: 25 (5x5) + // ============================================================================ - public FuzzyLogic WithGainP(double gainP) + // ============================================================================ + // PHẦN 1: CÁC HÀM MEMBERSHIP (HÀM THUỘC) + // ============================================================================ + + /// + /// Hàm thuộc hình thang (Trapezoidal Membership Function) + /// Dạng hình thang với 4 điểm: [left_base, left_top, right_top, right_base] + /// + /// left_top ______ right_top + /// / \ + /// / \ + /// _________/ \_________ + /// left_base right_base + /// + /// + /// Giá trị đầu vào cần tính độ thuộc + /// Điểm bắt đầu hình thang (μ = 0) + /// Điểm bắt đầu vùng phẳng trên (μ = 1) + /// Điểm kết thúc vùng phẳng trên (μ = 1) + /// Điểm kết thúc hình thang (μ = 0) + /// Độ thuộc trong khoảng [0, 1] + private static double Fuzzy_trapmf(double inputValue, double left_base, double left_top, double right_top, double right_base) { - Gain_P = gainP; - return this; + double membership = 0.0; + + // Trường hợp 1: Nằm ngoài phía trái hình thang + if (inputValue <= left_base) + { + membership = 0.0; + } + // Trường hợp 2: Nằm trên cạnh tăng dần (trái) + else if (inputValue > left_base && inputValue < left_top) + { + if (left_top != left_base) // Tránh chia cho 0 + { + membership = (inputValue - left_base) / (left_top - left_base); + } + } + // Trường hợp 3: Nằm trên vùng phẳng (đỉnh) + else if (inputValue >= left_top && inputValue <= right_top) + { + membership = 1.0; + } + // Trường hợp 4: Nằm trên cạnh giảm dần (phải) + else if (inputValue > right_top && inputValue < right_base) + { + if (right_base != right_top) // Tránh chia cho 0 + { + membership = (right_base - inputValue) / (right_base - right_top); + } + } + // Trường hợp 5: Nằm ngoài phía phải hình thang + else if (inputValue >= right_base) + { + membership = 0.0; + } + + return membership; } - public FuzzyLogic WithGainI(double gainI) + /// + /// Hàm thuộc tam giác (Triangular Membership Function) + /// Dạng tam giác với 3 điểm: [left, peak, right] + /// + /// peak + /// /\ + /// / \ + /// / \ + /// _____/ \_____ + /// left right + /// + /// + /// Giá trị đầu vào cần tính độ thuộc + /// Điểm bắt đầu tam giác (μ = 0) + /// Điểm đỉnh tam giác (μ = 1) + /// Điểm kết thúc tam giác (μ = 0) + /// Độ thuộc trong khoảng [0, 1] + private static double Fuzzy_trimf(double inputValue, double left, double peak, double right) { - Gain_I = gainI; - return this; + double membership = 0.0; + + // Trường hợp 1: Nằm ngoài phía trái tam giác + if (inputValue <= left) + { + membership = 0.0; + } + // Trường hợp 2: Nằm trên cạnh tăng dần (trái) + else if (inputValue > left && inputValue < peak) + { + if (peak != left) // Tránh chia cho 0 + { + membership = (inputValue - left) / (peak - left); + } + } + // Trường hợp 3: Đúng tại đỉnh tam giác + else if (inputValue == peak) + { + membership = 1.0; + } + // Trường hợp 4: Nằm trên cạnh giảm dần (phải) + else if (inputValue > peak && inputValue < right) + { + if (right != peak) // Tránh chia cho 0 + { + membership = (right - inputValue) / (right - peak); + } + } + // Trường hợp 5: Nằm ngoài phía phải tam giác + else if (inputValue >= right) + { + membership = 0.0; + } + + return membership; } - private static double Fuzzy_trapmf(double x, double[] parame) + // ============================================================================ + // PHẦN 2: FUZZIFICATION (MỜ HÓA ĐẦU VÀO) + // ============================================================================ + + /// + /// Mờ hóa tín hiệu PI thành 5 tập mờ + /// + /// Tín hiệu đầu ra của bộ PI + /// Mảng lưu giá trị độ thuộc (vị trí 0-4) + private static void FuzzifyPISignal(double piSignal, double[] membershipValues) { - double b_y1; - double y2; - b_y1 = 0.0; - y2 = 0.0; - if (x >= parame[1]) - { - b_y1 = 1.0; - } - if (x < parame[0]) - { - b_y1 = 0.0; - } - if (parame[0] <= x && x < parame[1] && parame[0] != parame[1]) - { - b_y1 = 1.0 / (parame[1] - parame[0]) * (x - parame[0]); - } - if (x <= parame[2]) - { - y2 = 1.0; - } - if (x > parame[3]) - { - y2 = 0.0; - } - if (parame[2] < x && x <= parame[3] && parame[2] != parame[3]) - { - y2 = 1.0 / (parame[3] - parame[2]) * (parame[3] - x); - } - return b_y1 < y2 ? b_y1 : y2; + // 1. NB (Negative Big): [-∞, -∞, -1.0, -0.5] + membershipValues[0] = Fuzzy_trapmf(piSignal, -1.0E+10, -1.0E+10, -1.0, -0.5); + + // 2. Z (Zero): [-0.5, 0.0, 0.5] + membershipValues[1] = Fuzzy_trimf(piSignal, -0.5, 0.0, 0.5); + + // 3. PB (Positive Big): [0.5, 1.0, +∞, +∞] + membershipValues[2] = Fuzzy_trapmf(piSignal, 0.5, 1.0, 1.0E+10, 1.0E+10); + + // 4. NM (Negative Medium): [-1.0, -0.5, 0.0] + membershipValues[3] = Fuzzy_trimf(piSignal, -1.0, -0.5, 0.0); + + // 5. PM (Positive Medium): [0.0, 0.5, 1.0] + membershipValues[4] = Fuzzy_trimf(piSignal, 0.0, 0.5, 1.0); } - private static double Fuzzy_trimf(double x, double[] parame) + /// + /// Mờ hóa vận tốc V thành 9 tập mờ (dải 0.0 - 2.0) + /// + /// Vận tốc tuyến tính mong muốn (0.0 - 2.0 m/s) + /// Mảng lưu giá trị độ thuộc (vị trí 5-13) + private static void FuzzifyVelocity(double velocity, double[] membershipValues) { - double y; - y = 0.0; - if (parame[0] != parame[1] && parame[0] < x && x < parame[1]) - { - y = 1.0 / (parame[1] - parame[0]) * (x - parame[0]); - } - if (parame[1] != parame[2] && parame[1] < x && x < parame[2]) - { - y = 1.0 / (parame[2] - parame[1]) * (parame[2] - x); - } - if (x == parame[1]) - { - y = 1.0; - } - return y; + // Phân chia 9 tập mờ đều cho dải 0.0 - 2.0 + // Mỗi tập mờ cách nhau 0.25, overlap 50% + + // 1. VVS (Very Very Slow): [-∞, -∞, 0.0, 0.25] + membershipValues[5] = Fuzzy_trapmf(velocity, -1.0E+9, -1.0E+9, 0.0, 0.25); + + // 2. VS (Very Slow): [0.0, 0.25, 0.5] + membershipValues[6] = Fuzzy_trimf(velocity, 0.0, 0.25, 0.5); + + // 3. S (Slow): [0.25, 0.5, 0.75] + membershipValues[7] = Fuzzy_trimf(velocity, 0.25, 0.5, 0.75); + + // 4. SM (Slow-Medium): [0.5, 0.75, 1.0] + membershipValues[8] = Fuzzy_trimf(velocity, 0.5, 0.75, 1.0); + + // 5. M (Medium): [0.75, 1.0, 1.25] + membershipValues[9] = Fuzzy_trimf(velocity, 0.75, 1.0, 1.25); + + // 6. MF (Medium-Fast): [1.0, 1.25, 1.5] + membershipValues[10] = Fuzzy_trimf(velocity, 1.0, 1.25, 1.5); + + // 7. F (Fast): [1.25, 1.5, 1.75] + membershipValues[11] = Fuzzy_trimf(velocity, 1.25, 1.5, 1.75); + + // 8. VF (Very Fast): [1.5, 1.75, 2.0] + membershipValues[12] = Fuzzy_trimf(velocity, 1.5, 1.75, 2.0); + + // 9. VVF (Very Very Fast): [1.75, 2.0, +∞, +∞] + membershipValues[13] = Fuzzy_trapmf(velocity, 1.75, 2.0, 1.0E+9, 1.0E+9); } - public (double wl, double wr) Fuzzy_step(double v, double w, double TimeSample) + // ============================================================================ + // PHẦN 3: RULE EVALUATION (ĐÁNH GIÁ LUẬT MỜ) + // ============================================================================ + + /// + /// Đánh giá luật mờ cho một bộ điều khiển + /// + /// Độ thuộc của các đầu vào (14 giá trị: 5 PI + 9 V) + /// Ma trận chỉ số tiền đề (numRules * 2 phần tử) + /// Ma trận chỉ số hệ quả (numRules phần tử) + /// Các giá trị singleton đầu ra + /// Số lượng luật + /// (weightedSum: tổng có trọng số, totalWeight: tổng trọng số) + private static (double weightedSum, double totalWeight) EvaluateRules( + double[] inputMembershipValues, + byte[] ruleAntecedentIndices, + byte[] ruleConsequentIndices, + double[] outputSingletons, + int numRules) { - (double wl, double wr) result = new(); - double[] inputMFCache = new double[10]; - double[] outputMFCache = new double[5]; - double[] outputMFCache_0 = new double[5]; - double[] tmp = new double[3]; - double aggregatedOutputs; - double rtb_TmpSignalConversionAtSFun_0; - double rtb_antecedentOutputs_e; - double sumAntecedentOutputs; - int ruleID; - double[] f = [-1.0E+10, -1.0E+10, -1.0, -0.5]; - double[] e = [0.5, 1.0, 1.0E+10, 1.0E+10]; - double[] d = [0.75, 1.0, 1.0E+9, 1.0E+9]; - double[] c = [-1.0E+9, -1.0E+9, 0.0, 0.25]; - byte[] b = [ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 5, 5, 5, 5, 5, 1, 2, 3, 4, 5, 1, 2, 3, - 4, 5, 1, 2, 3, 4, 5, 3, 4, 5, 1, 2, 1, 2, 3, 4, 5 ]; - byte[] b_0 = [1, 1, 2, 1, 1, 2, 3, 5, 1, 4, 5, 5, 5, 5, 5, 2, 1, 1, 1, 1, 5, 5, 5, 5, 5]; - byte[] b_1 = [ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 5, 5, 5, 5, 5, 1, 2, 3, 4, 5, 4, 1, - 2, 3, 5, 3, 1, 2, 4, 5, 1, 2, 3, 4, 5, 1, 2, 4, 5, 3 ]; - byte[] b_2 = [5, 5, 5, 5, 5, 1, 2, 3, 5, 4, 2, 1, 1, 1, 1, 5, 5, 5, 5, 5, 1, 1, 1, 1, 2]; - double inputMFCache_tmp; - double inputMFCache_tmp_0; - double inputMFCache_tmp_1; - double inputMFCache_tmp_2; - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller' */ - /* SignalConversion generated from: '/ SFunction ' incorporates: - * Constant: '/w' - * DiscreteIntegrator: '/Discrete-Time Integrator' - * Gain: '/Gain1' - * MATLAB Function: '/Evaluate Rule Antecedents' - * MATLAB Function: '/Evaluate Rule Antecedents' - * SignalConversion generated from: '/ SFunction ' - * Sum: '/Sum' - */ - DiscreteTimeIntegrator_DSTATE += Gain_I * w * TimeSample; - rtb_TmpSignalConversionAtSFun_0 = Gain_P * w + DiscreteTimeIntegrator_DSTATE; - /* End of Outputs for SubSystem: '/Fuzzy Logic Controller1' */ - /* MATLAB Function: '/Evaluate Rule Antecedents' incorporates: - * Constant: '/v' - * MATLAB Function: '/Evaluate Rule Antecedents' - * SignalConversion generated from: '/ SFunction ' - */ - sumAntecedentOutputs = 0.0; - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache_tmp = Fuzzy_trapmf(rtb_TmpSignalConversionAtSFun_0, f); - /* End of Outputs for SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache[0] = inputMFCache_tmp; - tmp[0] = -0.5; - tmp[1] = 0.0; - tmp[2] = 0.5; - inputMFCache[1] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache_tmp_0 = Fuzzy_trapmf(rtb_TmpSignalConversionAtSFun_0, e); - /* End of Outputs for SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache[2] = inputMFCache_tmp_0; - tmp[0] = -1.0; - tmp[1] = -0.5; - tmp[2] = 0.0; - inputMFCache[3] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - tmp[0] = 0.0; - tmp[1] = 0.5; - tmp[2] = 1.0; - inputMFCache[4] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - tmp[0] = 0.0; - tmp[1] = 0.25; - tmp[2] = 0.5; - inputMFCache[5] = Fuzzy_trimf(v, tmp); - tmp[0] = 0.25; - tmp[1] = 0.5; - tmp[2] = 0.75; - inputMFCache[6] = Fuzzy_trimf(v, tmp); - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache_tmp_1 = Fuzzy_trapmf(v, d); - /* End of Outputs for SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache[7] = inputMFCache_tmp_1; - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache_tmp_2 = Fuzzy_trapmf(v, c); - /* End of Outputs for SubSystem: '/Fuzzy Logic Controller1' */ - inputMFCache[8] = inputMFCache_tmp_2; - tmp[0] = 0.5; - tmp[1] = 0.75; - tmp[2] = 1.0; - inputMFCache[9] = Fuzzy_trimf(v, tmp); - /* MATLAB Function: '/Evaluate Rule Consequents' */ - aggregatedOutputs = 0.0; - outputMFCache[0] = 0.0; - outputMFCache[1] = 0.25; - outputMFCache[2] = 0.5; - outputMFCache[3] = 0.75; - outputMFCache[4] = 1.0; - for (ruleID = 0; ruleID < 25; ruleID++) + const int VELOCITY_OFFSET = 5; // Chỉ số bắt đầu của V trong inputMembershipValues + + double weightedSum = 0.0; + double totalWeight = 0.0; + + for (int ruleIndex = 0; ruleIndex < numRules; ruleIndex++) { - /* MATLAB Function: '/Evaluate Rule Antecedents' */ - rtb_antecedentOutputs_e = inputMFCache[b[ruleID + 25] + 4] * inputMFCache[b[ruleID] - 1]; - sumAntecedentOutputs += rtb_antecedentOutputs_e; - /* MATLAB Function: '/Evaluate Rule Consequents' */ - aggregatedOutputs += outputMFCache[b_0[ruleID] - 1] * rtb_antecedentOutputs_e; + // Lấy chỉ số tập mờ cho PI (từ 1-5, cần trừ 1 để thành 0-4) + int piMembershipIndex = ruleAntecedentIndices[ruleIndex] - 1; + + // Lấy chỉ số tập mờ cho Velocity (từ 1-9, cộng offset) + int velocityMembershipIndex = ruleAntecedentIndices[ruleIndex + numRules] + VELOCITY_OFFSET - 1; + + // Tính độ kích hoạt của luật (AND operator = phép nhân) + double ruleActivation = inputMembershipValues[piMembershipIndex] + * inputMembershipValues[velocityMembershipIndex]; + + // Lấy giá trị singleton đầu ra tương ứng (từ 1-9, cần trừ 1) + int outputIndex = ruleConsequentIndices[ruleIndex] - 1; + double outputValue = outputSingletons[outputIndex]; + + // Tích lũy tổng trọng số và tổng có trọng số + totalWeight += ruleActivation; + weightedSum += outputValue * ruleActivation; } - /* MATLAB Function: '/Defuzzify Outputs' incorporates: - * MATLAB Function: '/Evaluate Rule Antecedents' - * MATLAB Function: '/Evaluate Rule Consequents' - */ - if (sumAntecedentOutputs == 0.0) + + return (weightedSum, totalWeight); + } + + // ============================================================================ + // PHẦN 4: DEFUZZIFICATION (GIẢI MỜ ĐẦU RA) + // ============================================================================ + + /// + /// Giải mờ bằng phương pháp trọng tâm (Weighted Average / Center of Gravity) + /// Công thức: output = Σ(singleton_i × weight_i) / Σ(weight_i) + /// + /// Tổng đầu ra có trọng số + /// Tổng trọng số của tất cả các luật + /// Giá trị mặc định nếu totalWeight = 0 + /// Giá trị đầu ra rõ (crisp output) + private static double Defuzzify(double weightedSum, double totalWeight, double defaultValue = 0.5) + { + // Nếu không có luật nào được kích hoạt, trả về giá trị mặc định + if (totalWeight == 0.0) { - result.wr = 0.5; + return defaultValue; } else { - result.wr = 1.0 / sumAntecedentOutputs * aggregatedOutputs; + // Tính trọng tâm: output = weightedSum / totalWeight + return weightedSum / totalWeight; } - /* Outputs for Atomic SubSystem: '/Fuzzy Logic Controller1' */ - /* MATLAB Function: '/Evaluate Rule Antecedents' incorporates: - * Constant: '/v' - * SignalConversion generated from: '/ SFunction ' - */ - sumAntecedentOutputs = 0.0; - inputMFCache[0] = inputMFCache_tmp; - tmp[0] = -0.5; - tmp[1] = 0.0; - tmp[2] = 0.5; - inputMFCache[1] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - inputMFCache[2] = inputMFCache_tmp_0; - tmp[0] = -1.0; - tmp[1] = -0.5; - tmp[2] = 0.0; - inputMFCache[3] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - tmp[0] = 0.0; - tmp[1] = 0.5; - tmp[2] = 1.0; - inputMFCache[4] = Fuzzy_trimf(rtb_TmpSignalConversionAtSFun_0, tmp); - tmp[0] = 0.0; - tmp[1] = 0.25; - tmp[2] = 0.5; - inputMFCache[5] = Fuzzy_trimf(v, tmp); - tmp[0] = 0.25; - tmp[1] = 0.5; - tmp[2] = 0.75; - inputMFCache[6] = Fuzzy_trimf(v, tmp); - inputMFCache[7] = inputMFCache_tmp_1; - inputMFCache[8] = inputMFCache_tmp_2; - tmp[0] = 0.5; - tmp[1] = 0.75; - tmp[2] = 1.0; - inputMFCache[9] = Fuzzy_trimf(v, tmp); - /* MATLAB Function: '/Evaluate Rule Consequents' */ - aggregatedOutputs = 0.0; - outputMFCache_0[0] = 0.0; - outputMFCache_0[1] = 0.25; - outputMFCache_0[2] = 0.5; - outputMFCache_0[3] = 0.75; - outputMFCache_0[4] = 1.0; - for (ruleID = 0; ruleID < 25; ruleID++) - { - /* MATLAB Function: '/Evaluate Rule Antecedents' */ - rtb_antecedentOutputs_e = inputMFCache[b_1[ruleID + 25] + 4] * inputMFCache[b_1[ruleID] - 1]; - sumAntecedentOutputs += rtb_antecedentOutputs_e; - /* MATLAB Function: '/Evaluate Rule Consequents' */ - aggregatedOutputs += outputMFCache_0[b_2[ruleID] - 1] * - rtb_antecedentOutputs_e; - } - /* MATLAB Function: '/Defuzzify Outputs' incorporates: - * MATLAB Function: '/Evaluate Rule Antecedents' - * MATLAB Function: '/Evaluate Rule Consequents' - */ - if (sumAntecedentOutputs == 0.0) - { - result.wl = 0.5; - } - else - { - result.wl = 1.0 / sumAntecedentOutputs * aggregatedOutputs; - } - return result; + } + + // ============================================================================ + // PHẦN 5: BẢNG LUẬT VÀ CẤU HÌNH + // ============================================================================ + + /// + /// Bảng chỉ số tiền đề luật cho bánh phải (Rule Antecedent Indices - Right Wheel) + /// 90 phần tử: 45 cho PI + 45 cho Velocity + /// Hệ thống: 5 tập mờ PI × 9 tập mờ V = 45 luật + /// Giá trị từ 1-5 cho PI, 1-9 cho V + /// + private readonly byte[] RULE_ANTECEDENT_INDICES_RIGHT = + [ + // 45 phần tử đầu: Chỉ số tập mờ của PI Signal (1-5) + // Mỗi tập PI lặp lại 9 lần (cho 9 mức vận tốc) + 1, 1, 1, 1, 1, 1, 1, 1, 1, // Luật 1-9: PI = NB (tập 1) + 2, 2, 2, 2, 2, 2, 2, 2, 2, // Luật 10-18: PI = Z (tập 2) + 3, 3, 3, 3, 3, 3, 3, 3, 3, // Luật 19-27: PI = PB (tập 3) + 4, 4, 4, 4, 4, 4, 4, 4, 4, // Luật 28-36: PI = NM (tập 4) + 5, 5, 5, 5, 5, 5, 5, 5, 5, // Luật 37-45: PI = PM (tập 5) + + // 45 phần tử sau: Chỉ số tập mờ của Velocity (1-9) + // Lặp lại theo pattern: VVS, VS, S, SM, M, MF, F, VF, VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 1-9: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 10-18: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 19-27: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 28-36: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9 // Luật 37-45: V = VVS đến VVF + ]; + + /// + /// Bảng chỉ số hệ quả cho bánh phải (Rule Consequent Indices - Right Wheel) + /// 45 phần tử tương ứng với 45 luật + /// Giá trị từ 1-9 tương ứng với 9 mức tốc độ đầu ra (0.0 - 2.0) + /// + /// Logic: + /// - Khi PI âm (NB, NM): Bánh phải chậm hơn (robot rẽ trái) + /// - Khi PI = 0 (Z): Bánh phải tỷ lệ với vận tốc V + /// - Khi PI dương (PB, PM): Bánh phải nhanh hơn (robot rẽ phải) + /// + private readonly byte[] RULE_CONSEQUENT_INDICES_RIGHT = + [ + // PI = NB (Negative Big) - Bánh phải chậm + 1, 1, 2, 2, 3, 3, 4, 5, 6, // Luật 1-9: V thấp->cao → Output 1-6 + + // PI = Z (Zero) - Bánh phải theo vận tốc thẳng + 2, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 10-18: V thấp->cao → Output 2-9 + + // PI = PB (Positive Big) - Bánh phải nhanh + 4, 5, 6, 7, 8, 9, 9, 9, 9, // Luật 19-27: V thấp->cao → Output 4-9 + + // PI = NM (Negative Medium) - Bánh phải hơi chậm + 1, 2, 2, 3, 4, 4, 5, 6, 7, // Luật 28-36: V thấp->cao → Output 1-7 + + // PI = PM (Positive Medium) - Bánh phải hơi nhanh + 3, 4, 5, 6, 7, 8, 8, 9, 9 // Luật 37-45: V thấp->cao → Output 3-9 + ]; + + /// + /// Bảng chỉ số tiền đề luật cho bánh trái (Rule Antecedent Indices - Left Wheel) + /// 90 phần tử: 45 cho PI + 45 cho Velocity + /// + private readonly byte[] RULE_ANTECEDENT_INDICES_LEFT = + [ + // 45 phần tử đầu: Chỉ số tập mờ của PI Signal (1-5) + 1, 1, 1, 1, 1, 1, 1, 1, 1, // Luật 1-9: PI = NB (tập 1) + 2, 2, 2, 2, 2, 2, 2, 2, 2, // Luật 10-18: PI = Z (tập 2) + 3, 3, 3, 3, 3, 3, 3, 3, 3, // Luật 19-27: PI = PB (tập 3) + 4, 4, 4, 4, 4, 4, 4, 4, 4, // Luật 28-36: PI = NM (tập 4) + 5, 5, 5, 5, 5, 5, 5, 5, 5, // Luật 37-45: PI = PM (tập 5) + + // 45 phần tử sau: Chỉ số tập mờ của Velocity (1-9) + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 1-9: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 10-18: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 19-27: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 28-36: V = VVS đến VVF + 1, 2, 3, 4, 5, 6, 7, 8, 9 // Luật 37-45: V = VVS đến VVF + ]; + + /// + /// Bảng chỉ số hệ quả cho bánh trái (Rule Consequent Indices - Left Wheel) + /// 45 phần tử tương ứng với 45 luật + /// + /// Logic: NGƯỢC LẠI với bánh phải + /// - Khi PI âm (NB, NM): Bánh trái nhanh hơn (robot rẽ trái) + /// - Khi PI = 0 (Z): Bánh trái tỷ lệ với vận tốc V + /// - Khi PI dương (PB, PM): Bánh trái chậm hơn (robot rẽ phải) + /// + private readonly byte[] RULE_CONSEQUENT_INDICES_LEFT = + [ + // PI = NB (Negative Big) - Bánh trái nhanh + 4, 5, 6, 7, 8, 9, 9, 9, 9, // Luật 1-9: V thấp->cao → Output 4-9 + + // PI = Z (Zero) - Bánh trái theo vận tốc thẳng + 2, 2, 3, 4, 5, 6, 7, 8, 9, // Luật 10-18: V thấp->cao → Output 2-9 + + // PI = PB (Positive Big) - Bánh trái chậm + 1, 1, 2, 2, 3, 3, 4, 5, 6, // Luật 19-27: V thấp->cao → Output 1-6 + + // PI = NM (Negative Medium) - Bánh trái hơi nhanh + 3, 4, 5, 6, 7, 8, 8, 9, 9, // Luật 28-36: V thấp->cao → Output 3-9 + + // PI = PM (Positive Medium) - Bánh trái hơi chậm + 1, 2, 2, 3, 4, 4, 5, 6, 7 // Luật 37-45: V thấp->cao → Output 1-7 + ]; + + /// + /// Các mức đầu ra singleton (Output Singletons) + /// 9 mức tốc độ từ 0.0 đến 2.0 (tăng gấp đôi so với phiên bản cũ) + /// + private readonly double[] OUTPUT_SINGLETON_LEVELS = + [ + 0.0, // Mức 1: Dừng hoàn toàn (0%) + 0.25, // Mức 2: Rất chậm (12.5%) + 0.5, // Mức 3: Chậm (25%) + 0.75, // Mức 4: Hơi chậm (37.5%) + 1.0, // Mức 5: Trung bình (50%) - Điểm chuẩn cũ + 1.25, // Mức 6: Hơi nhanh (62.5%) + 1.5, // Mức 7: Nhanh (75%) + 1.75, // Mức 8: Rất nhanh (87.5%) + 2.0 // Mức 9: Tối đa (100%) + ]; + + // ============================================================================ + // PHẦN 6: HÀM CHÍNH - BỘ ĐIỀU KHIỂN PI + FUZZY + // ============================================================================ + + // Biến trạng thái bộ tích phân + private double integratorState = 0.0; + + // Hệ số bộ điều khiển PI + private double proportionalGain = 1.0; // Hệ số tỷ lệ (Kp) + private double integralGain = 0.5; // Hệ số tích phân (Ki) + + /// + /// Hàm điều khiển chính - Tính toán tốc độ bánh trái và phải + /// Sử dụng bộ điều khiển PI kết hợp với logic mờ + /// + /// CẤU HÌNH MỚI: + /// - PI Signal: 5 tập mờ (NB, Z, PB, NM, PM) + /// - Velocity: 9 tập mờ (VVS, VS, S, SM, M, MF, F, VF, VVF) + /// - Tổng số luật: 5 × 9 = 45 luật cho mỗi bánh + /// - Dải đầu ra: 0.0 - 2.0 (gấp đôi phiên bản cũ) + /// + /// Vận tốc tuyến tính mong muốn (0.0 - 2.0 m/s) + /// Vận tốc góc mong muốn (rad/s) + /// Chu kỳ lấy mẫu (giây) + /// (leftWheelSpeed, rightWheelSpeed): Tốc độ bánh trái và phải trong [0.0, 2.0] + public (double leftWheelSpeed, double rightWheelSpeed) Fuzzy_step( + double desiredVelocity, + double desiredAngularVelocity, + double samplingTime) + { + const int NUM_INPUT_MEMBERSHIPS = 14; // 5 cho PI + 9 cho Velocity + const int NUM_OUTPUT_LEVELS = 9; // 9 mức đầu ra (0.0 - 2.0) + const int NUM_RULES = 45; // 5 × 9 = 45 luật + + // Khởi tạo mảng lưu độ thuộc đầu vào + double[] inputMembershipValues = new double[NUM_INPUT_MEMBERSHIPS]; + + // Khởi tạo mảng lưu các mức đầu ra + double[] outputLevels = new double[NUM_OUTPUT_LEVELS]; + Array.Copy(OUTPUT_SINGLETON_LEVELS, outputLevels, NUM_OUTPUT_LEVELS); + + // ========== BƯỚC 1: BỘ ĐIỀU KHIỂN PI ========== + // Cập nhật trạng thái tích phân: I(t) = I(t-1) + Ki * error * dt + integratorState += integralGain * desiredAngularVelocity * samplingTime; + + // Tính tín hiệu điều khiển PI: u(t) = Kp * error + I(t) + double piControlSignal = proportionalGain * desiredAngularVelocity + integratorState; + + // ========== BƯỚC 2: MỜ HÓA ĐẦU VÀO ========== + FuzzifyPISignal(piControlSignal, inputMembershipValues); + FuzzifyVelocity(desiredVelocity, inputMembershipValues); + + // ========== BƯỚC 3: TÍNH TOÁN BÁNH PHẢI ========== + var (weightedSum_Right, totalWeight_Right) = EvaluateRules( + inputMembershipValues, + RULE_ANTECEDENT_INDICES_RIGHT, + RULE_CONSEQUENT_INDICES_RIGHT, + outputLevels, + NUM_RULES + ); + + double rightWheelSpeed = Defuzzify(weightedSum_Right, totalWeight_Right, defaultValue: 1.0); + + // ========== BƯỚC 4: TÍNH TOÁN BÁNH TRÁI ========== + var (weightedSum_Left, totalWeight_Left) = EvaluateRules( + inputMembershipValues, + RULE_ANTECEDENT_INDICES_LEFT, + RULE_CONSEQUENT_INDICES_LEFT, + outputLevels, + NUM_RULES + ); + + double leftWheelSpeed = Defuzzify(weightedSum_Left, totalWeight_Left, defaultValue: 1.0); + + // ========== BƯỚC 5: TRẢ VỀ KẾT QUẢ ========== + return (leftWheelSpeed, rightWheelSpeed); + } + + // ============================================================================ + // PHẦN 7: HÀM HỖ TRỢ + // ============================================================================ + + /// + /// Reset trạng thái bộ tích phân về 0 + /// Nên gọi khi bắt đầu chu kỳ điều khiển mới hoặc khi cần reset hệ thống + /// + public void ResetIntegrator() + { + integratorState = 0.0; + } + + /// + /// Thiết lập hệ số cho bộ điều khiển PI + /// + /// Hệ số tỷ lệ (Proportional Gain) - Phản ứng với sai số hiện tại + /// Hệ số tích phân (Integral Gain) - Loại bỏ sai số tích lũy + public FuzzyLogic WithPIGains(double kp, double ki) + { + proportionalGain = kp; + integralGain = ki; + return this; + } + + /// + /// Lấy trạng thái hiện tại của bộ tích phân + /// Hữu ích cho việc debug và giám sát hệ thống + /// + /// Giá trị tích phân hiện tại + public double GetIntegratorState() + { + return integratorState; + } + + /// + /// Lấy hệ số PI hiện tại + /// + /// (Kp, Ki): Hệ số tỷ lệ và tích phân + public (double Kp, double Ki) GetPIGains() + { + return (proportionalGain, integralGain); } }