Ver código fonte

实现并测试了 transform utility

jcsyshc 2 anos atrás
pai
commit
892e9ff94f

+ 6 - 24
src/algorithm/transform_tree.cpp

@@ -1,10 +1,9 @@
-#include "transform_tree.h"
-
 #include "core/basic_obj_types.hpp"
 #include "core/global_defs.h"
 #include "utility/bit_operations.hpp"
 #include "utility/config_utility.hpp"
 #include "utility/coro_worker.hpp"
+#include "utility/coro_worker_helper_func.hpp"
 #include "utility/coro_signal_group.hpp"
 #include "utility/named_vector.hpp"
 
@@ -21,6 +20,8 @@
 
 #include "utility/assert_utility.h"
 
+DEFAULT_TRISTATE_OBJ_DEF(transform_tree)
+
 namespace sophiar {
 
     using namespace Eigen;
@@ -47,18 +48,6 @@ namespace sophiar {
         named_vector<node_index_type, node_type> nodes;
         std::vector<coro_worker::pointer> worker_list;
 
-        static trans_type transform_from_json_array(const nlohmann::json &arr) {
-            assert(arr.is_array());
-            assert(arr.size() == 7);
-            double val[7];
-            for (size_t i = 0; i < 7; ++i) {
-                assert(arr[i].is_number());
-                val[i] = arr[i].get<double>();
-            }
-            return Translation3d(val[0], val[1], val[2]) *
-                   Quaterniond(val[3], val[4], val[5], val[6]);
-        }
-
         std::optional<trans_type> get_transform_from_path(const std::list<node_index_type> &path) const {
             auto ret = trans_type::Identity();
             for (auto index: path) {
@@ -111,16 +100,9 @@ namespace sophiar {
 
             signal_group->start();
             auto signal_watcher = signal_group->new_watcher();
-            auto exit_func = [signal_group = std::move(signal_group)]() mutable {
-                co_spawn(*global_context, [
-                        signal_group = std::move(signal_group)]() mutable
-                        -> boost::asio::awaitable<void> {
-                    co_await signal_group->stop();
-                    co_return;
-                }, detached);
-            };
+            auto exit_func = SIGNAL_GROUP_AUTO_CLOSER(signal_group);
 
-            auto worker_func = [=,
+            auto worker_func = [=, this,
                     scheme = std::move(scheme),
                     signal_watcher = std::move(signal_watcher)]() mutable
                     -> boost::asio::awaitable<bool> {
@@ -168,7 +150,7 @@ namespace sophiar {
 
                 trans_type default_trans = trans_type::Identity();
                 if (node.contains("transform")) {
-                    default_trans = transform_from_json_array(node["transform"]);
+                    default_trans = transform_obj::read_value_from_json_array(node["transform"]);
                 } else if (is_valid_id(parent_index)) {
                     assert(is_valid_id(transform_var_index));
                 }

+ 0 - 8
src/algorithm/transform_tree.h

@@ -1,8 +0,0 @@
-#ifndef SOPHIAR2_TRANSFORM_TREE_H
-#define SOPHIAR2_TRANSFORM_TREE_H
-
-#include "core/tristate_obj.h"
-
-DEFAULT_TRISTATE_OBJ_DEF(transform_tree)
-
-#endif //SOPHIAR2_TRANSFORM_TREE_H

+ 125 - 79
src/algorithm/transform_utility.hpp

@@ -2,6 +2,7 @@
 #define SOPHIAR2_TRANSFORM_UTILITY_HPP
 
 #include "core/basic_obj_types.hpp"
+#include "utility/config_utility.hpp"
 #include "utility/coro_signal_group.hpp"
 #include "utility/coro_worker.hpp"
 #include "utility/simple_tristate_obj.hpp"
@@ -12,100 +13,145 @@
 namespace sophiar {
 
     inline coro_worker::pointer make_transform_inverter_func(const nlohmann::json &config) {
-        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_obj_index = REGISTER_GLOBAL_OBJ(transform_obj, input_obj_name);
-        auto output_obj_index = REGISTER_GLOBAL_OBJ(transform_obj, output_obj_name);
-        auto worker = make_infinite_coro_worker(global_context, [=,
-                input_helper = GLOBAL_OBJ_AUTO_DELEGATE(transform_obj, input_obj_index)]() mutable
-                -> boost::asio::awaitable<bool> {
-            co_await input_helper.coro_wait_update(false);
-            auto ts = GLOBAL_OBJ_UPDATE_TS(input_obj_index);
-            if (input_helper.empty()) [[unlikely]] {
-                UPDATE_GLOBAL_OBJ_TS(transform_obj, output_obj_index, nullptr, ts);
-            } else [[likely]] {
-                auto new_trans = transform_obj::new_instance();
-                new_trans->value = input_helper->value.inverse();
-                UPDATE_GLOBAL_OBJ_TS(transform_obj, output_obj_index, new_trans, ts);
-            }
-            co_return true;
-        });
+        auto input_var_index = LOAD_VARIABLE_INDEX(transform_obj, "input_var_name");
+        auto output_var_index = LOAD_VARIABLE_INDEX(transform_obj, "output_var_name");
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        input_helper = VARIABLE_MANUAL_DELEGATE(transform_obj, input_var_index)]() mutable
+                        -> boost::asio::awaitable<bool> {
+                    co_await input_helper.coro_wait_update();
+                    auto ts = input_helper.get_last_update_ts();
+                    if (input_helper.empty()) [[unlikely]] {
+                        UPDATE_VARIABLE_WITH_TS(transform_obj, output_var_index, nullptr, ts);
+                    } else [[likely]] {
+                        UPDATE_VARIABLE_VAL_WITH_TS(transform_obj, output_var_index, input_helper->value.inverse(), ts);
+                    }
+                    co_return true;
+                });
         return std::move(worker);
     }
 
-    scalarxyz_obj::value_type get_scalarxyz_from_json_list(const nlohmann::json &config) {
-        assert(config.is_array());
-        assert(config.size() == 3);
-        double val[3];
-        for (int i = 0; i < 3; ++i) {
-            assert(config[i].is_number());
-            val[i] = config[i].get<double>();
+    inline bool determine_is_transform_vector(const nlohmann::json &config) {
+        assert(config.contains("transform_type"));
+        if (config["transform_type"] == "point") {
+            return false;
+        } else if (config["transform_type"] == "vector") {
+            return true;
         }
-        return {val[0], val[1], val[2]};
+        assert(false);
+        return false;
     }
 
-    template<bool IsVector>
-    inline coro_worker::pointer make_fixed_scalarxyz_transformer(const nlohmann::json &config) {
-        assert(config["transform_type"] == "point");
-        auto fixed_value = get_scalarxyz_from_json_list(config["fixed_value"]);
-        auto trans_obj_name = config["transform_obj_name"].get<std::string>();
-        auto output_obj_name = config["output_obj_name"].get<std::string>();
-        auto trans_obj_index = REGISTER_GLOBAL_OBJ(transform_obj, trans_obj_name);
-        auto output_obj_index = REGISTER_GLOBAL_OBJ(scalarxyz_obj, output_obj_name);
-        auto worker = make_infinite_coro_worker(global_context, [=,
-                trans_helper = GLOBAL_OBJ_AUTO_DELEGATE(transform_obj, trans_obj_index)]() mutable
-                -> boost::asio::awaitable<bool> {
-            co_await trans_helper.coro_wait_update(false);
-            auto ts = GLOBAL_OBJ_UPDATE_TS(trans_obj_index);
-            if (trans_helper.empty()) [[unlikely]] {
-                UPDATE_GLOBAL_OBJ_TS(scalarxyz_obj, output_obj_index, nullptr, ts);
-            } else [[likely]] {
-                auto new_value = scalarxyz_obj::new_instance();
-                if constexpr (IsVector) {
-                    new_value->value = trans_helper->value.linear() * fixed_value;
-                } else {
-                    new_value->value = trans_helper->value * fixed_value;
-                }
-                UPDATE_GLOBAL_OBJ_TS(scalarxyz_obj, output_obj_index, new_value, ts);
-            }
-            co_return true;
-        });
+    // 变换矩阵变换, 变换目标不动
+    inline coro_worker::pointer make_target_fixed_scalarxyz_transformer(const nlohmann::json &config) {
+        auto is_vector = determine_is_transform_vector(config);
+        auto fixed_value = scalarxyz_obj::read_value_from_json_array(config["target_value"]);
+        auto trans_var_index = LOAD_VARIABLE_INDEX(transform_obj, "transform_var_name");
+        auto output_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "output_var_name");
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        trans_helper = VARIABLE_MANUAL_DELEGATE(transform_obj, trans_var_index)]() mutable
+                        -> boost::asio::awaitable<bool> {
+                    co_await trans_helper.coro_wait_update();
+                    auto ts = trans_helper.get_last_update_ts();
+                    if (trans_helper.empty()) [[unlikely]] {
+                        UPDATE_VARIABLE_WITH_TS(scalarxyz_obj, output_var_index, nullptr, ts);
+                    } else [[likely]] {
+                        UPDATE_VARIABLE_VAL_WITH_TS(scalarxyz_obj, output_var_index,
+                                                    is_vector ?
+                                                    (trans_helper->value.linear() * fixed_value) :
+                                                    (trans_helper->value * fixed_value),
+                                                    ts);
+                    }
+                    co_return true;
+                });
+        return std::move(worker);
+    }
+
+    // 变换矩阵不变, 变换目标移动
+    inline coro_worker::pointer make_transform_fixed_scalarxyz_transformer(const nlohmann::json &config) {
+        auto is_vector = determine_is_transform_vector(config);
+        auto fixed_trans = transform_obj::read_value_from_json_array(config["transform_value"]);
+        auto input_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "input_var_name");
+        auto output_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "output_var_name");
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        xyz_helper = VARIABLE_MANUAL_DELEGATE(scalarxyz_obj, input_var_index)]() mutable
+                        -> boost::asio::awaitable<bool> {
+                    co_await xyz_helper.coro_wait_update();
+                    auto ts = xyz_helper.get_last_update_ts();
+                    if (xyz_helper.empty()) [[unlikely]] {
+                        UPDATE_VARIABLE_WITH_TS(scalarxyz_obj, output_var_index, nullptr, ts);
+                    } else [[likely]] {
+                        UPDATE_VARIABLE_VAL_WITH_TS(scalarxyz_obj, output_var_index,
+                                                    is_vector ?
+                                                    (fixed_trans.linear() * xyz_helper->value) :
+                                                    (fixed_trans * xyz_helper->value),
+                                                    ts);
+                    }
+                    co_return true;
+                });
+        return std::move(worker);
+    }
+
+    // 变换矩阵变换, 变换目标移动
+    template<bool IsSynced>
+    inline auto make_dynamic_scalarxyz_transformer(const nlohmann::json &config) {
+        auto is_vector = determine_is_transform_vector(config);
+        auto trans_var_index = LOAD_VARIABLE_INDEX(transform_obj, "transform_var_name");
+        auto input_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "input_var_name");
+        auto output_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "output_var_name");
+
+        static constexpr auto cond_func = [](auto &mask) { return IsSynced ? mask.all() : mask.any(); };
+        auto signal_group = coro_signal_group<cond_func>::new_instance();
+        signal_group->add_watcher(REQUIRE_VARIABLE_WATCHER(trans_var_index));
+        signal_group->add_watcher(REQUIRE_VARIABLE_WATCHER(input_var_index));
+        auto signal_watcher = signal_group->new_watcher();
+        signal_group->start();
+        auto exit_func = SIGNAL_GROUP_AUTO_CLOSER(signal_group);
+
+        auto worker = make_infinite_coro_worker(
+                [=,
+                        watcher = std::move(signal_watcher)]() mutable
+                        -> boost::asio::awaitable<bool> {
+                    co_await watcher.coro_wait(false);
+                    auto ts = watcher.get_last_update_ts();
+                    auto trans = QUERY_VARIABLE(transform_obj, trans_var_index);
+                    auto xyz = QUERY_VARIABLE(scalarxyz_obj, input_var_index);
+                    if (trans == nullptr || xyz == nullptr) [[unlikely]] {
+                        UPDATE_VARIABLE_WITH_TS(scalarxyz_obj, output_var_index, nullptr, ts);
+                    } else [[likely]] {
+                        UPDATE_VARIABLE_VAL_WITH_TS(scalarxyz_obj, output_var_index,
+                                                    is_vector ?
+                                                    (trans->value.linear() * xyz->value) :
+                                                    (trans->value * xyz->value),
+                                                    ts);
+                    }
+                    co_return true;
+                }, std::move(exit_func));
+
         return std::move(worker);
     }
 
     inline coro_worker::pointer make_scalarxyz_transformer(const nlohmann::json &config) {
-        assert(config.contains("transform_obj_name"));
-        assert(config.contains("output_obj_name"));
-        assert(config["transform_obj_name"].is_string());
-        assert(config["output_obj_name"].is_string());
-        assert(config.contains("transform_type"));
-        assert(config["transform_type"].is_string());
-        auto trans_type = config["transform_type"].get<std::string>();
-        if (config.contains("fixed_value")) {
-            if (trans_type == "vector") {
-                return make_fixed_scalarxyz_transformer<true>(config);
-            } else {
-                assert(trans_type == "point");
-                return make_fixed_scalarxyz_transformer<false>(config);
-            }
-        } else {
-            assert(config.contains("input_obj_name"));
-            assert(config["input_obj_name"].is_string());
-            // TODO 实现 dynamic_scalaxyz_transformer
-            assert(false);
-            return nullptr;
+        auto is_transform_dynamic = config.contains("transform_var_name");
+        auto is_target_dynamic = config.contains("input_var_name");
+        if (is_transform_dynamic && is_target_dynamic) { // dynamic
+            auto is_synced = TRY_LOAD_BOOL_ITEM("synced", false);
+            return is_synced ?
+                   make_dynamic_scalarxyz_transformer<true>(config) :
+                   make_dynamic_scalarxyz_transformer<false>(config);
+        } else if (!is_transform_dynamic) { // transform fixed
+            assert(is_target_dynamic);
+            return make_transform_fixed_scalarxyz_transformer(config);
+        } else {                            // target fixed
+            assert(is_transform_dynamic);
+            return make_target_fixed_scalarxyz_transformer(config);
         }
         assert(false);
         return nullptr;
     }
 
-    using transform_inverter = simple_tristate_obj_wrapper<make_transform_inverter_func>;
-    using scalarxyz_transformer = simple_tristate_obj_wrapper<make_scalarxyz_transformer>;
-
 }
 
 #endif //SOPHIAR2_TRANSFORM_UTILITY_HPP

+ 42 - 1
src/core/basic_obj_types.hpp

@@ -5,8 +5,12 @@
 
 #include <Eigen/Geometry>
 
+#include <nlohmann/json.hpp>
+
 #include <array>
 
+#include "utility/assert_utility.h"
+
 namespace sophiar {
 
     template<typename T>
@@ -41,6 +45,14 @@ namespace sophiar {
             writer << value;
         }
 
+        void fill_from_json_array(const nlohmann::json &config);
+
+        static value_type read_value_from_json_array(const nlohmann::json &config) {
+            auto tmp_obj = this_type{};
+            tmp_obj.fill_from_json_array(config);
+            return std::move(tmp_obj.value);
+        }
+
         template<typename WriterType>
         static void raw_pointer_write_to(WriterType &writer, void *raw_ptr) {
             auto &real_ptr = *static_cast<typename this_type::pointer *>(raw_ptr);
@@ -93,7 +105,7 @@ namespace sophiar {
     inline void transform_obj::fill_from(ReaderType &reader) {
         double tx, ty, tz, qw, qx, qy, qz;
         reader >> tx >> ty >> tz >> qw >> qx >> qy >> qz;
-        value = Eigen::Quaterniond(qw, qx, qy, qz) * Eigen::Translation3d(tx, ty, tz);
+        value = Eigen::Translation3d(tx, ty, tz) * Eigen::Quaterniond(qw, qx, qy, qz);
     }
 
     template<>
@@ -172,6 +184,35 @@ namespace sophiar {
         static constexpr size_t element_size = 6;
     };
 
+    template<typename T>
+    inline void small_obj_wrapper<T>::fill_from_json_array(const nlohmann::json &config) {
+
+        using elem_type = basic_obj_traits<this_type>::element_type;
+        static constexpr auto elem_num = basic_obj_traits<this_type>::element_size;
+
+        struct elem_provider {
+            std::array<elem_type, elem_num> vals = {};
+            size_t pos = 0;
+
+            auto &operator>>(elem_type &_val) {
+                _val = vals[pos++];
+                return *this;
+            }
+        };
+
+        auto provider = elem_provider{};
+        if (!config.is_array()) {
+            assert(elem_num == 1);
+            provider.vals[0] = config.get<elem_type>();
+        } else {
+            assert(elem_num == config.size());
+            for (int i = 0; i < elem_num; ++i) {
+                provider.vals[i] = config[i].get<elem_type>();
+            }
+        }
+        fill_from(provider);
+    }
+
 }
 
 #endif //SOPHIAR2_BASIC_OBJ_TYPES_HPP

+ 15 - 2
src/core/global_defs.cpp

@@ -1,10 +1,11 @@
 #include "global_defs.h"
-#include "algorithm/transform_tree.h"
+#include "algorithm/transform_utility.hpp"
 #include "core/external_controller.h"
 #include "core/external_variable_io.h"
 #include "core/sophiar_manager.h"
 #include "core/sophiar_pool.h"
 #include "core/timestamp_helper.hpp"
+#include "core/tristate_obj.h"
 #include "robot/ur/ur_interface.h"
 #include "utility/debug_utility.hpp"
 #include "utility/dynamic_pool.hpp"
@@ -17,6 +18,8 @@
 
 #endif // BOOST_OS_WINDOWS_AVAILABLE
 
+DEFAULT_TRISTATE_OBJ_DEF(transform_tree)
+
 namespace sophiar {
 
     dynamic_pool *global_dynamic_pool = nullptr;
@@ -47,6 +50,16 @@ namespace sophiar {
 #undef REGISTER_VARIABLE_TYPE
     }
 
+    void register_algorithms() {
+        // transform tree
+        REGISTER_TYPE(transform_tree);
+        // transform utility
+        using transform_inverter = simple_tristate_obj_wrapper<make_transform_inverter_func>;
+        using scalarxyz_transformer = simple_tristate_obj_wrapper<make_scalarxyz_transformer>;
+        REGISTER_TYPE(transform_inverter);
+        REGISTER_TYPE(scalarxyz_transformer);
+    }
+
     struct empty_object : public tristate_obj {
         DEFAULT_NEW_INSTANCE(empty_object);
     };
@@ -57,7 +70,7 @@ namespace sophiar {
 
 #if !SOPHIAR_TEST || SOPHIAR_TEST_ALGORITHM
 
-        REGISTER_TYPE(transform_tree);
+        register_algorithms();
 
 #endif
 

+ 1 - 25
src/core/sophiar_pool.cpp

@@ -49,32 +49,8 @@ namespace sophiar {
             }
 
             // load default value
-            using elem_type = basic_obj_traits<SmallObjType>::element_type;
-            static constexpr auto elem_num = basic_obj_traits<SmallObjType>::element_size;
-
-            struct elem_provider {
-                std::array<elem_type, elem_num> vals = {};
-                size_t pos = 0;
-
-                auto &operator>>(elem_type &_val) {
-                    _val = vals[pos++];
-                    return *this;
-                }
-            };
-
-            auto provider = elem_provider{};
-            const auto &value_conf = config["value"];
-            if (!value_conf.is_array()) {
-                assert(elem_num == 1);
-                provider.vals[0] = value_conf.get<elem_type>();
-            } else {
-                assert(elem_num == value_conf.size());
-                for (int i = 0; i < elem_num; ++i) {
-                    provider.vals[i] = value_conf[i].get<elem_type>();
-                }
-            }
             *placeholder = SmallObjType::new_instance();
-            (*placeholder)->fill_from(provider);
+            (*placeholder)->fill_from_json_array(config["value"]);
             return (void *) placeholder;
         }
 

+ 10 - 0
src/utility/coro_worker_helper_func.hpp

@@ -2,6 +2,7 @@
 #define SOPHIAR2_CORO_WORKER_HELPER_FUNC_HPP
 
 #include "core/tristate_obj.h"
+#include "utility/coro_signal_group.hpp"
 
 #include <boost/asio/co_spawn.hpp>
 #include <boost/asio/detached.hpp>
@@ -32,6 +33,15 @@ namespace sophiar {
         return std::move(func);
     }
 
+#define SIGNAL_GROUP_AUTO_CLOSER(signal_group) \
+    [_signal_group = std::move(signal_group)]() mutable { \
+        boost::asio::co_spawn(*global_context, [ \
+                _signal_group = std::move(_signal_group)]() mutable \
+                -> boost::asio::awaitable<void> { \
+            co_await _signal_group->stop(); \
+            co_return; \
+        }, boost::asio::detached); \
+    }
 }
 
 #endif //SOPHIAR2_CORO_WORKER_HELPER_FUNC_HPP

+ 1 - 1
tests/algorithm/transform_tree.cpp

@@ -1,8 +1,8 @@
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MAIN  // in only one cpp file
 
-#include "algorithm/transform_tree.h"
 #include "core/basic_obj_types.hpp"
+#include "core/sophiar_pool.h"
 #include "utility/debug_utility.hpp"
 #include "utility/coro_worker.hpp"
 

+ 51 - 0
tests/algorithm/transform_utility.cpp

@@ -0,0 +1,51 @@
+#define BOOST_TEST_DYN_LINK
+
+#include "core/basic_obj_types.hpp"
+#include "core/sophiar_pool.h"
+#include "utility/debug_utility.hpp"
+#include "utility/coro_worker.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <boost/asio/awaitable.hpp>
+#include <boost/asio/co_spawn.hpp>
+#include <boost/asio/detached.hpp>
+
+#include <nlohmann/json.hpp>
+
+#include <fstream>
+
+using namespace nlohmann;
+using namespace sophiar;
+
+using boost::asio::awaitable;
+using boost::asio::co_spawn;
+using boost::asio::detached;
+
+BOOST_AUTO_TEST_CASE(test_transform_utility) {
+
+    spdlog::set_level(spdlog::level::trace);
+
+    std::ifstream config_file("data/transform_utility_config.json");
+    BOOST_TEST(config_file.is_open());
+    auto config = nlohmann::json::parse(config_file);
+    BOOST_TEST(initialize(config));
+
+    using namespace std::chrono_literals;
+    auto tracker_in_model_index = REQUIRE_VARIABLE(transform_obj, "tracker_in_model");
+    auto worker_a = make_interval_coro_worker(1s, [=, cur_y = int(0)]() mutable -> awaitable<bool> {
+        auto new_trans = Eigen::Isometry3d(Eigen::Translation3d(0, cur_y += 1, 0));
+        UPDATE_VARIABLE_VAL(transform_obj, tracker_in_model_index, std::move(new_trans));
+        co_return true;
+    });
+    worker_a->run();
+
+    auto probe_in_tracker_index = REQUIRE_VARIABLE(transform_obj, "probe_in_tracker");
+    auto worker_b = make_interval_coro_worker(2s, [=, cur_z = int(0)]() mutable -> awaitable<bool> {
+        auto new_trans = Eigen::Isometry3d(Eigen::Translation3d(0, 0, cur_z -= 1));
+        UPDATE_VARIABLE_VAL(transform_obj, probe_in_tracker_index, std::move(new_trans));
+        co_return true;
+    });
+    worker_b->run();
+
+    global_context->run();
+}

+ 94 - 51
tests/data/transform_utility_config.json

@@ -1,72 +1,115 @@
 {
-  "listen_port": 5277,
+  "controller_port": 5277,
+  "variable_list": [
+    {
+      "name": "tracker_in_model",
+      "type": "transform_obj"
+    },
+    {
+      "name": "model_in_tracker",
+      "type": "transform_obj"
+    },
+    {
+      "name": "probe_in_tracker",
+      "type": "transform_obj"
+    },
+    {
+      "name": "probe_in_model",
+      "type": "transform_obj"
+    },
+    {
+      "name": "probe_tip_offset",
+      "type": "scalarxyz_obj",
+      "value": [
+        1,
+        1,
+        1
+      ]
+    },
+    {
+      "name": "probe_tip_in_model",
+      "type": "scalarxyz_obj"
+    }
+  ],
   "object_list": [
     {
       "type": "transform_inverter",
-      "name": "sample_inverter",
+      "name": "model_in_tracker_inverter",
       "start_config": {
-        "input_obj_name": "C_in_D",
-        "output_obj_name": "D_in_C"
+        "input_var_name": "tracker_in_model",
+        "output_var_name": "model_in_tracker"
       }
     },
+    {
+      "type": "transform_tree",
+      "name": "transform_tree",
+      "init_config": {
+        "node_list": [
+          {
+            "name": "tracker"
+          },
+          {
+            "name": "model",
+            "parent": "tracker",
+            "transform_var_name": "model_in_tracker"
+          },
+          {
+            "name": "probe",
+            "parent": "tracker",
+            "transform_var_name": "probe_in_tracker"
+          }
+        ]
+      },
+      "start_config": {
+        "watch_list": [
+          {
+            "target": "probe",
+            "observer": "model",
+            "transform_var_name": "probe_in_model"
+          }
+        ]
+      },
+      "dependencies": [
+        "model_in_tracker_inverter"
+      ]
+    },
     {
       "type": "scalarxyz_transformer",
-      "name": "sample_transformer",
+      "name": "probe_tip_transformer",
       "start_config": {
         "transform_type": "point",
-        "input_obj_name": "probe_offset",
-        "fixed_value": [
-          1,
-          2,
-          3
-        ],
-        "transform_obj_name": "tracker_probe_transform",
-        "output_obj_name": "point_in_probe"
-      }
+        "transform_var_name": "probe_in_model",
+        "input_var_name": "probe_tip_offset",
+        "output_var_name": "probe_tip_in_model"
+      },
+      "dependencies": [
+        "transform_tree"
+      ]
     },
     {
-      "type": "transform_stabilizer",
-      "name": "sample_stabilizer",
+      "type": "scalarxyz_obj_watcher",
+      "name": "probe_tip_watcher",
       "start_config": {
-        "stable_type": "point",
-        "input_obj_name": "point_in_tracker",
-        "output_obj_name": "pickled_point_in_tracker",
-        "linear_tolerance_mm": 0.2,
-        "angular_tolerance_deg": 0.05,
-        "temporal_interval_s": 1.5,
-        "counting_interval": 150
-      }
+        "variable_name": "probe_tip_in_model"
+      },
+      "dependencies": [
+        "probe_tip_transformer"
+      ]
     },
     {
-      "type": "landmark_registration",
-      "name": "sample_registration",
+      "type": "transform_obj_watcher",
+      "name": "debug_watcher",
       "start_config": {
-        "fiducial_points": [
-          [
-            0,
-            0,
-            0
-          ],
-          [
-            0,
-            1,
-            1
-          ],
-          [
-            1,
-            1,
-            1
-          ],
-          [
-            1,
-            1,
-            2
-          ]
-        ],
-        "input_point_obj_name": "pickled_point_in_tracker",
-        "output_transform_obj_name": "registration_result",
-        "output_error_obj_name": "registration_error"
+        "variable_name": "probe_in_model"
       }
+    },
+    {
+      "type": "empty_object",
+      "name": "all",
+      "dependencies": [
+        "probe_tip_watcher",
+        "debug_watcher"
+      ]
     }
   ]
 }

+ 0 - 1
tests/utility/global_obj_record_playback.cpp

@@ -1,6 +1,5 @@
 #define BOOST_TEST_DYN_LINK
 
-#include "algorithm/transform_tree.h"
 #include "core/basic_obj_types.hpp"
 
 #include <boost/test/unit_test.hpp>