ESPHome  2023.11.6
kalman_combinator.cpp
Go to the documentation of this file.
1 #include "kalman_combinator.h"
2 #include "esphome/core/hal.h"
3 #include <cmath>
4 #include <functional>
5 
6 namespace esphome {
7 namespace kalman_combinator {
8 
10  ESP_LOGCONFIG("kalman_combinator", "Kalman Combinator:");
11  ESP_LOGCONFIG("kalman_combinator", " Update variance: %f per ms", this->update_variance_value_);
12  ESP_LOGCONFIG("kalman_combinator", " Sensors:");
13  for (const auto &sensor : this->sensors_) {
14  auto &entity = *sensor.first;
15  ESP_LOGCONFIG("kalman_combinator", " - %s", entity.get_name().c_str());
16  }
17 }
18 
20  for (const auto &sensor : this->sensors_) {
21  const auto stddev = sensor.second;
22  sensor.first->add_on_state_callback([this, stddev](float x) -> void { this->correct_(x, stddev(x)); });
23  }
24 }
25 
26 void KalmanCombinatorComponent::add_source(Sensor *sensor, std::function<float(float)> const &stddev) {
27  this->sensors_.emplace_back(sensor, stddev);
28 }
29 
30 void KalmanCombinatorComponent::add_source(Sensor *sensor, float stddev) {
31  this->add_source(sensor, std::function<float(float)>{[stddev](float x) -> float { return stddev; }});
32 }
33 
34 void KalmanCombinatorComponent::update_variance_() {
35  uint32_t now = millis();
36 
37  // Variance increases by update_variance_ each millisecond
38  auto dt = now - this->last_update_;
39  auto dv = this->update_variance_value_ * dt;
40  this->variance_ += dv;
41  this->last_update_ = now;
42 }
43 
44 void KalmanCombinatorComponent::correct_(float value, float stddev) {
45  if (std::isnan(value) || std::isinf(stddev)) {
46  return;
47  }
48 
49  if (std::isnan(this->state_) || std::isinf(this->variance_)) {
50  this->state_ = value;
51  this->variance_ = stddev * stddev;
52  if (this->std_dev_sensor_ != nullptr) {
53  this->std_dev_sensor_->publish_state(stddev);
54  }
55  return;
56  }
57 
58  this->update_variance_();
59 
60  // Combine two gaussian distributions mu1+-var1, mu2+-var2 to a new one around mu
61  // Use the value with the smaller variance as mu1 to prevent precision errors
62  const bool this_first = this->variance_ < (stddev * stddev);
63  const float mu1 = this_first ? this->state_ : value;
64  const float mu2 = this_first ? value : this->state_;
65 
66  const float var1 = this_first ? this->variance_ : stddev * stddev;
67  const float var2 = this_first ? stddev * stddev : this->variance_;
68 
69  const float mu = mu1 + var1 * (mu2 - mu1) / (var1 + var2);
70  const float var = var1 - (var1 * var1) / (var1 + var2);
71 
72  // Update and publish state
73  this->state_ = mu;
74  this->variance_ = var;
75 
76  this->publish_state(mu);
77  if (this->std_dev_sensor_ != nullptr) {
78  this->std_dev_sensor_->publish_state(std::sqrt(var));
79  }
80 }
81 } // namespace kalman_combinator
82 } // namespace esphome
uint16_t x
Definition: tt21100.cpp:17
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
void add_source(Sensor *sensor, std::function< float(float)> const &stddev)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7