|
|
@@ -1,12 +1,16 @@
|
|
|
-#include "transform_stabilizer.h"
|
|
|
-
|
|
|
#include "algorithm/measure_window.hpp"
|
|
|
#include "core/basic_obj_types.hpp"
|
|
|
+#include "core/global_defs.h"
|
|
|
+#include "core/tristate_obj.h"
|
|
|
#include "utility/bit_operations.hpp"
|
|
|
+#include "utility/config_utility.hpp"
|
|
|
#include "utility/coro_worker.hpp"
|
|
|
+#include "utility/variable_helper.hpp"
|
|
|
|
|
|
#include <Eigen/Geometry>
|
|
|
|
|
|
+DEFAULT_TRISTATE_OBJ_DEF(transform_stabilizer)
|
|
|
+
|
|
|
namespace sophiar {
|
|
|
|
|
|
using boost::asio::awaitable;
|
|
|
@@ -19,20 +23,22 @@ namespace sophiar {
|
|
|
static constexpr double default_angular_tolerance_deg = 0.05;
|
|
|
|
|
|
bool stable_point_only = true; // 是否仅稳定一个点
|
|
|
- double linear_tolerance = 0.0002; // 0.2 mm
|
|
|
- double angular_tolerance = 0.01; // 0.01 rad
|
|
|
+ double linear_tolerance = 0.02; // 0.2 mm
|
|
|
+ double angular_tolerance = 0.0002; // ≈ 0.01 deg
|
|
|
|
|
|
bool use_temporal_interval = true; // 使用基于时间长度的窗长控制
|
|
|
timestamp_type temporal_interval = 1500000; // 1.5s
|
|
|
- timestamp_type counting_start_ts = 0, last_sample_ts = 0;
|
|
|
size_t counting_interval = 150;
|
|
|
|
|
|
measure_window pos_win[3];
|
|
|
measure_window ang_win[3];
|
|
|
measure_window qua_win[4];
|
|
|
|
|
|
- global_obj_index_type input_obj_index = invalid_global_obj_index;
|
|
|
- global_obj_index_type output_obj_index = invalid_global_obj_index;
|
|
|
+ using dynamic_ts_deque = std::deque<timestamp_type, DYNAMIC_ALLOCATOR(timestamp_type) >;
|
|
|
+ dynamic_ts_deque ts_que;
|
|
|
+
|
|
|
+ variable_index_type input_var_index = invalid_variable_index;
|
|
|
+ variable_index_type output_var_index = invalid_variable_index;
|
|
|
|
|
|
coro_worker::pointer stable_worker;
|
|
|
|
|
|
@@ -43,6 +49,7 @@ namespace sophiar {
|
|
|
|
|
|
void reset_windows() {
|
|
|
ALL_WIN_OP({ win.reset(); })
|
|
|
+ ts_que.clear();
|
|
|
}
|
|
|
|
|
|
size_t get_cur_length() const {
|
|
|
@@ -92,10 +99,10 @@ namespace sophiar {
|
|
|
return std::sqrt(std2);
|
|
|
}
|
|
|
|
|
|
- template<bool UseTemporalInterval>
|
|
|
bool check_interval_condition() const {
|
|
|
- if constexpr (UseTemporalInterval) {
|
|
|
- return last_sample_ts - counting_start_ts >= temporal_interval;
|
|
|
+ if (use_temporal_interval) {
|
|
|
+ if (ts_que.empty()) return false;
|
|
|
+ return ts_que.back() - ts_que.front() >= temporal_interval;
|
|
|
} else {
|
|
|
return get_cur_length() >= counting_interval;
|
|
|
}
|
|
|
@@ -111,21 +118,19 @@ namespace sophiar {
|
|
|
return calc_translation_std() <= linear_tolerance;
|
|
|
}
|
|
|
|
|
|
- template<bool StablePointOnly, bool UseTemporalInterval>
|
|
|
+ template<bool StablePointOnly>
|
|
|
bool check_stable() const {
|
|
|
- if (!check_interval_condition<UseTemporalInterval>()) return false;
|
|
|
- if (!check_translation_condition()) return false;
|
|
|
+ if (!check_interval_condition()) [[likely]] return false;
|
|
|
+ if (!check_translation_condition()) [[likely]] return false;
|
|
|
if constexpr (!StablePointOnly) {
|
|
|
- if (!check_rotation_condition()) return false;
|
|
|
+ if (!check_rotation_condition()) [[likely]] return false;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void update_sample_ts(timestamp_type ts = current_timestamp()) {
|
|
|
- if (get_cur_length() == 0) {
|
|
|
- counting_start_ts = ts;
|
|
|
- }
|
|
|
- last_sample_ts = ts;
|
|
|
+ if (!use_temporal_interval) return;
|
|
|
+ ts_que.push_back(ts);
|
|
|
}
|
|
|
|
|
|
template<typename RotationType>
|
|
|
@@ -159,90 +164,109 @@ namespace sophiar {
|
|
|
on_new_rotation_sample(trans.rotation());
|
|
|
}
|
|
|
|
|
|
+ void remove_old_translation_sample() {
|
|
|
+ while (check_interval_condition()) {
|
|
|
+ for (auto &win: pos_win) {
|
|
|
+ win.pop();
|
|
|
+ }
|
|
|
+ if (use_temporal_interval) {
|
|
|
+ ts_que.pop_front();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void remove_old_transform_sample() {
|
|
|
+ while (check_interval_condition()) {
|
|
|
+ ALL_WIN_OP({ win.pop(); })
|
|
|
+ if (use_temporal_interval) {
|
|
|
+ ts_que.pop_front();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
void on_point_stabled() const {
|
|
|
auto trans_result = get_translation();
|
|
|
auto ret = scalarxyz_obj::new_instance();
|
|
|
ret->value.x() = trans_result.x();
|
|
|
ret->value.y() = trans_result.y();
|
|
|
ret->value.z() = trans_result.z();
|
|
|
- UPDATE_GLOBAL_OBJ(scalarxyz_obj, output_obj_index, ret);
|
|
|
+ UPDATE_VARIABLE(scalarxyz_obj, output_var_index, ret);
|
|
|
}
|
|
|
|
|
|
void on_transform_stabled() const {
|
|
|
auto trans_result = get_transform();
|
|
|
auto ret = transform_obj::new_instance();
|
|
|
ret->value = trans_result;
|
|
|
- UPDATE_GLOBAL_OBJ(transform_obj, output_obj_index, ret);
|
|
|
+ UPDATE_VARIABLE(transform_obj, output_var_index, ret);
|
|
|
}
|
|
|
|
|
|
template<bool StablePointOnly>
|
|
|
- void on_stabled() const {
|
|
|
- assert(is_valid_id(output_obj_index));
|
|
|
+ void on_stabled() {
|
|
|
+ assert(is_valid_id(output_var_index));
|
|
|
if constexpr (StablePointOnly) {
|
|
|
on_point_stabled();
|
|
|
} else {
|
|
|
on_transform_stabled();
|
|
|
}
|
|
|
+ reset_windows();
|
|
|
}
|
|
|
|
|
|
- template<bool StablePointOnly, bool UseTemporalInterval>
|
|
|
- coro_worker::pointer create_worker_impl() {
|
|
|
+ auto create_stable_point_only_worker() {
|
|
|
+ static constexpr bool StablePointOnly = true;
|
|
|
auto func = [this,
|
|
|
- watcher = NEW_GLOBAL_OBJ_WATCHER(input_obj_index)]() mutable
|
|
|
+ point = VARIABLE_MANUAL_DELEGATE(scalarxyz_obj, input_var_index)]() mutable
|
|
|
-> awaitable<bool> {
|
|
|
- co_await watcher.coro_wait(false);
|
|
|
- auto update_ts = GLOBAL_OBJ_UPDATE_TS(input_obj_index);
|
|
|
- if constexpr (StablePointOnly) {
|
|
|
- auto point = GET_GLOBAL_OBJ(scalarxyz_obj, input_obj_index);
|
|
|
- if (point == nullptr) {
|
|
|
- reset_windows();
|
|
|
- } else {
|
|
|
- if constexpr (UseTemporalInterval) {
|
|
|
- update_sample_ts(update_ts);
|
|
|
- }
|
|
|
- on_new_translation_sample(point->value);
|
|
|
- }
|
|
|
+ co_await point.coro_wait_update();
|
|
|
+ auto update_ts = point.get_last_update_ts();
|
|
|
+ if (point.empty()) {
|
|
|
+ reset_windows();
|
|
|
} else {
|
|
|
- auto trans = GET_GLOBAL_OBJ(transform_obj, input_obj_index);
|
|
|
- if (trans == nullptr) {
|
|
|
- reset_windows();
|
|
|
- } else {
|
|
|
- if constexpr (UseTemporalInterval) {
|
|
|
- update_sample_ts(update_ts);
|
|
|
- }
|
|
|
- on_new_transform_sample(trans->value);
|
|
|
- }
|
|
|
+ update_sample_ts(update_ts);
|
|
|
+ on_new_translation_sample(point->value);
|
|
|
}
|
|
|
- if (check_stable<StablePointOnly, UseTemporalInterval>()) [[unlikely]] {
|
|
|
+ if (check_stable<StablePointOnly>()) [[unlikely]] {
|
|
|
on_stabled<StablePointOnly>();
|
|
|
}
|
|
|
+ remove_old_translation_sample();
|
|
|
co_return true;
|
|
|
};
|
|
|
- return make_infinite_coro_worker(get_context(), std::move(func));
|
|
|
+ return make_infinite_coro_worker(std::move(func));
|
|
|
}
|
|
|
|
|
|
- void create_worker() {
|
|
|
- assert(stable_worker == nullptr);
|
|
|
- if (stable_point_only) {
|
|
|
- if (use_temporal_interval) {
|
|
|
- stable_worker = create_worker_impl<true, true>();
|
|
|
+ auto create_stable_transform_worker() {
|
|
|
+ static constexpr bool StablePointOnly = false;
|
|
|
+ auto func = [this,
|
|
|
+ trans = VARIABLE_MANUAL_DELEGATE(transform_obj, input_var_index)]() mutable
|
|
|
+ -> awaitable<bool> {
|
|
|
+ co_await trans.coro_wait_update();
|
|
|
+ auto update_ts = trans.get_last_update_ts();
|
|
|
+ if (trans.empty()) {
|
|
|
+ reset_windows();
|
|
|
+ co_return true;
|
|
|
} else {
|
|
|
- stable_worker = create_worker_impl<true, false>();
|
|
|
+ update_sample_ts(update_ts);
|
|
|
+ on_new_transform_sample(trans->value);
|
|
|
}
|
|
|
- } else {
|
|
|
- if (use_temporal_interval) {
|
|
|
- stable_worker = create_worker_impl<false, true>();
|
|
|
- } else {
|
|
|
- stable_worker = create_worker_impl<false, false>();
|
|
|
+ if (check_stable<StablePointOnly>()) [[unlikely]] {
|
|
|
+ on_stabled<StablePointOnly>();
|
|
|
}
|
|
|
- }
|
|
|
+ remove_old_transform_sample();
|
|
|
+ co_return true;
|
|
|
+ };
|
|
|
+ return make_infinite_coro_worker(std::move(func));
|
|
|
+ }
|
|
|
+
|
|
|
+ void create_worker() {
|
|
|
+ assert(stable_worker == nullptr);
|
|
|
+ stable_worker = stable_point_only ?
|
|
|
+ create_stable_point_only_worker() :
|
|
|
+ create_stable_transform_worker();
|
|
|
stable_worker->run();
|
|
|
}
|
|
|
|
|
|
void load_config(const nlohmann::json &config) {
|
|
|
// stable type
|
|
|
- assert(config.contains("stable_type"));
|
|
|
- assert(config["stable_type"].is_string());
|
|
|
+ ENSURE_STRING("stable_type");
|
|
|
if (config["stable_type"] == "transform") {
|
|
|
stable_point_only = false;
|
|
|
} else {
|
|
|
@@ -251,36 +275,30 @@ namespace sophiar {
|
|
|
}
|
|
|
|
|
|
// I/O
|
|
|
- assert(config.contains("input_obj_name"));
|
|
|
- assert(config.contains("output_obj_name"));
|
|
|
- assert(config["input_obj_name"].is_string());
|
|
|
- assert(config["output_obj_name"].is_string());
|
|
|
- auto input_obj_name = config["input_obj_name"].get<std::string>();
|
|
|
- auto output_obj_name = config["output_obj_name"].get<std::string>();
|
|
|
+ auto input_var_name = LOAD_STRING_ITEM("input_var_name");
|
|
|
+ auto output_var_name = LOAD_STRING_ITEM("output_var_name");
|
|
|
if (stable_point_only) {
|
|
|
- input_obj_index = REGISTER_GLOBAL_OBJ(scalarxyz_obj, input_obj_name);
|
|
|
- output_obj_index = REGISTER_GLOBAL_OBJ(scalarxyz_obj, output_obj_name);
|
|
|
+ input_var_index = REQUIRE_VARIABLE(scalarxyz_obj, input_var_name);
|
|
|
+ output_var_index = REQUIRE_VARIABLE(scalarxyz_obj, output_var_name);
|
|
|
} else {
|
|
|
- input_obj_index = REGISTER_GLOBAL_OBJ(transform_obj, input_obj_name);
|
|
|
- output_obj_index = REGISTER_GLOBAL_OBJ(transform_obj, output_obj_name);
|
|
|
+ input_var_index = REQUIRE_VARIABLE(transform_obj, input_var_name);
|
|
|
+ output_var_index = REQUIRE_VARIABLE(transform_obj, output_var_name);
|
|
|
}
|
|
|
|
|
|
// linear tolerance
|
|
|
double linear_tolerance_mm = default_linear_tolerance_mm;
|
|
|
if (config.contains("linear_tolerance_mm")) {
|
|
|
- assert(config["linear_tolerance_mm"].is_number());
|
|
|
- linear_tolerance_mm = config["linear_tolerance_mm"].get<double>();
|
|
|
+ linear_tolerance_mm = LOAD_FLOAT_ITEM("linear_tolerance_mm");
|
|
|
} else {
|
|
|
// TODO show log, use default value
|
|
|
}
|
|
|
- linear_tolerance = linear_tolerance_mm * 1000.0; // mm -> m
|
|
|
+ linear_tolerance = linear_tolerance_mm;
|
|
|
|
|
|
// angular tolerance
|
|
|
if (!stable_point_only) {
|
|
|
double angular_tolerance_deg = default_angular_tolerance_deg;
|
|
|
if (config.contains("angular_tolerance_deg")) {
|
|
|
- assert(config["angular_tolerance_deg"].is_number());
|
|
|
- angular_tolerance_deg = config["angular_tolerance_deg"].get<double>();
|
|
|
+ angular_tolerance_deg = LOAD_FLOAT_ITEM("angular_tolerance_deg");
|
|
|
} else {
|
|
|
// TODO show log, use default value
|
|
|
}
|
|
|
@@ -289,39 +307,40 @@ namespace sophiar {
|
|
|
|
|
|
// interval
|
|
|
if (config.contains("temporal_interval_s")) {
|
|
|
- assert(config["temporal_interval_s"].is_number());
|
|
|
use_temporal_interval = true;
|
|
|
- auto temporal_interval_s = config["temporal_interval_s"].get<double>();
|
|
|
- temporal_interval = temporal_interval_s * 1000000; // s -> us
|
|
|
+ temporal_interval = LOAD_FLOAT_ITEM("temporal_interval_s") * 1000000;
|
|
|
} else {
|
|
|
- assert(config.contains("counting_interval"));
|
|
|
- assert(config["counting_interval"].is_number_unsigned());
|
|
|
use_temporal_interval = false;
|
|
|
- counting_interval = config["counting_interval"].get<uint64_t>();
|
|
|
+ counting_interval = LOAD_UINT_ITEM("counting_interval");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
- awaitable<bool> transform_stabilizer::on_init(const nlohmann::json &config) {
|
|
|
+ transform_stabilizer::transform_stabilizer()
|
|
|
+ : pimpl(std::make_unique<impl>()) {}
|
|
|
+
|
|
|
+ transform_stabilizer::~transform_stabilizer() = default;
|
|
|
+
|
|
|
+ awaitable<bool> transform_stabilizer::on_init(const nlohmann::json &config) noexcept {
|
|
|
co_return true;
|
|
|
}
|
|
|
|
|
|
- awaitable<bool> transform_stabilizer::on_start(const nlohmann::json &config) {
|
|
|
+ awaitable<bool> transform_stabilizer::on_start(const nlohmann::json &config) noexcept {
|
|
|
pimpl->load_config(config);
|
|
|
pimpl->reset_windows();
|
|
|
pimpl->create_worker();
|
|
|
co_return true;
|
|
|
}
|
|
|
|
|
|
- awaitable<void> transform_stabilizer::on_stop() {
|
|
|
+ awaitable<void> transform_stabilizer::on_stop() noexcept {
|
|
|
pimpl->stable_worker->cancel();
|
|
|
co_await pimpl->stable_worker->coro_wait_stop();
|
|
|
pimpl->stable_worker = nullptr;
|
|
|
co_return;
|
|
|
}
|
|
|
|
|
|
- awaitable<void> transform_stabilizer::on_reset() {
|
|
|
+ awaitable<void> transform_stabilizer::on_reset() noexcept {
|
|
|
co_return;
|
|
|
}
|
|
|
|