ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
streaming_model.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef USE_ESP32
4
6#include "model_data.h"
7
9
10#include <memory>
11#include <tensorflow/lite/core/c/common.h>
12#include <tensorflow/lite/micro/micro_interpreter.h>
13#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
14
16
17static const uint8_t MIN_SLICES_BEFORE_DETECTION = 100;
18static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024;
19
21 std::string *wake_word;
23 bool partially_detection; // Set if the most recent probability exceed the threshold, but the sliding window average
24 // hasn't yet
27 bool blocked_by_vad = false;
28};
29
31 public:
32 // Runtime models are heap owned and destroyed while the device is running, so freeing the arenas cannot
33 // depend on the owner calling unload_model() first. unload_model() is not virtual and is safe to repeat.
34 virtual ~StreamingModel() { this->unload_model(); }
35
36 virtual void log_model_config() = 0;
38
39 // Performs inference on the given features.
40 // - If the model is enabled but not loaded, it will load it
41 // - If the model is disabled but loaded, it will unload it
42 // Returns true if sucessful or false if there is an error
43 bool perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]);
44
47
49 void unload_model();
50
52 virtual void enable() { this->enabled_ = true; }
53
55 virtual void disable() { this->enabled_ = false; }
56
58 bool is_enabled() const { return this->enabled_; }
59
61 bool has_model_data() const { return this->model_start_ != nullptr; }
62
64
65 // Quantized probability cutoffs mapping 0.0 - 1.0 to 0 - 255
67 uint8_t get_probability_cutoff() const { return this->probability_cutoff_; }
68 void set_probability_cutoff(uint8_t probability_cutoff) { this->probability_cutoff_ = probability_cutoff; }
69
70 protected:
73 bool load_model_();
77 size_t probe_arena_size_();
79 bool register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver);
80
81 tflite::MicroMutableOpResolver<20> streaming_op_resolver_;
82
83 bool loaded_{false};
84 bool enabled_{true};
88 int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION};
89
93
94 size_t last_n_index_{0};
96 std::vector<uint8_t> recent_streaming_probabilities_;
97
98 const uint8_t *model_start_{nullptr};
99 uint8_t *tensor_arena_{nullptr};
100 uint8_t *var_arena_{nullptr};
101 std::unique_ptr<tflite::MicroInterpreter> interpreter_;
102 tflite::MicroResourceVariables *mrv_{nullptr};
103 tflite::MicroAllocator *ma_{nullptr};
104};
105
106class WakeWordModel final : public StreamingModel {
107 public:
118 WakeWordModel(const std::string &id, const uint8_t *model_start, uint8_t default_probability_cutoff,
119 size_t sliding_window_average_size, const std::string &wake_word, size_t tensor_arena_size,
120 bool default_enabled, bool internal_only);
121
131 WakeWordModel(const std::string &id, std::shared_ptr<ModelData> model_data, uint8_t default_probability_cutoff,
132 size_t sliding_window_average_size, const std::string &wake_word,
133 std::vector<std::string> trained_languages, size_t tensor_arena_size);
134
135 // model_data_ is a member of this class, so it is destroyed before ~StreamingModel() runs. Unload here, while
136 // the buffer is still alive, so the interpreter is never torn down over freed model data.
137 ~WakeWordModel() override { this->unload_model(); }
138
139 void log_model_config() override;
140
145
146 const std::string &get_id() const { return this->id_; }
147 const std::string &get_wake_word() const { return this->wake_word_; }
148
149 void add_trained_language(const std::string &language) { this->trained_languages_.push_back(language); }
150 const std::vector<std::string> &get_trained_languages() const { return this->trained_languages_; }
151
153 void enable() override;
154
156 void disable() override;
157
158 bool get_internal_only() { return this->internal_only_; }
159
160 protected:
161 // Kept for runtime-downloaded models so the model buffer stays alive for the model's lifetime.
162 // Null for compiled-in models (their data lives in flash).
163 std::shared_ptr<ModelData> model_data_;
164
165 std::string id_;
166 std::string wake_word_;
167 std::vector<std::string> trained_languages_;
168
170
172};
173
174class VADModel final : public StreamingModel {
175 public:
176 VADModel(const uint8_t *model_start, uint8_t default_probability_cutoff, size_t sliding_window_size,
177 size_t tensor_arena_size);
178
179 void log_model_config() override;
180
185};
186
187} // namespace esphome::micro_wake_word
188
189#endif
virtual void disable()
Disable the model. The next performing_streaming_inference call will unload it.
virtual DetectionEvent determine_detected()=0
bool load_model_()
Allocates tensor and variable arenas and sets up the model interpreter.
virtual void enable()
Enable the model. The next performing_streaming_inference call will load it.
bool has_model_data() const
Return true if the model has usable data. A model without it can never be loaded or run.
std::unique_ptr< tflite::MicroInterpreter > interpreter_
tflite::MicroMutableOpResolver< 20 > streaming_op_resolver_
bool register_streaming_ops_(tflite::MicroMutableOpResolver< 20 > &op_resolver)
Returns true if successfully registered the streaming model's TensorFlow operations.
void reset_probabilities()
Sets all recent_streaming_probabilities to 0 and resets the ignore window count.
std::vector< uint8_t > recent_streaming_probabilities_
size_t probe_arena_size_()
Probes the actual required tensor arena size by trial allocation.
tflite::MicroResourceVariables * mrv_
bool perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE])
void unload_model()
Destroys the TFLite interpreter and frees the tensor and variable arenas' memory.
void set_probability_cutoff(uint8_t probability_cutoff)
bool is_enabled() const
Return true if the model is enabled.
DetectionEvent determine_detected() override
Checks for voice activity by comparing the max probability in the sliding window with the probability...
VADModel(const uint8_t *model_start, uint8_t default_probability_cutoff, size_t sliding_window_size, size_t tensor_arena_size)
void enable() override
Enable the model and save to flash. The next performing_streaming_inference call will load it.
const std::string & get_wake_word() const
DetectionEvent determine_detected() override
Checks for the wake word by comparing the mean probability in the sliding window with the probability...
std::shared_ptr< ModelData > model_data_
const std::vector< std::string > & get_trained_languages() const
WakeWordModel(const std::string &id, const uint8_t *model_start, uint8_t default_probability_cutoff, size_t sliding_window_average_size, const std::string &wake_word, size_t tensor_arena_size, bool default_enabled, bool internal_only)
Constructs a wake word model object with compile-time model data.
void add_trained_language(const std::string &language)
void disable() override
Disable the model and save to flash. The next performing_streaming_inference call will unload it.
std::vector< std::string > trained_languages_
static void uint32_t