物联网与射频识别技术,课程实验(一)

本文介绍了使用Python模拟C1G2标签在EPCC1G2标准下的状态转换模型,包括就绪态、仲裁态、回复态、确认态等。通过Reader和Tag类实现阅读器和标签的行为,并进行通信交互,如选择、盘存、访问等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

(1)communication.py

(2)Reader.py

(3)Tag.py


实验1—— EPC C1G2标准下的标签状态转换仿真 

实验说明: 利用Python或Matlab模拟C1G2标签的状态转换模型; 程序应能显示标签当前的状态,并能通过键入的不同指令完成状态的转换。

文件结构如下:

(1)communication.py

import sys
from Tag import Tag
from Reader import Reader


def validACK(tagIndex):
    print('有效的的ACK RN16,tag返回EPC,标签变为Acknowledged状态')
    tagArray[tagIndex].setState('Acknowledged')
    return tagArray[tagIndex].returnEPC()


def invalidACK():
    print('无效的ACK RN16,no reply')


def returnHandle(tagIndex):
    tagArray[tagIndex].setState('Open')
    handle = tagArray[tagIndex].returnHandle()
    print('tag返回handle:{},进入{}状态,交出钥匙,接受指令'.format(handle, tagArray[tagIndex].state))
    return handle


def access(command):
    if command == 'read':
        cmd = reader.read(tagArray[tagIndex])
    elif command == 'write':
        cmd = reader.write(tagArray[tagIndex])
    elif command == 'lock':
        cmd = reader.lock(tagArray[tagIndex])
    elif command == 'kill':
        cmd = reader.kill(tagArray[tagIndex])
    return cmd


# tagNum是标签的数目,tagArray用以存储所有标签
tagNum = int(input('please10 input the number of Tag: '))
Q = int(input('please input the Q to decide the maxNum of slot: '))
tagArray = []
tagIndex = 0

for i in range(tagNum):
    tag = Tag()
    tag.endPoint = (2 ** (Q - 1))
    tagArray.append(tag)

reader = Reader()
print('*****************开始通信*****************')
print('\n*****************进入选择阶段*****************')
selectChoice = int(input('输入0选择全部标签,输入1选择奇数号标签,输入2选择偶数号标签: '))
reader.select(tagArray, selectChoice)
print('\n*****************进入盘存阶段*****************')
ans, tagIndex = reader.Query(tagArray)
while ans is None:  # ans非空时代表slot值为0 tag返回一个RN16 此时ans=RN16
    ans, tagIndex = reader.QueryRep(tagArray)
else:
    ACKRN16 = reader.ACK(ans)
    print('ACK返回RN16,判断是否有效进入下一步')
    if ACKRN16 == ans:
        EPC = validACK(tagIndex)
        print('\n*****************进入访问阶段*****************')
        NewRN16 = reader.Req_RN(EPC, ACKRN16)
        if NewRN16 == ACKRN16:
            print('收到有效的RN16,tag将返回handle(新的16位随机数)')
            handle = returnHandle(tagIndex)
            command, returnhandle = reader.Command(handle)
            if returnhandle == handle:
                cmd = access(command)
                while cmd:
                    command, returnhandle = reader.Command(handle)
                    cmd = access(command)
                else:
                    sys.exit(0)
        else:
            print('收到无效的RN16,no reply')

    else:
        invalidACK()

(2)Reader.py

import random

from Tag import Tag

class Reader:
    def __init__(self):
        self.readerID = self.setReaderID()
        self.crush = 0
        self.noRespond = 0
        self.Q = 2

    # 给每一个阅读器设置一个唯一的16位ID
    def setReaderID(self):
        str = ''
        ID = str.join(random.choice('0123456789') for i in range(16))
        return ID

    # 定义阅读器的标签选择规则 tag=0 选择所有标签 tag=1 选择奇数标签 =2选择偶数标签
    def select(self, tagArray, tag=0):
        if tag == 0:
            for i in range(len(tagArray)):
                tagArray[i].selected = True
            print('select阶段, 所有标签全部被选中')
        elif tag == 1:
            for i in range(1, len(tagArray), 2):
                tagArray[i].selected = True
            print('select阶段, 奇数号标签全部被选中')
        else:
            for i in range(0, len(tagArray), 2):
                tagArray[i].selected = True
            print('select阶段, 偶数号标签全部被选中')

    def QueryTagState(self, tagArray):
        cnt, index = 0, 0  # cnt为此刻slot值为0的标签数目,index为可得到回应的标签
        for i in range(len(tagArray)):
            if tagArray[i].slot == 0:
                cnt += 1
                index = i
        if cnt > 1:
            print('多个tag回应, 发生碰撞, 无法识别, 为Arbitrate状态')
            self.crush += 1
            # 碰撞次数大于随即数右端点的一半
            if self.crush > Tag.endPoint / 2:
                self.QueryAdjust()
            return None, index
        elif cnt == 1:
            tagArray[index].setState('Reply')
            print('{}号tag,其ID为{},slot值为0, 回应reader的随机数为{}, tag进入{}状态'.format(index, tagArray[index].id,
                                                                             tagArray[index].returnRN16(),
                                                                             tagArray[index].state))
            return tagArray[index].returnRN16(), index
        else:
            print('无tag回应, 均为Arbitrate状态')
            self.noRespond += 1
            # 无响应次数大于随即数右端点的一半
            if self.noRespond > Tag.endPoint / 2:
                self.QueryAdjust()
            return None, index

    # 阅读器发出查询Query命令,被选择的标签开始置随机数slot
    def Query(self, tagArray):
        print('********发出第一次Query********')
        for i in range(len(tagArray)):
            if tagArray[i].selected == True:
                tagArray[i].setSlot()
                tagArray[i].setState('Arbitrate')
                print('{}号tag,状态为{}'.format(i, tagArray[i].state))

        tag, index = self.QueryTagState(tagArray)
        return tag, index

    def QueryRep(self, tagArray):
        print('********发出QueryRep, 各标签slot值减1, 或恰好有标签响应********')
        for i in range(len(tagArray)):
            if tagArray[i].selected == True:
                tagArray[i].reduceSlot()

        tag, index = self.QueryTagState(tagArray)
        return tag, index

    def QueryAdjust(self, tagArray):
        print('********发出QueryAdjust, 根据碰撞次数和未响应次数调整slot阈值********')
        tagNum = len(tagArray)
        if self.noRespond > tagNum * 2:
            print('等待响应时间过长, {}轮未得到响应, 下调slot阈值'.format(self.noRespond))
            for i in range(tagNum):
                tagArray[i].adjustSlot(tag=False)
            self.noRespond = 0
        elif self.crush > tagNum * 2:
            print('碰撞{}轮, 过于频繁, 上调slot阈值'.format(self.crush))
            for i in range(tagNum):
                tagArray[i].adjustSlot(tag=True)
            self.crush = 0
        else:
            print('无需调整slot阈值, 已碰撞{}次, 等待响应{}轮'.format(self.crush, self.noRespond))

    # 收到标签的RN16 1.返回ACK指令夹带原RN16 或 2.返回ACK指令,但夹带的RN16错误
    def ACK(self, RN16):
        print('我收到了你的RN16{},ACK'.format(RN16))
        return RN16

    # 得到EPC码后 再次请求随机数 RN16为标签第一次返回的随机数
    def Req_RN(self, EPC, RN16):
        print('我收到了你的EPC{}, Req_RN, 请求返回一个新的随机数'.format(EPC))
        return RN16

    # 得到handle后 随机返回一次命令已经确认的handle值
    def Command(self, handle):
        commandSet = ['read', 'write', 'lock', 'kill']
        command = random.choice(commandSet)
        print('发出新命令{}'.format(command))
        return command, handle

    def read(self, tag):
        print('read tag data:')
        print('tagID:{}\n tagEPC:{}\n tagData:{}'.format(tag.id, tag.EPC, tag.data))
        return 1

    def write(self, tag):
        data = input('待写入数据:')
        tag.data = data
        print('写入完成')
        return 1

    def lock(self, tag):
        print('tagID:{}已lock'.format(tag.id))
        return 1

    def kill(self, tag):
        print('tagID:{} killed,访问结束'.format(tag.id))
        return 0

(3)Tag.py

import random

class Tag:
    # 类属性
    endPoint = 4

    def __init__(self):
        self.id = self.setID()
        self.slot = None
        self.state = 'Ready'
        self.EPC = self.setEPC()
        self.selected = False
        self.len = None
        self.sendTime = None
        self.readerNum = None
        self.data = self.initData()

    # 给每一个标签一个唯一ID 生成16位随机数
    def setID(self):
        str = ''
        ID = str.join(random.choice('0123456789') for i in range(16))
        print('初始化标签ID,tagID={}'.format(ID))
        return ID

    # 给每个标签赋值一个给定区间[0,10]的随机数
    def setSlot(self):
        self.slot = random.randint(0, self.endPoint)
        print('随机给定slot值,tagID={}的slot值为{}, tag的右端点是{}'.format(self.id, self.slot, self.endPoint))

    # 收到QueryAdjust就调整区间右端点 根据tag的值来更改 调整槽计数器的范围 True上调 False下调
    def adjustSlot(self, tag=True):
        if tag == True:
            print()
            Tag.endPoint *= 2
        else:
            Tag.endPoint /= 2
        self.setSlot()

    def setLength(self, length):
        """
        function: 设置数据包长度len和对应所需要的花费时间
        :return: NULL
        """
        self.len = length
        self.sendTime = length * 0.5 + 0.1   #数据包发送时间

    # 自减Slot
    def reduceSlot(self):
        self.slot = (self.slot-1+self.endPoint)%self.endPoint

    # 给每个标签赋值一个96位的随机数 即它的EPC码 只初始化一次
    def setEPC(self):
        str = ''
        RN96 = str.join(random.choice('0123456789') for i in range(96))
        return RN96

    # 返回EPC码
    def returnEPC(self):
        return self.EPC

    # 返回16位的随机数 RN16
    def returnRN16(self):
        str = ''
        RN16 = str.join(random.choice('0123456789') for i in range(16))
        return RN16

    # 返回一个新的随机数 即Handle
    def returnHandle(self):
        return self.returnRN16()

    # 改变标签状态
    def setState(self, newState):
        self.state = newState

    def initData(self):
        foodSet = ['奥利奥', '趣多多', '好丽友', '喜之郎', '娃哈哈']
        priceSet = ['10', '15', '21', '8', '29']
        food = random.choice(foodSet)
        price = random.choice(priceSet)
        return '我的存储区里搁了一个{},{}元一个'.format(food, price)


(4)概要介绍

在EPC C1G2协议标准中,标签的行为可以用有限状态机来描述,七个状态。

就绪态(ready state)
        就绪态是标签在通电前所处的状态。标签处于就绪态,不参与询问过程。
一个询问过程由阅读器发出的查询命令引起,由下一个查询命令终止。阅读器通过查询命令获得标签的EPC码。
当标签接收到一个查询命令,离开就绪态。标签从阅读器发送的查询命令中,选择一个参数生成随机数用于计算发送的时隙。如果随机数为0,则标签进入恢复状态。否则标签进入仲裁态。
仲裁态(arbitrate state)
        当标签处于仲裁态时,它将参加这一轮的查询。但由于此时的标签的时隙数不为0,所以标签等待时隙数变为0.当标签的时隙数变为0时,标签进入回复状态。
回复态(reply state)
        当标签需要回复EPC码时,首先进入的是回复态。这时,标签的时隙数为0,标签返回一个16比特的随机数RN16给阅读器。发送RN16是标签发送EPC码给阅读器的第一步。如果标签正确地接收到RN16,它将会发送一个确认命令ACK给标签。在接收RN16的时候,阅读器可能无法正确接收,主要原因有:a.两个或多个标签同时发生RN16给阅读器b.其它射频信号干扰了RN16的传输c.阅读器错过接收RN16的时间。
        如果标签成功接收到来自阅读器的确认命令ACK,标签将会发送它的EPC码,同时发送PC码(描述标签的物理信息)和CRC码(用于错误检测)。
接收到确认命令ACK引起标签从回复态进入确认态。标签处于回复态的时间有限,只有标签发送RN16后,没有接收到阅读器的任何命令的有效时间才处于该状态。此后,标签会自动进入仲裁态。
确认态(acknowledged state)
        当标签发送它的EPC码给阅读器后 ,标签进入确认态。确认态是标签进入访问命令的必经状态。在确认态中,标签不会被杀死。确认态就像回复态一样,有一个计时器,每当接收到一个命令式,计时器将会置位0。如果标签在指定的时间内没有收到来自阅读器的命令,标签将会自动返回仲裁态。
开放态
安全态
杀死态

        杀死操作不能被解除,它将会永远地毁坏标签。 对于一个标签而言,它是在接收到阅读器的正确的密码和杀死命令之后,才会进入杀死态。此后,标签会一直处于杀死态而不是就绪态。当标签进入杀死态,不会再响应阅读器的任何命令。

参考链接:

(24条消息) 标签的状态机_Caramel_biscuit的博客-CSDN博客_在确认态,标签会回复给阅读器pc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值