Skip to content

Commit 35360b9

Browse files
committed
import inference app
1 parent 2f0c868 commit 35360b9

File tree

8 files changed

+805
-0
lines changed

8 files changed

+805
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
inference-app/build/
2+

inference-app/CMakeLists.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
cmake_minimum_required(VERSION 3.12)
2+
3+
# initialize pico_sdk from GIT
4+
# (note this can come from environment, CMake cache etc)
5+
# set(PICO_SDK_FETCH_FROM_GIT on)
6+
7+
# pico_sdk_import.cmake is a single file copied from this SDK
8+
# note: this must happen before project()
9+
include(pico_sdk_import.cmake)
10+
11+
project(pico_inference_app)
12+
13+
# initialize the Pico SDK
14+
pico_sdk_init()
15+
16+
# Define ARM_CPU, CMSIS ROOT and DSP to use CMSIS-DSP
17+
set(ARM_CPU "cortex-m0plus")
18+
set(ROOT ${CMAKE_CURRENT_LIST_DIR}/lib/CMSIS_5)
19+
set(DSP ${ROOT}/CMSIS/DSP)
20+
21+
set(CONFIGTABLE ON)
22+
set(RFFT_Q15_256 ON)
23+
set(ALLFAST ON)
24+
25+
# include CMSIS-DSP .cmake for GCC Toolchain
26+
include(${DSP}/Toolchain/GCC.cmake)
27+
28+
# add CMSIS-DSP Source directory as subdirectory
29+
add_subdirectory(${DSP}/Source EXCLUDE_FROM_ALL)
30+
31+
# rest of your project
32+
add_executable(pico_inference_app
33+
${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
34+
${CMAKE_CURRENT_LIST_DIR}/src/dsp_pipeline.cpp
35+
${CMAKE_CURRENT_LIST_DIR}/src/ml_model.cpp
36+
)
37+
38+
target_link_libraries(pico_inference_app
39+
pico_stdlib
40+
hardware_pwm
41+
pico-tflmicro
42+
pico_pdm_microphone
43+
CMSISDSPTransform CMSISDSPSupport CMSISDSPCommon CMSISDSPComplexMath CMSISDSPFastMath CMSISDSPBasicMath
44+
)
45+
46+
# enable usb output, disable uart output
47+
pico_enable_stdio_usb(pico_inference_app 1)
48+
pico_enable_stdio_uart(pico_inference_app 0)
49+
50+
# create map/bin/hex/uf2 file in addition to ELF.
51+
pico_add_extra_outputs(pico_inference_app)
52+
53+
add_subdirectory("lib/microphone-library-for-pico" EXCLUDE_FROM_ALL)
54+
add_subdirectory("lib/pico-tflmicro" EXCLUDE_FROM_ALL)

inference-app/src/dsp_pipeline.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#include "dsp_pipeline.h"
9+
10+
DSPPipeline::DSPPipeline(int fft_size) :
11+
_fft_size(fft_size),
12+
_hanning_window(NULL)
13+
{
14+
}
15+
16+
DSPPipeline::~DSPPipeline()
17+
{
18+
if (_hanning_window != NULL) {
19+
delete [] _hanning_window;
20+
21+
_hanning_window = NULL;
22+
}
23+
}
24+
25+
int DSPPipeline::init()
26+
{
27+
_hanning_window = new int16_t[_fft_size];
28+
if (_hanning_window == NULL) {
29+
return 0;
30+
}
31+
32+
for (size_t i = 0; i < _fft_size; i++) {
33+
float32_t f = 0.5 * (1.0 - arm_cos_f32(2 * PI * i / _fft_size));
34+
35+
arm_float_to_q15(&f, &_hanning_window[i], 1);
36+
}
37+
38+
if (arm_rfft_init_q15(&_S_q15, _fft_size, 0, 1) != ARM_MATH_SUCCESS) {
39+
return 0;
40+
}
41+
42+
return 1;
43+
}
44+
45+
void DSPPipeline::calculate_spectrum(const int16_t* input, int8_t* output, int32_t scale_divider, float scale_zero_point)
46+
{
47+
int16_t windowed_input[_fft_size];
48+
int16_t fft_q15[_fft_size * 2];
49+
int16_t fft_mag_q15[_fft_size / 2 + 1];
50+
51+
// apply the DSP pipeline: Hanning Window + FFT
52+
arm_mult_q15(_hanning_window, input, windowed_input, _fft_size);
53+
arm_rfft_q15(&_S_q15, windowed_input, fft_q15);
54+
arm_cmplx_mag_q15(fft_q15, fft_mag_q15, (_fft_size / 2) + 1);
55+
56+
int8_t* dst = output;
57+
58+
for (int j = 0; j < ((_fft_size / 2) + 1); j++) {
59+
*dst++ = __SSAT((fft_mag_q15[j] / scale_divider) + scale_zero_point, 8);
60+
}
61+
}
62+
63+
void DSPPipeline::shift_spectrogram(int8_t* spectrogram, int shift_amount, int spectrogram_width)
64+
{
65+
int spectrogram_height = _fft_size / 2 + 1;
66+
67+
memmove(spectrogram, spectrogram + (spectrogram_height * shift_amount), spectrogram_height * (spectrogram_width - shift_amount) * sizeof(spectrogram[0]));
68+
}

inference-app/src/dsp_pipeline.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#ifndef _DSP_PIPELINE_H_
9+
#define _DSP_PIPELINE_H_
10+
11+
#include "arm_math.h"
12+
13+
class DSPPipeline {
14+
public:
15+
DSPPipeline(int fft_size);
16+
virtual ~DSPPipeline();
17+
18+
int init();
19+
void calculate_spectrum(const int16_t* input, int8_t* output, int32_t scale_divider, float scale_zero_point);
20+
void shift_spectrogram(int8_t* spectrogram, int shift_amount, int spectrogram_width);
21+
22+
private:
23+
int _fft_size;
24+
int16_t* _hanning_window;
25+
arm_rfft_instance_q15 _S_q15;
26+
};
27+
28+
#endif

inference-app/src/main.cpp

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#include <stdio.h>
9+
10+
#include "pico/stdlib.h"
11+
#include "hardware/pwm.h"
12+
13+
extern "C" {
14+
#include "pico/pdm_microphone.h"
15+
}
16+
17+
#include "tflite_model.h"
18+
19+
#include "dsp_pipeline.h"
20+
#include "ml_model.h"
21+
22+
// constants
23+
#define SAMPLE_RATE 16000
24+
#define FFT_SIZE 256
25+
#define SPECTRUM_SHIFT 4
26+
#define INPUT_BUFFER_SIZE ((FFT_SIZE / 2) * SPECTRUM_SHIFT)
27+
#define INPUT_SHIFT 0
28+
29+
// microphone configuration
30+
const struct pdm_microphone_config pdm_config = {
31+
// GPIO pin for the PDM DAT signal
32+
.gpio_data = 2,
33+
34+
// GPIO pin for the PDM CLK signal
35+
.gpio_clk = 3,
36+
37+
// PIO instance to use
38+
.pio = pio0,
39+
40+
// PIO State Machine instance to use
41+
.pio_sm = 0,
42+
43+
// sample rate in Hz
44+
.sample_rate = SAMPLE_RATE,
45+
46+
// number of samples to buffer
47+
.sample_buffer_size = INPUT_BUFFER_SIZE,
48+
};
49+
50+
q15_t capture_buffer_q15[INPUT_BUFFER_SIZE];
51+
volatile int new_samples_captured = 0;
52+
53+
q15_t input_q15[INPUT_BUFFER_SIZE + (FFT_SIZE / 2)];
54+
55+
DSPPipeline dsp_pipeline(FFT_SIZE);
56+
MLModel ml_model(tflite_model, 128 * 1024);
57+
58+
int8_t* scaled_spectrum = nullptr;
59+
int32_t spectogram_divider;
60+
float spectrogram_zero_point;
61+
62+
void on_pdm_samples_ready();
63+
64+
int main( void )
65+
{
66+
// initialize stdio
67+
stdio_init_all();
68+
69+
printf("hello pico fire alarm detection\n");
70+
71+
gpio_set_function(PICO_DEFAULT_LED_PIN, GPIO_FUNC_PWM);
72+
73+
uint pwm_slice_num = pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN);
74+
uint pwm_chan_num = pwm_gpio_to_channel(PICO_DEFAULT_LED_PIN);
75+
76+
// Set period of 256 cycles (0 to 255 inclusive)
77+
pwm_set_wrap(pwm_slice_num, 256);
78+
79+
// Set the PWM running
80+
pwm_set_enabled(pwm_slice_num, true);
81+
82+
if (!ml_model.init()) {
83+
printf("Failed to initialize ML model!\n");
84+
while (1) { tight_loop_contents(); }
85+
}
86+
87+
if (!dsp_pipeline.init()) {
88+
printf("Failed to initialize DSP Pipeline!\n");
89+
while (1) { tight_loop_contents(); }
90+
}
91+
92+
scaled_spectrum = (int8_t*)ml_model.input_data();
93+
spectogram_divider = 64 * ml_model.input_scale();
94+
spectrogram_zero_point = ml_model.input_zero_point();
95+
96+
// initialize the PDM microphone
97+
if (pdm_microphone_init(&pdm_config) < 0) {
98+
printf("PDM microphone initialization failed!\n");
99+
while (1) { tight_loop_contents(); }
100+
}
101+
102+
// set callback that is called when all the samples in the library
103+
// internal sample buffer are ready for reading
104+
pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready);
105+
106+
// start capturing data from the PDM microphone
107+
if (pdm_microphone_start() < 0) {
108+
printf("PDM microphone start failed!\n");
109+
while (1) { tight_loop_contents(); }
110+
}
111+
112+
while (1) {
113+
// wait for new samples
114+
while (new_samples_captured == 0) {
115+
tight_loop_contents();
116+
}
117+
new_samples_captured = 0;
118+
119+
dsp_pipeline.shift_spectrogram(scaled_spectrum, SPECTRUM_SHIFT, 124);
120+
121+
// move input buffer values over by INPUT_BUFFER_SIZE samples
122+
memmove(input_q15, &input_q15[INPUT_BUFFER_SIZE], (FFT_SIZE / 2));
123+
124+
// copy new samples to end of the input buffer with a bit shift of INPUT_SHIFT
125+
arm_shift_q15(capture_buffer_q15, INPUT_SHIFT, input_q15 + (FFT_SIZE / 2), INPUT_BUFFER_SIZE);
126+
127+
for (int i = 0; i < SPECTRUM_SHIFT; i++) {
128+
dsp_pipeline.calculate_spectrum(
129+
input_q15 + i * ((FFT_SIZE / 2)),
130+
scaled_spectrum + (129 * (124 - SPECTRUM_SHIFT + i)),
131+
spectogram_divider, spectrogram_zero_point
132+
);
133+
}
134+
135+
float y = ml_model.predict();
136+
137+
printf("y = %f\n", y);
138+
139+
pwm_set_chan_level(pwm_slice_num, pwm_chan_num, y * 255);
140+
}
141+
142+
return 0;
143+
}
144+
145+
void on_pdm_samples_ready()
146+
{
147+
// callback from library when all the samples in the library
148+
// internal sample buffer are ready for reading
149+
150+
// read in the new samples
151+
new_samples_captured = pdm_microphone_read(capture_buffer_q15, INPUT_BUFFER_SIZE);
152+
}

0 commit comments

Comments
 (0)