如图,电流和电压方向一致吗从下向上的方向,电压源输出为多少?

据估计,打鼾影响了美国57%的男性和40%的女性。它甚至发生在高达27%的儿童身上。这些统计数据表明打鼾很普遍,但其严重程度和健康影响可能有所不同。打鼾可能是轻微的、偶尔的和无关紧要的,也可能是严重的潜在睡眠相关呼吸障碍的征兆。打鼾是由喉咙后部气道附近组织的嘎嘎声和振动引起的。在睡眠期间,肌肉松弛,使气道变窄,当我们吸气和呼气时,流动的空气会导致组织颤动并发出噪音。阻塞性睡眠呼吸暂停是一种呼吸障碍,在睡眠期间气道阻塞或塌陷,导致反复呼吸困难。打鼾是阻塞性睡眠呼吸暂停最常见的症状之一。除非有人告诉他们,否则大多数打鼾的人都没有意识到这一点,这也是睡眠呼吸暂停未被诊断出的部分原因。在这个项目中,我将建立一个非侵入性低功率边缘设备的概念证明,它可以在你睡眠期间监测你的打鼾情况,并发出振动。开发环境我们使用EdgeImpulseStudio进行特征生成以及TensorFlowLite模型的创建和训练。我们需要在https://studio.edgeimpulse.com注册一个免费帐户并创建一个项目才能开始。MacOS用于本地开发工作。数据采集我们使用了Audioset,一个手动注释的音频事件的大规模数据集,来下载夜间可能发生的打鼾和其他自然声音。AudioSet由632个音频事件类的扩展本体和从YouTube视频中提取的人类标记的10秒声音剪辑的集合组成。音频从选定事件的YouTube视频中提取,并转换为波形音频文件格式(wav),具有16位深度的单声道,采样率为16KHz。下载了从AudiosetOntology中选择的以下类别。第一列是类别ID,第二列是类别标签。/m/01d3sdSnoring/m/07yv9Vehicle/m/01jt3mToiletflush/m/06mb1Rain/m/03m9d0zWind/m/07c52Television/m/06bz3Radio/m/028v0cSilence/m/03vt0Insect/m/07qjznlTick-tock/m/0bt9lrDog/m/01hsr_Sneeze/m/01b_21Cough/m/07ppn3jSniff/m/07pbtc8Walk,footsteps/m/02fxyjHumming/m/07q6cd_Squeak/m/0btp2Trafficnoise,roadwaynoise/m/09l8gHumanVoice/m/07pggtnChirp,tweet/t/dd00002Babycry,infantcry/m/04rlfMusic数据集分为两类,打鼾和噪声。通过过滤平衡训练、不平衡训练和评估数据集CSV文件创建了两个CSV文件snoring.csv和noise.csv,其中包含YouTube剪辑URL和其他元数据,可以从这里下载。下面的bash脚本(download.sh)用于下载视频剪辑并将音频提取为wav文件。请在运行以下命令之前安装youtube-dl和ffmpeg。#!/bin/bashSAMPLE_RATE=16000#fetch_youtube_clip(videoID,startTime,endTime)fetch_youtube_clip(){echo"Fetching$1($2to$3)..."outname="$1_$2"if[-f"${outname}.wav"];thenecho"Filealreadyexists."returnfiyoutube-dlhttps://youtube.com/watch?v=$1\--quiet--extract-audio--audio-formatwav\--output"$outname.%(ext)s"if[$?-eq0];thenyes|ffmpeg-loglevelquiet-i"./$outname.wav"-ar$SAMPLE_RATE\-ac1-ss"$2"-to"$3""./${outname}_out.wav"mv"./${outname}_out.wav""./$outname.wav"elsesleep1fi}grep-E'^[^#]'|whilereadlinedofetch_youtube_clip$(echo"$line"|sed-E's/,//g')done要执行脚本,请运行以下命令。$catnoise.csv|./download.sh$catsnoring.csv|./download.sh使用EdgeImpulseUploader将数据集上传到EdgeImpulseStudio。请按照此处的说明安装EdgeImpulseCLI工具并执行以下命令。$edge-impulse-uploader--categorysplit--labelsnoringsnoring/*.wav$edge-impulse-uploader--categorysplit--labelnoisenoise/*.wav上面的命令还将数据集拆分为训练和测试样本。我们可以在EdgeImpulseStudio的数据采集页面中看到上传的数据集。所述打鼾事件的音频剪辑有背景噪声在其中从通过分裂段夹子取出多个打鼾事件之间。该噪声类音频剪辑使用,无需任何修改。我们可以通过选择每个样本并从下拉菜单中单击“拆分样本”来进行拆分,但这是一项耗时且乏味的工作。幸运的是,有一个EdgeImpulseSDKAPI可用于自动化该过程。importjsonimportrequestsimportloggingimportthreadingAPI_KEY="Keys"projectId="headers={"Accept":"application/json","x-api-key":API_KEY}defsegment(tid,ids):forsampleIdinids:url1="https://studio.edgeimpulse.com/v1/api/{}/raw-data/{}/find-segments".format(projectId,sampleId)payload1={"shiftSegments":True,"segmentLengthMs":1500}response1=requests.request("POST",url1,json=payload1,headers=headers)resp1=json.loads(response1.text)segments=resp1["segments"]iflen(segments)==0:continuepayload2={"segments":segments}url2="https://studio.edgeimpulse.com/v1/api/{}/raw-data/{}/segment".format(projectId,sampleId)response2=requests.request("POST",url2,json=payload2,headers=headers)logging.info('{}{}{}'.format(tid,sampleId,response2.text))if__name__=="__main__":format="%(asctime)s:%(message)s"logging.basicConfig(format=format,level=logging.INFO,datefmt="%H:%M:%S")querystring={"category":"testing","excludeSensors":"true"}url="https://studio.edgeimpulse.com/v1/api/{}/raw-data".format(projectId)response=requests.request("GET",url,headers=headers,params=querystring)resp=json.loads(response.text)id_list=list(map(lambdas:s["id"],resp["samples"]))div=8n=int(len(id_list)/div)threads=list()foriinrange(div):ifi==(div-1):ids=id_list[n*i:]else:ids=id_list[n*i:n*(i+1)]x=threading.Thread(target=segment,args=(i,ids))threads.append(x)x.start()forthreadinthreads:thread.join()logging.info("Finished")训练转到ImpulseDesign>CreateImpulse页面,然后单击Addaprocessingblock并选择Spectrogram,这是一种表示信号强度或“响度”随时间在特定波形中存在的各种频率的可视化方式。此外,在同一页面上单击添加学习块并选择从数据中学习模式的神经网络(Keras),并将其应用于新数据。我们选择了1000毫秒窗口大小和125毫秒窗口增加。现在单击“保存冲动”按钮。现在转到ImpulseDesign>Spectrogram页面并更改参数,如下图所示,然后单击Saveparameters按钮。我们选择了FrameLength=0.02s,framestride=0.01538s和。频带=128(FFT大小),并且本底噪声=-54dB。本底噪声用于滤除频谱图中的背景噪声。它首先将窗口划分为多个重叠的帧。可以通过参数Framelength和Framestride调整帧的大小和数量.例如,窗口为1000毫秒,帧长为20毫秒,步幅为15.38毫秒,它将创建65个时间帧。然后使用FFT(快速傅立叶变换)将每个时间帧划分为频率区间,然后我们计算其功率谱。频率区间的数量等于频段参数除以2加1。频谱图块生成的特征等于生成的时间帧数乘以频率区间的数量。单击“保存参数”按钮重定向到另一个页面,我们应该在其中单击“生成特征”按钮。完成特征生成通常需要几分钟。我们可以在FeatureExplorer中看到生成的特征的3D可视化。现在转到ImpulseDesign>NNClassifier页面并从下拉菜单中选择SwitchtoKeras(expert)mode并定义模型架构。有许多现成的音频分类模型可用,但它们具有大量参数,因此不适用于256KB或更少内存的微控制器。经过大量试验,我们创建了如下所示的模型架构。importtensorflowastffromtensorflow.keras.modelsimportSequentialfromtensorflow.keras.layersimportReshape,Conv2D,Flatten,ReLU,Dropout,MaxPooling2D,Densefromtensorflow.keras.optimizers.schedulesimportInverseTimeDecayfromtensorflow.keras.optimizersimportAdamfromtensorflow.keras.layers.experimentalimportpreprocessingsys.path.append('./resources/libraries')importei_tensorflow.trainingchannels=1columns=65rows=int(input_length/(columns*channels))norm_layer=preprocessing.Normalization()norm_layer.adapt(train_dataset.map(lambdax,_:x))#modelarchitecturemodel=Sequential()model.add(Reshape((rows,columns,channels),input_shape=(input_length,)))model.add(preprocessing.Resizing(24,24,interpolation='nearest'))model.add(norm_layer)model.add(Conv2D(16,kernel_size=3))#model.add(BatchNormalization())#model.add(Activation('relu'))model.add(ReLU(6.0))model.add(Conv2D(32,kernel_size=3))#model.add(BatchNormalization())#model.add(Activation('relu'))model.add(ReLU(6.0))model.add(MaxPooling2D(pool_size=2,strides=2,padding='same'))model.add(Dropout(0.7))model.add(Flatten())model.add(Dense(64))#model.add(BatchNormalization())#model.add(Activation('relu'))model.add(ReLU(6.0))#model.add(Dropout(0.50))model.add(Dense(32))#model.add(BatchNormalization())#model.add(Activation('relu'))model.add(ReLU(6.0))#model.add(Dropout(0.50))model.add(Dense(classes,activation='softmax',name='y_pred'))BATCH_SIZE=64lr_schedule=InverseTimeDecay(0.0005,decay_steps=train_sample_count//BATCH_SIZE*15,decay_rate=1,staircase=False)defget_optimizer():returnAdam(lr_schedule)train_dataset=train_dataset.batch(BATCH_SIZE,drop_remainder=False)validation_dataset=validation_dataset.batch(BATCH_SIZE,drop_remainder=False)callbacks.append(BatchLoggerCallback(BATCH_SIZE,train_sample_count))#traintheneuralnetworkmodel.compile(loss='categorical_crossentropy',optimizer=get_optimizer(),metrics=['accuracy'])print(model.summary())model.fit(train_dataset,epochs=70,validation_data=validation_dataset,verbose=2,callbacks=callbacks)在定义模型架构时,我们已尽力针对TinyML用例对其进行优化。由于64x65单通道频谱图特征将具有大量训练参数,并且编译后的模型不适合可用的微控制器RAM,我们将频谱图调整为24x24大小,这是模型大小与精度的最佳选择。此外,我们使用了受限范围激活(ReLU6),因为ReLU6将输出限制为[0,6],并且训练后量化不会降低准确性。模型概要如下。Model:"sequential"_________________________________________________________________Layer(type)OutputShapeParam#=================================================================reshape(Reshape)(None,64,65,1)0_________________________________________________________________resizing(Resizing)(None,24,24,1)0_________________________________________________________________normalization(Normalization(None,24,24,1)3_________________________________________________________________conv2d(Conv2D)(None,22,22,16)160_________________________________________________________________re_lu(ReLU)(None,22,22,16)0_________________________________________________________________conv2d_1(Conv2D)(None,20,20,32)4640_________________________________________________________________re_lu_1(ReLU)(None,20,20,32)0_________________________________________________________________max_pooling2d(MaxPooling2D)(None,10,10,32)0_________________________________________________________________dropout(Dropout)(None,10,10,32)0_________________________________________________________________flatten(Flatten)(None,3200)0_________________________________________________________________dense(Dense)(None,64)204864_________________________________________________________________re_lu_2(ReLU)(None,64)0_________________________________________________________________dense_1(Dense)(None,32)2080_________________________________________________________________re_lu_3(ReLU)(None,32)0_________________________________________________________________y_pred(Dense)(None,2)66=================================================================Totalparams:211,813Trainableparams:211,810Non-trainableparams:3现在单击开始训练按钮并等待大约一个小时直到训练完成。我们可以在下面看到训练输出。该模型具有94.6%的准确率。测试我们可以在测试中测试模型。数据集通过转到模型测试页面并单击分类所有按钮。该模型在测试数据集上的准确率为88.58%。部署由于我们将在ArduinoNanoBLEsense部署模型,因此在部署页面我们将选择创建库>Arduino选项。对于Selectoptimization选项,我们将选择EnableEONCompiler以减少模型的内存使用。此外,我们将选择量化(Int8)模型。现在单击Build按钮,几秒钟后库包将下载到本地计算机。硬件设置我们将使用带有板载麦克风的ArduinoNano33BLESense。由于ArduinoNano33BLESense上的5V引脚默认断开连接,要使用5V引脚为振动电机供电,我们需要在标记为VUSB的两个焊盘上制作一个焊桥(下图中的红色矩形突出显示)。振动电机使用直接焊接在ArduinoNanoBLE传感头引脚上的Grove连接器连接。原理图可以在下文原理图部分找到。运行推理请按照此处的说明下载并安装ArduinoIDE。安装后,打开ArduinoIDE并通过转到工具>开发板>开发板管理器为ArduinoNano33BLESense安装开发板包。搜索如下图的板子包并安装。板包安装完成后,从Tools>Board>ArduinoMbedOSNanoBoards菜单中选择ArduinoNano33BLE。另外,从工具>端口菜单中选择连接的开发板的串口。我们需要使用库管理器(工具>管理库...)安装RingBuffer库。下面是推理的代码。应用程序使用双缓冲区连续捕获音频事件。//Ifyourtargetislimitedinmemoryremovethismacrotosave10KRAM#defineEIDSP_QUANTIZE_FILTERBANK0/**Definethenumberofslicespermodelwindow.E.g.amodelwindowof1000mswithslicespermodelwindowsetto4.Resultsinaslicesizeof250ms.Formoreinfo:https://docs.edgeimpulse.com/docs/continuous-audio-sampling*/#defineEI_CLASSIFIER_SLICES_PER_MODEL_WINDOW3/*Includes----------------------------------------------------------------*/#include#include#include#include/**Audiobuffers,pointersandselectors*/typedefstruct{signedshort*buffers[2];unsignedcharbuf_select;unsignedcharbuf_ready;unsignedintbuf_count;unsignedintn_samples;}inference_t;staticinference_tinference;staticboolrecord_ready=false;staticsignedshort*sampleBuffer;staticbooldebug_nn=false;//Setthistotruetoseee.g.featuresgeneratedfromtherawsignalstaticintprint_results=-(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW);boolalert=false;RingBuflast_ten_predictions;intgreenLED=23;intvibratorPin=3;//VibrationmotorconnectedtoD3PWMpinboolis_motor_running=false;voidrun_vibration(){if(alert){is_motor_running=true;for(inti=0;i{analogWrite(vibratorPin,30);delay(1000);analogWrite(vibratorPin,0);delay(1500);}is_motor_running=false;}else{if(is_motor_running){analogWrite(vibratorPin,0);}}yield();}/**@briefPrintffunctionusesvsnprintfandoutputusingArduinoSerial@param[in]formatVariableargumentlist*/voidei_printf(constchar*format,...){staticcharprint_buf[1024]={0};va_listargs;va_start(args,format);intr=vsnprintf(print_buf,sizeof(print_buf),format,args);va_end(args);if(r>0){Serial.write(print_buf);}}/**@briefPDMbufferfullcallbackGetdataandcallaudiothreadcallback*/staticvoidpdm_data_ready_inference_callback(void){intbytesAvailable=PDM.available();//readintothesamplebufferintbytesRead=PDM.read((char*)&sampleBuffer[0],bytesAvailable);if(record_ready==true){for(inti=0;i>1;i++){inference.buffers[inference.buf_select][inference.buf_count++]=sampleBuffer[i];if(inference.buf_count>=inference.n_samples){inference.buf_select^=1;inference.buf_count=0;inference.buf_ready=1;}}}}/**@briefInitinferencingstructandsetup/startPDM@param[in]n_samplesThensamples@return{description_of_the_return_value}*/staticboolmicrophone_inference_start(uint32_tn_samples){inference.buffers[0]=(signedshort*)malloc(n_samples*sizeof(signedshort));if(inference.buffers[0]==NULL){returnfalse;}inference.buffers[1]=(signedshort*)malloc(n_samples*sizeof(signedshort));if(inference.buffers[0]==NULL){free(inference.buffers[0]);returnfalse;}sampleBuffer=(signedshort*)malloc((n_samples>>1)*sizeof(signedshort));if(sampleBuffer==NULL){free(inference.buffers[0]);free(inference.buffers[1]);returnfalse;}inference.buf_select=0;inference.buf_count=0;inference.n_samples=n_samples;inference.buf_ready=0;//configurethedatareceivecallbackPDM.onReceive(&pdm_data_ready_inference_callback);PDM.setBufferSize((n_samples>>1)*sizeof(int16_t));//initializePDMwith://-onechannel(monomode)//-a16kHzsamplerateif(!PDM.begin(1,EI_CLASSIFIER_FREQUENCY)){ei_printf("FailedtostartPDM!");}//setthegain,defaultsto20PDM.setGain(127);record_ready=true;returntrue;}/**@briefWaitonnewdata@returnTruewhenfinished*/staticboolmicrophone_inference_record(void){boolret=true;if(inference.buf_ready==1){ei_printf("Errorsamplebufferoverrun.Decreasethenumberofslicespermodelwindow""(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)\n");ret=false;}while(inference.buf_ready==0){delay(1);}inference.buf_ready=0;returnret;}/**Getrawaudiosignaldata*/staticintmicrophone_audio_signal_get_data(size_toffset,size_tlength,float*out_ptr){numpy::int16_to_float(&inference.buffers[inference.buf_select^1][offset],out_ptr,length);return0;}/**@briefStopPDMandreleasebuffers*/staticvoidmicrophone_inference_end(void){PDM.end();free(inference.buffers[0]);free(inference.buffers[1]);free(sampleBuffer);}voidsetup(){Serial.begin(115200);pinMode(greenLED,OUTPUT);pinMode(greenLED,LOW);pinMode(vibratorPin,OUTPUT);//setsthepinasoutput//summaryofinferencingsettings(frommodel_metadata.h)ei_printf("Inferencingsettings:\n");ei_printf("\tInterval:%.2fms.\n",(float)EI_CLASSIFIER_INTERVAL_MS);ei_printf("\tFramesize:%d\n",EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);ei_printf("\tSamplelength:%dms.\n",EI_CLASSIFIER_RAW_SAMPLE_COUNT/16);ei_printf("\tNo.ofclasses:%d\n",sizeof(ei_classifier_inferencing_categories)/sizeof(ei_classifier_inferencing_categories[0]));run_classifier_init();if(microphone_inference_start(EI_CLASSIFIER_SLICE_SIZE)==false){ei_printf("ERR:Failedtosetupaudiosampling\r\n");return;}Scheduler.startLoop(run_vibration);}voidloop(){boolm=microphone_inference_record();if(!m){ei_printf("ERR:Failedtorecordaudio...\n");return;}signal_tsignal;signal.total_length=EI_CLASSIFIER_SLICE_SIZE;signal.get_data=&microphone_audio_signal_get_data;ei_impulse_result_tresult={0};EI_IMPULSE_ERRORr=run_classifier_continuous(&signal,&result,debug_nn);if(r!=EI_IMPULSE_OK){ei_printf("ERR:Failedtorunclassifier(%d)\n",r);return;}if(++print_results>=(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)){//printthepredictionsei_printf("Predictions");ei_printf("(DSP:%dms.,Classification:%dms.,Anomaly:%dms.)",result.timing.dsp,result.timing.classification,result.timing.anomaly);ei_printf(":\n");for(size_tix=0;ixei_printf("%s:%.5f\n",result.classification[ix].label,result.classification[ix].value);if(ix==1&&!is_motor_running&&result.classification[ix].value>0.9){if(last_ten_predictions.isFull()){uint8_tk;last_ten_predictions.pop(k);}last_ten_predictions.push(ix);uint8_tcount=0;for(uint8_tj=0;jcount+=last_ten_predictions[j];//ei_printf("%d,",last_ten_predictions[j]);}//ei_printf("\n");ei_printf("Snoring\n");pinMode(greenLED,HIGH);if(count>=5){ei_printf("Triggervibrationmotor\n");alert=true;}}else{ei_printf("Noise\n");pinMode(greenLED,LOW);alert=false;}print_results=0;}}}#if!defined(EI_CLASSIFIER_SENSOR)
EI_CLASSIFIER_SENSOR!=EI_CLASSIFIER_SENSOR_MICROPHONE#error"Invalidmodelforcurrentsensor."#endif要运行推理草图,请使用以下命令克隆应用程序存储库。$gitclonehttps://github.com/metanav/Snoring_Guardian.git在ArduinoIDE中打开Arduino草图Snoring_Guardian/snoring_detection_inferencing/examples/tflite_micro_snoring_detection/tflite_micro_snoring_detection.ino。编译并上传固件到连接的开发板。我们可以使用波特率115200bps的Tools>SerialMonitor查看推理输出。视频演示:套管最终版本的设备被放置在一个带有移动电源的袋子里。小袋中有一个小开口,可以让位于开口附近的麦克风听到声音。现场演示原理图:源代码:点击下载总结这个项目为一个现实生活中的问题提供了一个解决方案,这个问题看起来很有趣但需要仔细关注。它是一种易于使用且方便的设备,通过在边缘运行推理来尊重用户的隐私。该项目还展示了一个简单的神经网络可以通过以正确的方式进行信号处理来解决复杂的问题,并且可以在低功耗资源受限的微型设备上运行。尽管TensorFlowLiteMicro模型运行良好,但仍有改进的空间。随着更多的训练数据,模型可以变得更加准确和健壮。

我要回帖

更多关于 电流和电压方向一致吗 的文章

 

随机推荐