2026年1月15日 星期四

RFID Slotted ALOHA

RFID Slotted ALOHA 


Slotted ALOHA 的核心在於引入「時間槽」(Time Slots)的概念。在 Slotted ALOHA 中,時間被劃分為等長的區段,標籤只能在每個時間槽的起始點發送資料。

這種機制減少了碰撞發生的時間窗(從 2T 降至 1T),理論上能將最大吞吐量從 18.4% 提升至 36.8%。

  • 時間槽管理 (Slot Manager)

    • 不同於 Pure ALOHA 每個標籤亂跳,Slotted 版使用一個 slot_clock_manager。這像是一個指揮家,確保所有標籤在同一個時間點(Slot 開始)決定是否發送。

  • 手動模式邏輯變更

    • 點擊「下一時間槽」時,程式會計算該槽位內的發送狀況。

    • 你會發現,標籤現在是「整齊劃一」地變色(發送或碰撞),這正是 Slotted ALOHA 的特性。

  • 狀態回饋

    • 增加了 Slot Marker 顯示目前槽位的狀態(空閒、成功或碰撞)。

    • 標籤顏色:白色(閒置)、黃色(傳輸中)、綠色(成功)、紅色(碰撞)。





  • import tkinter as tk

    import random

    import time

    from threading import Thread, Event


    class SlottedALOHASimulator:

        def __init__(self, root):

            self.root = root

            self.root.title("RFID Slotted ALOHA 模擬器")

            

            # 參數設置

            self.num_tags = 5

            self.is_running = False

            self.manual_mode = tk.BooleanVar(value=False)

            self.slot_event = Event()  # 控制時間槽同步

            

            self.success_count = 0

            self.collision_count = 0

            self.current_slot = 0

            self.active_transmissions = []

            

            self.setup_ui()

            

        def setup_ui(self):

            # 畫布

            self.canvas = tk.Canvas(self.root, width=600, height=280, bg="#f0f0f0")

            self.canvas.pack(pady=10)

            

            # 繪製 Reader

            self.reader = self.canvas.create_rectangle(250, 20, 350, 70, fill="#bbdefb", outline="#1976d2")

            self.canvas.create_text(300, 45, text="RFID Reader", font=("Arial", 10, "bold"))

            

            # 繪製時間槽指示器 (Slot Indicator)

            self.slot_marker = self.canvas.create_text(300, 100, text="等待開始...", font=("Courier", 12, "bold"), fill="#555")

            

            self.tags = []

            for i in range(self.num_tags):

                x = 100 + (i * 100)

                tag = self.canvas.create_oval(x-20, 180, x+20, 220, fill="white", outline="grey")

                self.canvas.create_text(x, 235, text=f"Tag {i+1}")

                self.tags.append(tag)


            # 控制區

            ctrl_frame = tk.Frame(self.root)

            ctrl_frame.pack(pady=10)


            self.btn_start = tk.Button(ctrl_frame, text="開始模擬", command=self.start_simulation, width=10, bg="#c8e6c9")

            self.btn_start.pack(side=tk.LEFT, padx=5)

            

            self.btn_stop = tk.Button(ctrl_frame, text="停止", command=self.stop_simulation, state=tk.DISABLED, width=10)

            self.btn_stop.pack(side=tk.LEFT, padx=5)


            tk.Checkbutton(ctrl_frame, text="手動模式 (逐槽控制)", variable=self.manual_mode).pack(side=tk.LEFT, padx=10)


            self.btn_next = tk.Button(ctrl_frame, text="下一時間槽 >>", command=self.next_slot, state=tk.DISABLED, bg="#e1f5fe")

            self.btn_next.pack(side=tk.LEFT, padx=5)


            # 數據面板

            self.stat_label = tk.Label(self.root, text="Slot: 0 | 成功: 0 | 碰撞: 0", font=("Consolas", 11))

            self.stat_label.pack(pady=5)


        def slot_clock_manager(self):

            """時間槽管理器:負責同步所有標籤的發送時機"""

            while self.is_running:

                if self.manual_mode.get():

                    self.slot_event.wait() # 等待手動點擊

                else:

                    time.sleep(1.5) # 自動模式下的槽位間隔

                

                if not self.is_running: break

                

                self.current_slot += 1

                self.update_slot_ui(f"Slot {self.current_slot} 開始發送!")

                

                # 觸發標籤嘗試發送 (邏輯與 Pure 不同:所有標籤在同一瞬間判斷)

                self.active_transmissions = []

                results = []

                

                # 模擬標籤隨機決定本槽位是否發送 (假設發送機率 p=0.3)

                for i in range(self.num_tags):

                    if random.random() < 0.3:

                        self.active_transmissions.append(i)

                        self.canvas.itemconfig(self.tags[i], fill="#fff176") # 黃色:準備


                time.sleep(0.5) # 模擬傳輸中


                # 碰撞檢測

                if len(self.active_transmissions) == 0:

                    self.update_slot_ui(f"Slot {self.current_slot}: 空閒 (Idle)")

                elif len(self.active_transmissions) == 1:

                    idx = self.active_transmissions[0]

                    self.success_count += 1

                    self.canvas.itemconfig(self.tags[idx], fill="#81c784") # 綠色:成功

                    self.update_slot_ui(f"Slot {self.current_slot}: 成功讀取 Tag {idx+1}")

                else:

                    self.collision_count += 1

                    for idx in self.active_transmissions:

                        self.canvas.itemconfig(self.tags[idx], fill="#e57373") # 紅色:碰撞

                    self.update_slot_ui(f"Slot {self.current_slot}: 發生碰撞 ({len(self.active_transmissions)} 個標籤)")


                self.update_stats()

                time.sleep(0.8) # 讓使用者看清楚結果

                

                # 重設標籤顏色準備進入下一個槽位

                for i in range(self.num_tags):

                    self.canvas.itemconfig(self.tags[i], fill="white")

                

                if self.manual_mode.get():

                    self.slot_event.clear()


        def next_slot(self):

            self.slot_event.set()


        def update_slot_ui(self, msg):

            self.root.after(0, lambda: self.canvas.itemconfig(self.slot_marker, text=msg))


        def update_stats(self):

            self.root.after(0, lambda: self.stat_label.config(

                text=f"Slot: {self.current_slot} | 成功: {self.success_count} | 碰撞: {self.collision_count}"))


        def start_simulation(self):

            self.is_running = True

            self.success_count = 0

            self.collision_count = 0

            self.current_slot = 0

            self.update_stats()

            

            self.btn_start.config(state=tk.DISABLED)

            self.btn_stop.config(state=tk.NORMAL)

            self.btn_next.config(state=tk.NORMAL)

            

            # 啟動時脈執行緒

            Thread(target=self.slot_clock_manager, daemon=True).start()


        def stop_simulation(self):

            self.is_running = False

            self.slot_event.set()

            self.btn_start.config(state=tk.NORMAL)

            self.btn_stop.config(state=tk.DISABLED)

            self.btn_next.config(state=tk.DISABLED)

            self.update_slot_ui("模擬已停止")


    if __name__ == "__main__":

        root = tk.Tk()

        app = SlottedALOHASimulator(root)

        root.mainloop()





    Slotted ALOHA 中,最關鍵的變數就是每個標籤在一個槽位中發送的機率 p

    • 如果 p 太高,會不斷發生碰撞 (Collision)

    • 如果 p 太低,通道會一直處於空閒 (Idle),浪費頻寬。

  • 測試低負載:將 p 調到 0.05。你會發現大多數時間都是「空閒」,成功率很低。

  • 測試高負載:將 p 調到 0.6 以上。你會發現畫面上經常是一片紅色,因為標籤都在搶著發送,導致碰撞嚴重。

  • 尋找最佳值:理論上,當 $G = 1$(即平均每槽位有 1 個標籤發送)時效率最高。在 8 個標籤的情況下,將 $p$ 設定在 0.125 ($1/8$) 左右,您會觀察到最高的成功率。


  • import tkinter as tk
    from tkinter import ttk
    import random
    import time
    from threading import Thread, Event

    class AdvancedSlottedALOHA:
        def __init__(self, root):
            self.root = root
            self.root.title("RFID Slotted ALOHA 專業模擬器")
            
            # 變數
            self.num_tags = 8 # 增加標籤數量讓碰撞更明顯
            self.is_running = False
            self.manual_mode = tk.BooleanVar(value=False)
            self.p_value = tk.DoubleVar(value=0.2) # 發送機率
            self.slot_event = Event()
            
            self.success_count = 0
            self.collision_count = 0
            self.idle_count = 0
            self.total_slots = 0
            
            self.setup_ui()
            
        def setup_ui(self):
            # 頂部統計面板
            stat_frame = tk.LabelFrame(self.root, text="即時統計 (Efficiency)")
            stat_frame.pack(fill="x", padx=10, pady=5)
            
            self.stat_label = tk.Label(stat_frame, text="成功率 (S): 0% | 碰撞率: 0% | 空閒率: 0%", font=("Consolas", 10))
            self.stat_label.pack(pady=5)

            # 畫布
            self.canvas = tk.Canvas(self.root, width=800, height=200, bg="#2c3e50")
            self.canvas.pack(pady=10)
            
            self.tags = []
            for i in range(self.num_tags):
                x = 50 + (i * 100)
                tag = self.canvas.create_rectangle(x-30, 100, x+30, 140, fill="#ecf0f1", outline="#bdc3c7", width=2)
                self.canvas.create_text(x, 155, text=f"Tag {i+1}", fill="white")
                self.tags.append(tag)

            # 控制台
            ctrl_frame = tk.Frame(self.root)
            ctrl_frame.pack(pady=10, fill="x", padx=20)

            # 左側:機率調整
            prob_frame = tk.Frame(ctrl_frame)
            prob_frame.pack(side=tk.LEFT)
            tk.Label(prob_frame, text="發送機率 (p):").pack(side=tk.LEFT)
            self.p_scale = tk.Scale(prob_frame, from_=0.05, to=0.8, resolution=0.05, 
                                    orient=tk.HORIZONTAL, variable=self.p_value, length=150)
            self.p_scale.pack(side=tk.LEFT)

            # 右側:按鈕
            btn_frame = tk.Frame(ctrl_frame)
            btn_frame.pack(side=tk.RIGHT)
            
            tk.Checkbutton(btn_frame, text="手動模式", variable=self.manual_mode).pack(side=tk.LEFT, padx=10)
            self.btn_next = tk.Button(btn_frame, text="下一槽位 >>", command=self.next_slot, state=tk.DISABLED)
            self.btn_next.pack(side=tk.LEFT, padx=5)
            
            self.btn_start = tk.Button(btn_frame, text="開始", command=self.start_simulation, width=8, bg="#2ecc71")
            self.btn_start.pack(side=tk.LEFT, padx=5)
            
            self.btn_stop = tk.Button(btn_frame, text="停止", command=self.stop_simulation, state=tk.DISABLED, width=8)
            self.btn_stop.pack(side=tk.LEFT, padx=5)

        def slot_logic(self):
            while self.is_running:
                if self.manual_mode.get():
                    self.slot_event.wait()
                else:
                    time.sleep(1.0)
                
                if not self.is_running: break
                
                self.total_slots += 1
                active_list = []
                p = self.p_value.get()

                # 決定哪些標籤要發送
                for i in range(self.num_tags):
                    if random.random() < p:
                        active_list.append(i)
                        self.canvas.itemconfig(self.tags[i], fill="#f1c40f") # 嘗試中

                time.sleep(0.4)

                # 判定結果
                if len(active_list) == 0:
                    self.idle_count += 1
                elif len(active_list) == 1:
                    self.success_count += 1
                    self.canvas.itemconfig(self.tags[active_list[0]], fill="#2ecc71") # 成功
                else:
                    self.collision_count += 1
                    for idx in active_list:
                        self.canvas.itemconfig(self.tags[idx], fill="#e74c3c") # 碰撞

                self.update_stats()
                time.sleep(0.5)
                
                for i in range(self.num_tags):
                    self.canvas.itemconfig(self.tags[i], fill="#ecf0f1")
                
                if self.manual_mode.get():
                    self.slot_event.clear()

        def update_stats(self):
            if self.total_slots > 0:
                s_rate = (self.success_count / self.total_slots) * 100
                c_rate = (self.collision_count / self.total_slots) * 100
                i_rate = (self.idle_count / self.total_slots) * 100
                self.root.after(0, lambda: self.stat_label.config(
                    text=f"槽位總數: {self.total_slots} | 成功 (S): {s_rate.float():.1f}% | 碰撞: {c_rate.float():.1f}% | 空閒: {i_rate.float():.1f}%"
                ))

        def next_slot(self):
            self.slot_event.set()

        def start_simulation(self):
            self.is_running = True
            self.total_slots = self.success_count = self.collision_count = self.idle_count = 0
            self.btn_start.config(state=tk.DISABLED)
            self.btn_stop.config(state=tk.NORMAL)
            self.btn_next.config(state=tk.NORMAL)
            Thread(target=self.slot_logic, daemon=True).start()

        def stop_simulation(self):
            self.is_running = False
            self.slot_event.set()
            self.btn_start.config(state=tk.NORMAL)
            self.btn_stop.config(state=tk.DISABLED)
            self.btn_next.config(state=tk.DISABLED)

    if __name__ == "__main__":
        root = tk.Tk()
        app = AdvancedSlottedALOHA(root)
        root.mainloop()

    沒有留言:

    張貼留言

    RFID Super Tag 協議模擬器

    RFID Super Tag 協議模擬器 Super Tag 協議 (通常指一種改進型的時分多址或樹狀抗碰撞協議)的核心概念是「標籤主導」與「快速識別」。在這種模式下,標籤不僅僅是等待查詢,還會根據內部的計數器或隨機分配的 Slot 進行主動回應,而 Reader 則根據回應情況...