使用micro:bit、XinaBox和IoT实现数据捕捉

micro:bit编程、教学、展示
STEM
回复
头像
shaoziyang
帖子: 721
注册时间: 2019年 10月 21日 13:48

使用micro:bit、XinaBox和IoT实现数据捕捉

#1

帖子 shaoziyang » 2019年 11月 24日 22:38

来自:https://www.hackster.io/PragmaticPhil/d ... iot-f566fe

在BBC micro:bit上远程捕获加速度计数据,然后使用XinaBox Wi-Fi网关将数据传输到IoT平台。

图片


需要的材料 软件和在线服务

Ubidots,30天的免费IoT平台。


故事

加速度是使物体改变其速度的原因:当对物体施加足够强度的力并使其减速、加速或改变方向加速度已经发生。这是我们对这个世界的一个基本观察。不仅体验简单直观,而且分析也很有吸引力和教育性。能够可视化与掉落或投掷球或加速火箭相关的力,或者看到当移动物体撞击墙壁时发生的事情。

该项目将向您展示如何使用micro:bit和IoT来分析一段时间内的加速度。您可以使用此处描述的代码和技术来测量适用于micro:bit的移动对象的加速度。

下面是加速度可视化的示例:该图表显示了在平面中移动的物体的实际数据。我现在不会对它说太多 - 我鼓励你看一下加速度曲线并尝试推断出发生了什么(例如它是一个球弹跳,还是滚动,还是别的什么?)。本博客的最后一部分揭示了下面所示的真实世界事件。

图片

BBC micro的一个很棒的功能:它板载加速度计。许多年轻的学习者花了大量的时间从加速度计读取数据并以各种创造性的方式使用它。我见过无数遥控车的遥控车,手套作为控制器,一些非常聪明的平衡机器人,甚至基于手势的音乐'乐器'。

但是加速度计作为一种学习工具也具有巨大的潜力,可以支持速度/距离/时间三角函数,这种三角形非常重要,表面上是物理学中的知识领域。有什么更好的教授加速方法,而不是让年轻的学习者见证一个事件,然后回顾那个事件背后的力量?

在这篇博客中,我将向您展示如何:
  • 使用micro:bit来收集一堆加速度计读数。这个micro:bit将“远程”收集数据,这意味着它将在事件期间收集数据并将其存储在micro:bit中,以便稍后共享(我将在稍后解释原因)。
  • 一旦我们测量的事件完成,我们将使用基于Wi-fi的micro:bit XinaBox IoT入门套件(我在这里称为我们的micro:bit网关)来获取数据并将其传输到物联网平台。
在这个例子中,我们将在两个人之间扔一个球然后将数据传到Ubidots IoT平台上。


为什么要远程收集原始加速度计数据?

micro:bit包括无线功能,使micro:bit能够和另一个设备(不是另一个micro:bit)之间启用蓝牙连接。

在某些情况下,可以在收集数据时使用无线电或BT传输数据。这种方法不适合导致这个博客的情况。当我被要求查看在开始时生成图表的现实世界问题时,需要考虑一些具体的挑战:
  • 数据不会丢失至关重要。我需要在运行期间存储数据,然后再将其提取出来,我无法承受无线数据包丢失或BT连接丢失。在运行期间保存数据意味着我们可以在空闲时检索它,如果数据检索失败,我们可以重新尝试。
  • 由于某些我将在后面解释的原因,在加速事件期间(后面将称之为运行)我想使用全部处理器时间来获取加速度计读数。如果我在运行期间传输数据,它会减少我们可以采样的数据量,这会影响数据的质量。
  • 我想存储大量数据,因此我需要确保非必要进程使用最少的内存。生产级别的数据收集程序(此处未包含)确保将microbit的数据扩展到极限!
  • 在跑步中移动的物体可能移动了相当远的距离,将接收器放在固定位置是有风险的。可以这样想 - 这里的代码可用于测量从高处掉落的身体的加速度。代码可以适用于读取数据10秒或更长时间,并且在那段时间内,它将从您丢弃的地方落下很长的距离。

所以,我已经完成了以下工作流程:

图片


进行测量
  • 将附加的microPython代码写入到micro:bit。
  • 连接到电池
  • 在你的球上切一个足够大的孔,以便将microbit和电池装入。
  • 用填充物填充球中的空间(我使用了气泡膜)
图片

一旦球建成,请执行以下操作以激活它:
  • 单击复位按钮以清除旧数据,然后单击A按键
  • 将从3开始倒计时 - 在倒计时结束时,LED上显示停止,表示microbit正在进行测量。在此期间扔球。
  • 测量阶段完成后,LED上会显示一个小方块。示例代码将记录大约3秒钟的数据,但您可以调整代码以增加或减少数据。
  • 单击B按钮时,它将启动将数据传输到micro:bit网关。
Simple和Advanced版本程序之间的区别在于数据收集技术。两者都是以每25ms保存1次数据(相当于每秒40次),对于Simple版本,我们每25ms只读取一次加速度计;而advance版将在25ms周期内读取加速度计最多75次,然后我们保存一个值,即所有75个读数的平均值,这样可以“平滑”数据:它可以减少异常值并在整个25ms周期内提供加速度最大近似值。为了了解数据质量的差异,我建议您自己尝试一下:使用两个版本的代码并查看数据图表。您可以阅读有关此方法的更多详细信息,何时使用它以及为什么它有效。

如果你查看代码,你会注意到我在y平面上记录加速度。因为对于我开发这个程序的真实场景,micro:bit在该平面中移动。然而,当涉及投掷球时,作用在y平面上的力是投掷microbit方向的分量。可以调整代码来记录x和z读数,以及你可以抛出球的方式,使加速度在你正在记录的平面上。

您可能还注意到代码需要提供120个读数。每25ms读取1次,这相当于3秒的数据收集,我发现以这种方式可以记录多达400个数据点。我使用文件来存储数据,而不是列表。两种方式各有利弊,我在此不再赘述(但如果你感兴趣可以提问)。


microbit网关+物联网平台


您需要执行以下操作:
  • 构建并设置micro:bit网关。
  • 连接到物联网平台

我建议您查看此博客,其中包含连接所需的所有信息。

在完成实验之前,请确保您的网关已通电并连接到Ubidots。

图片

请注意,此项目的网关中的microPython代码几乎与我编写的其他博客中使用的代码相同。针对每种情况进行调整相对容易,使用MakeCode图形编程时(很快!),这个过程将大大简化。


把它们放在一起
  • 如上所述,设置并投掷球。
  • 测量完成后,确保您的球中的microbit于网关的范围内(应该有几米)。
  • 确保您的网关已准备好接收数据。
  • 单击球上的B按钮
  • 坐下来看看这个过程。观看数据在Ubidots积累很有趣。

我的代码大约需要一分钟才能将所有数据传输到IoT平台。这个时间可以显着减少。花了这么长时间的原因是我一直非常谨慎 -- 我花了一些时间来调整延时,当我发现各种效果始终如一时,我就放弃了。我很乐意看到它的工作速度更快,所以如果你实现了这一点,请与我联系,让我知道如何做。


从头开始分析图表

让我们再看一下博客开头的图表。这一次,我已经设置了一些线来标记有趣的“阶段”:

图片

阶段0 :对象处于静止状态。
阶段1 :快速加速(甚至可以说是爆炸性的)。它在标记的点处达到峰值,但在此期间物体会加速。在此期间结束时,物体已达到其最大速度
阶段2 :在此期间开始时,物体的减速超过了加速的力,开始快速减速。
阶段3 :此处的数据有点“嘈杂” -- 物体在大部分时间内正在减速,减速率的偶然变化很有意思。
阶段4 :发生快速减速,速度同样迅速减小,直到达到零。
阶段5 :物体再次处于静止状态。


你有没有猜到它是什么?

这是一辆火箭动力汽车。在第1阶段,火箭发动机起火,然后停止发射,我们进入第2阶段。这一阶段看到阻力对车辆起作用。在第3阶段,汽车反弹一点,有时可能会离开地面并短暂地进入自由落体(这可能解释了那段时期内发现的轻微加速度,idk?)。在第4阶段,它会撞上一个特别设计的破碎系统,在大约1.5米的范围内将其减速至死点。这是一辆火箭动力汽车。在第1阶段,火箭发动机起火,然后停止发射,我们进入第2阶段。这一阶段看到阻力对车辆起作用。在第3阶段,汽车反弹一点,有时可能会离开地面并短暂地进入自由落体(这可能解释了那段时期内发现的轻微加速度)。在第4阶段,它会撞上一个特别设计的缓冲系统,在大约1.5米的范围内将其减速至停止。 

 

头像
shaoziyang
帖子: 721
注册时间: 2019年 10月 21日 13:48

Re: 使用micro:bit、XinaBox和IoT实现数据捕捉

#2

帖子 shaoziyang » 2019年 11月 24日 22:39

程序代码

Catch_Collector_Simple

代码: 全选

from microbit import *
import radio
radio.on()
radio.config(length=16, queue=64, channel=11, power=6)

accReadingList = []             #   This is a list - we will add each accelerometer reading we take to this list.
totalNumberOfReadings = 120     #   The code is limited to 150 data points - a few more is possible but there is an upper limit
numberOfReadings = 0            #   I don't use a loop to do the 150 readings, so I count how many we've done and stop at 150

readingFrequencyInMS = 25

startingTime = 0                #   We time how long it takes (although this is a factor of totalNumberOfReadings and our sampling period)
duration = 0

recordData = False             #   This is a switch we set to True when we are reading accelerometer data


def countDownValue(currentCount):       #   Just a little countdown to prepare the user for when the micro:bit begins measuring data.
    display.show(str(currentCount))
    sleep(1000)


def getAccelerometerReading(readingFrequencyInMS):
    sleep(readingFrequencyInMS)
    return accelerometer.get_y()


while True:
    if(button_a.was_pressed()):         #   Initiate the process of reading data
        countDownValue(3)
        countDownValue(2)
        countDownValue(1)
        display.show(".")
        recordData  = True
        startingTime = running_time()

    if(recordData):                                         #   True until we collect totalNumberOfReadings data points
        accReadingList.append(str(getAccelerometerReading(readingFrequencyInMS)))
        numberOfReadings += 1

    if((numberOfReadings >= totalNumberOfReadings) and recordData):    #   for 1 cycle after totalNumberOfReadings readings have been taken this switch is True
        recordData = False
        duration = running_time() - startingTime
        display.show(Image.DIAMOND)

    if(button_b.was_pressed()):
        display.show(Image.YES)
        radio.send("0_a" + str(duration))

        for i in range(0, totalNumberOfReadings - 1):
            if( (i >0) and (i % 50) == 0):
                display.show(Image.SQUARE_SMALL)
                sleep(7500)

            radio.send("0_c" + str(accReadingList[i]))
            display.show(Image.YES)
            sleep(100)
Catch_Collector_Advanced

代码: 全选

from microbit import *
import radio
radio.on()
radio.config(length=16, queue=64, channel=11, power=6)

accReadingList = []             #   This is a list - we will add each accelerometer reading we take to this list.
totalNumberOfReadings = 120     #   The code is limited to 150 data points - a few more is possible but there is an upper limit
numberOfReadings = 0            #   I don't use a loop to do the 150 readings, so I count how many we've done and stop at 150

readingFrequencyInMS = 25

startingTime = 0                #   We time how long it takes (although this is a factor of totalNumberOfReadings and our sampling period)
duration = 0

recordData = False             #   This is a switch we set to True when we are reading accelerometer data


def countDownValue(currentCount):       #   Just a little countdown to prepare the user for when the micro:bit begins measuring data.
    display.show(str(currentCount))
    sleep(1000)


def getAccelerometerReading(readingFrequencyInMS):
    loopStart = running_time()  #   record the time now, so we can stop the loop below after 25ms
    accSmoothReading = 0        #   we'll use this to find the sum of all readings during our 25ms
    accSmoothReadings = 0       #   and we count how many readings we are able to make.

    while(  (loopStart + readingFrequencyInMS -1) > running_time() ):   #   we stay in this loop for readingFrequencyInMS ms
        accSmoothReading += accelerometer.get_y()                       #   we add the accelerometer readings during this period
        accSmoothReadings += 1                                          #   and we record the number of readings we are able to make.

    if(accSmoothReadings > 0):                                      #   Unlikely we need this, but it means no risk of dividing by zero below.
        return (accSmoothReading / accSmoothReadings)

    return 0


while True:
    if(button_a.was_pressed()):         #   Initiate the process of reading data
        countDownValue(3)
        countDownValue(2)
        countDownValue(1)
        display.show(".")
        recordData  = True
        startingTime = running_time()

    if(recordData):                                         #   True until we collect totalNumberOfReadings data points
        accReadingList.append(str(getAccelerometerReading(readingFrequencyInMS)))
        numberOfReadings += 1

    if((numberOfReadings >= totalNumberOfReadings) and recordData):    #   for 1 cycle after totalNumberOfReadings readings have been taken this switch is True
        recordData = False
        duration = running_time() - startingTime
        display.show(Image.DIAMOND)

    if(button_b.was_pressed()):
        display.show(Image.YES)
        radio.send("0_a" + str(duration))

        for i in range(0, totalNumberOfReadings - 1):
            if( (i >0) and (i % 50) == 0):
                display.show(Image.SQUARE_SMALL)
                sleep(7500)

            radio.send("0_c" + str(accReadingList[i]))
            display.show(Image.YES)
            sleep(100)
Catch_Gateway

代码: 全选

from microbit import *
import radio
radio.on()
radio.config(length=16, queue=64, channel=11, power=6)
#   NOTE: radio settings are identical to those in the classroomMicrobit.py code.  Long queue is necessary as we process each slowly

uart.init(baudrate=9600, bits=8, parity=None, stop=1, tx=pin20, rx=pin19)
# NOTE: gets the uart (serial / USB) port on the micro:bit ready to communicate with the CW01


def sendMessageToCW01(parm, pauseLength):
    display.clear()
    uart.write(parm)
    sleep(pauseLength)
    data = uart.readline()
    while(data is None):
        data = uart.readline()

    if(len(str(data)) >0):
        uartMessageID = getIntegerFromString(data[:1])
        if(uartMessageID == 1):     display.show(Image.YES)     # NOTE: a tick is displayed when data is being transmitted correctly to the CW01
        else:                       display.show(Image.NO)      # NOTE: this means that a cross is displayed when an attempt to send data fails

    sleep(pauseLength)


def processRadioSignal(radioSignal):
    global testMode

    if(len(str(radioSignal)) < 4):   return False                       #   NOTE: valid radio signals are at least 3 or more characters long

    locationOfUnderscore = getLocationOfUnderscore(radioSignal)
    if(locationOfUnderscore == -1): return False                        #   NOTE: valid radio signals contain an underscore

    currentMicrobitID = getIntegerFromString(radioSignal[0:locationOfUnderscore])
    if(currentMicrobitID < 0):    return False                          #   NOTE: valid radio messages begin with an integer starting at 0.
    if(currentMicrobitID > 9): return False                             #   NOTE: IDs should go from 0 to 9

    #   NOTE: If we've reached this point of the code the radioSignal has passed all our validation checks.  It is 'safe' to process it.
    return sendValidMessageToCW01(radioSignal, locationOfUnderscore)

def sendValidMessageToCW01(radioSignal, locationOfUnderscore):
    messageType = str(radioSignal[locationOfUnderscore +1 : locationOfUnderscore +2])

    if(messageType == "a"):
        sendMessageToCW01("+4@YOURMICROBIT@duration@" + getValueFromRadioSignal(radioSignal, locationOfUnderscore) + "$", 250)
        return True

    if(messageType == "b"):
        sendMessageToCW01("+4@YOURMICROBIT@speed@" + getValueFromRadioSignal(radioSignal, locationOfUnderscore) + "$", 250)
        return True

    if(messageType == "c"):
        sendMessageToCW01("+4@YOURMICROBIT@AccReading@" + getValueFromRadioSignal(radioSignal, locationOfUnderscore) + "$", 250)
        return True

    return False


def getLocationOfUnderscore(radioSignal):
    #   NOTE: The underscore can only be in 1 of 2 places in the string, so KISS:
    radioSignalStr = str(radioSignal)
    if(radioSignalStr[1:2] == "_"):    return 1
    if(radioSignalStr[2:3] == "_"):    return 2
    return -1


def getIntegerFromString(uncheckedString):
    try: return int(uncheckedString)
    except ValueError: return -1


def getValueFromRadioSignal(radioSignal, locationOfUnderscore):
    #display.show(str(radioSignal[locationOfUnderscore +2 : len(radioSignal)]))
    try: return str(radioSignal[locationOfUnderscore +2 : len(radioSignal)])
    except: return "0"

#   INIT:

display.show(Image.SQUARE)          # NOTE: the square is shown on your micro:bit while it is connecting to Ubidots
sleep(2000)
uart.write("$")                     # NOTE: Cleans out the serial buffer
sleep(100)
uart.write("+9@?$")                 # NOTE: Reboots the CW01
sleep(5000)                         # NOTE: long delay is necessary - we need to give Wi-Fi time to sort itself out.
uart.write("$")                     # NOTE: Clean out Serial buffer, again.
sleep(500)


#sendMessageToCW01("+1@WIFINAME@WIFIPASSWORD$")
# EDIT GUIDELINES: you MUST enter the name and password of the Wi-Fi network you are trying to connect to in the line above.

#sendMessageToCW01("+2@DEFAULTTOKEN@?$")
# EDIT GUIDELINES: you MUST enter the DEFAULT TOKEN from Ubidots in the line above.

sendMessageToCW01("+3@things.ubidots.com@1883$", 750)
# NOTE: above line tells the micro;bit where to send the data to - its a Ubidots URL.


while True:
    if(processRadioSignal(radio.receive())):
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD)

    sleep(50)
​​​​​​​

回复

  • 随机主题
    回复总数
    阅读次数
    最新文章