You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Weight/weigh/WeightStabilityDetector.cxx

437 lines
15 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "WeightStabilityDetector.hpp"
WeightStabilityDetector::WeightStabilityDetector(const Config& config) :
config_(config),
current_state_(VehicleState::NO_VEHICLE),
current_weight_(0.0f),
previous_weight_(0.0f),
stable_weight_(0.0f),
max_weight_(0.0f),
current_window_size_(static_cast<size_t>(config.window_size)),
vehicle_window_size_(static_cast<size_t>(config.window_size)),
stable_count_(0),
continuous_increase_count_(0),
total_samples_(0),
state_callback_(nullptr),
weight_callback_(nullptr),
state_callback_data_(nullptr),
weight_callback_data_(nullptr),
debug_output_(nullptr),
is_initialized_(false),
has_vehicle_on_(false),
is_leaving_state_(false) {
reset(false); // 初始化时不通知回调
// 默认调试输出到标准输出
debug_output_ = [](const std::string& msg) {
std::cout << "[WeightDetector] " << msg << std::endl;
};
}
WeightStabilityDetector::~WeightStabilityDetector() {
// 清理资源
}
void WeightStabilityDetector::reset(bool notify_callback) {
VehicleState old_state = current_state_;
current_state_ = VehicleState::NO_VEHICLE;
weight_window_.clear();
vehicle_weight_window_.clear();
weight_history_.clear();
current_weight_ = 0.0f;
previous_weight_ = 0.0f;
stable_weight_ = 0.0f;
max_weight_ = 0.0f;
stable_count_ = 0;
continuous_increase_count_ = 0;
total_samples_ = 0;
stats_ = Statistics{};
has_vehicle_on_ = false;
is_leaving_state_ = false;
last_state_change_time_ = std::chrono::steady_clock::now();
last_weight_time_ = std::chrono::steady_clock::now();
is_initialized_ = true;
// 通知状态变化
if (notify_callback && state_callback_ && old_state != current_state_) {
state_callback_(current_state_, 0.0f, state_callback_data_);
}
debugLog("Detector reset");
}
void WeightStabilityDetector::setConfig(const Config& config) {
config_ = config;
current_window_size_ = static_cast<size_t>(config.window_size);
vehicle_window_size_ = static_cast<size_t>(config.window_size);
// 如果窗口大小改变,需要调整窗口
if (weight_window_.size() > current_window_size_) {
while (weight_window_.size() > current_window_size_) {
weight_window_.pop_front();
}
}
if (vehicle_weight_window_.size() > vehicle_window_size_) {
while (vehicle_weight_window_.size() > vehicle_window_size_) {
vehicle_weight_window_.pop_front();
}
}
// 重新计算统计信息
calculateVehicleStatistics();
}
bool WeightStabilityDetector::processWeight(float weight_kg) {
return processWeightData(WeightData(weight_kg));
}
bool WeightStabilityDetector::processWeightData(const WeightData& data) {
if (!data.is_valid) {
debugLog("Invalid weight data received");
return false;
}
// 保存旧状态用于比较
VehicleState old_state = current_state_;
// 更新时间戳
last_weight_time_ = data.timestamp;
// 更新重量值
previous_weight_ = current_weight_;
current_weight_ = data.value_kg;
// 记录最大重量(非离开状态时)
if (!is_leaving_state_ && current_weight_ > max_weight_) {
max_weight_ = current_weight_;
}
// 添加到历史记录
addWeightToHistory(current_weight_);
// 更新全局滑动窗口
updateWindow(current_weight_);
// 如果不是离开状态,检查车辆状态
if (!is_leaving_state_) {
if (isVehicleOn(current_weight_) && !has_vehicle_on_) {
has_vehicle_on_ = true;
if (config_.reset_window_on_vehicle_on) {
vehicle_weight_window_.clear(); // 车辆上磅时清空车辆重量窗口
max_weight_ = current_weight_; // 重置最大重量
}
debugLog("Vehicle on scale detected, clearing vehicle weight window");
}
// 如果有车在磅上,更新车辆重量窗口
if (has_vehicle_on_ && current_weight_ > config_.empty_car_threshold) {
updateVehicleWindow(current_weight_);
} else if (current_weight_ < config_.empty_car_threshold) {
// 如果重量低于空车阈值,重置车辆状态
has_vehicle_on_ = false;
vehicle_weight_window_.clear();
}
}
// 计算统计信息(基于车辆重量窗口)
// if (!vehicle_weight_window_.empty()) {
calculateVehicleStatistics();
// }
// 检查状态转换
checkStateTransition(current_weight_);
// 触发回调
bool state_changed = (old_state != current_state_);
if (state_callback_ && state_changed) {
state_callback_(current_state_, current_weight_, state_callback_data_);
}
if (weight_callback_) {
weight_callback_(current_weight_, isStable(), weight_callback_data_);
}
// 调试输出
if (config_.enable_debug_log) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(2)
<< "Weight: " << current_weight_ << "kg, "
<< "State: " << static_cast<int>(current_state_) << ", "
<< "StableCount: " << stable_count_ << ", "
<< "VehicleWindow: " << vehicle_weight_window_.size() << ", "
<< "StdDev: " << stats_.std_dev << "kg, "
<< "MaxWeight: " << max_weight_ << "kg";
debugLog(oss.str());
}
return true;
}
void WeightStabilityDetector::updateWindow(float weight_kg) {
weight_window_.push_back(weight_kg);
// 保持窗口大小
if (weight_window_.size() > current_window_size_) {
weight_window_.pop_front();
}
}
void WeightStabilityDetector::updateVehicleWindow(float weight_kg) {
vehicle_weight_window_.push_back(weight_kg);
// 保持窗口大小
if (vehicle_weight_window_.size() > vehicle_window_size_) {
vehicle_weight_window_.pop_front();
}
}
void WeightStabilityDetector::calculateVehicleStatistics() {
// 使用车辆重量窗口计算统计信息
if (vehicle_weight_window_.empty()) {
stats_ = Statistics{};
return;
}
// 计算基本统计量
size_t n = vehicle_weight_window_.size();
// 计算最小值和最大值
stats_.min = *std::min_element(vehicle_weight_window_.begin(), vehicle_weight_window_.end());
stats_.max = *std::max_element(vehicle_weight_window_.begin(), vehicle_weight_window_.end());
// 计算平均值
float sum = std::accumulate(vehicle_weight_window_.begin(), vehicle_weight_window_.end(), 0.0f);
stats_.mean = sum / static_cast<float>(n);
// 计算方差和标准差
float variance_sum = 0.0f;
for (float w : vehicle_weight_window_) {
float diff = w - stats_.mean;
variance_sum += diff * diff;
}
stats_.variance = variance_sum / static_cast<float>(n);
stats_.std_dev = std::sqrt(stats_.variance);
// 更新计数器
stats_.sample_count = static_cast<int>(n);
stats_.stable_count = stable_count_;
stats_.increase_count = continuous_increase_count_;
}
void WeightStabilityDetector::checkStateTransition(float weight_kg) {
switch (current_state_) {
case VehicleState::NO_VEHICLE:
if (isVehicleOn(weight_kg)) {
transitionToState(VehicleState::VEHICLE_ON);
}
break;
case VehicleState::VEHICLE_ON: {
// 检查重量抖动
float weight_diff = std::abs(weight_kg - previous_weight_);
if (isJitterExceeded(weight_diff)) {
// 抖动过大,重置计数
stable_count_ = 0;
continuous_increase_count_ = 0;
debugLog("Jitter exceeded, resetting stability counters");
} else if (weight_kg > config_.min_weight_threshold) {
// 增加稳定计数
stable_count_++;
// 检查是否连续增长
if (weight_kg > previous_weight_) {
continuous_increase_count_++;
} else {
continuous_increase_count_ = 0;
}
// 检查是否达到稳定条件
if (checkStableCondition()) {
transitionToState(VehicleState::VEHICLE_STABLE);
}
}
// 检查是否离开(包括快速下降)
if (isVehicleLeaving(weight_kg) || checkFastDrop(weight_kg)) {
transitionToState(VehicleState::VEHICLE_LEAVING);
}
break;
}
case VehicleState::VEHICLE_STABLE: {
// 稳定状态下继续监测是否离开
if (isVehicleLeaving(weight_kg) || checkFastDrop(weight_kg)) {
transitionToState(VehicleState::VEHICLE_LEAVING);
}
else if (isVehicleShaking(weight_kg)) {
transitionToState(VehicleState::VEHICLE_ON);
}
break;
}
case VehicleState::VEHICLE_LEAVING: {
// 离开状态持续一段时间后自动回到无车状态
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_state_change_time_).count();
if (elapsed > config_.leave_reset_delay_ms) {
// 重置但不通知回调由processWeight统一处理
reset(false);
// 状态已改变需要在processWeight中触发回调
// 这里不需要额外处理因为reset(false)不会触发回调
// 状态变化会在processWeight返回前被检测到并触发回调
}
break;
}
default:
// 处理未知状态
break;
}
}
void WeightStabilityDetector::transitionToState(VehicleState new_state) {
current_state_ = new_state;
last_state_change_time_ = std::chrono::steady_clock::now();
// 状态特定处理
switch (new_state) {
case VehicleState::VEHICLE_STABLE:
stable_weight_ = stats_.mean; // 使用均值作为稳定重量
debugLog("Vehicle stable, weight: " + std::to_string(stable_weight_) + "kg");
break;
case VehicleState::VEHICLE_LEAVING:
vehicle_weight_window_.clear(); // 清空车辆重量窗口
is_leaving_state_ = true; // 标记为离开状态
debugLog("Vehicle leaving detected");
break;
case VehicleState::NO_VEHICLE:
is_leaving_state_ = false; // 清除离开状态标记
debugLog("No vehicle on scale");
break;
case VehicleState::VEHICLE_ON:
is_leaving_state_ = false; // 清除离开状态标记
debugLog("Vehicle detected on scale");
break;
default:
break;
}
}
bool WeightStabilityDetector::isJitterExceeded(float weight_diff) const {
return weight_diff > config_.jitter_threshold;
}
bool WeightStabilityDetector::isVehicleOn(float weight_kg) const {
return weight_kg > config_.min_weight_threshold;
}
bool WeightStabilityDetector::isVehicleLeaving(float weight_kg) const {
return weight_kg < config_.empty_car_threshold;
}
bool WeightStabilityDetector::isVehicleShaking(float weight_kg) {
if (std::abs(weight_kg - previous_weight_) > config_.jitter_threshold)
{
is_leaving_state_ = false; // 清除离开状态标记
stable_count_ = 0; // 重置稳定计数
continuous_increase_count_ = 0; // 重置连续增长计数
vehicle_weight_window_.clear(); // 重置重量窗口
return true;
}
return false;
}
bool WeightStabilityDetector::checkFastDrop(float weight_kg) const {
// 检查快速下降:当前重量远低于历史最高重量
if (max_weight_ > config_.min_weight_threshold) {
float drop_amount = max_weight_ - weight_kg;
if (drop_amount > config_.fast_drop_threshold) {
if (config_.enable_debug_log && debug_output_) {
std::ostringstream oss;
oss << "Fast drop detected: max=" << max_weight_
<< "kg, current=" << weight_kg << "kg, drop=" << drop_amount << "kg";
debug_output_(oss.str());
}
return true;
}
}
return false;
}
bool WeightStabilityDetector::checkStableCondition() const {
// 条件1稳定次数达到要求
if (stable_count_ < config_.required_stable_count) {
return false;
}
// 条件2车辆重量窗口至少有3个样本
if (vehicle_weight_window_.size() < 3) {
return false;
}
// 条件3基于车辆重量的稳定性判断
// 计算极差(最大值-最小值)
float weight_range = stats_.max - stats_.min;
// 条件3.1:标准差小于阈值
bool std_dev_stable = (stats_.std_dev < config_.max_std_dev);
// 条件3.2极差小于2倍抖动阈值
bool range_stable = (weight_range < (2.0f * config_.jitter_threshold));
// 条件4抖动次数要求
int non_increase_count = stable_count_ - continuous_increase_count_;
bool jitter_met = (non_increase_count >= config_.min_jitter_count);
// 条件5超时稳定
bool timeout_stable = (stable_count_ >= (config_.required_stable_count + config_.timeout_extra));
// 综合判断:统计稳定且(满足抖动要求或超时)
bool is_stable = (std_dev_stable || range_stable) && (jitter_met || timeout_stable);
if (config_.enable_debug_log && debug_output_) {
std::ostringstream oss;
oss << "Stable Check: std_dev=" << stats_.std_dev
<< ", range=" << weight_range
<< ", non_increase=" << non_increase_count
<< ", stable_count=" << stable_count_
<< ", vehicle_window=" << vehicle_weight_window_.size()
<< ", result=" << (is_stable ? "STABLE" : "NOT_STABLE");
debug_output_(oss.str());
}
return is_stable;
}
void WeightStabilityDetector::addWeightToHistory(float weight_kg) {
weight_history_.push_back(weight_kg);
total_samples_++;
// 限制历史记录大小
if (weight_history_.size() > MAX_HISTORY_SIZE) {
weight_history_.erase(weight_history_.begin());
}
}
void WeightStabilityDetector::debugLog(const std::string& message) const {
if (config_.enable_debug_log && debug_output_) {
debug_output_(message);
}
}