Tutorial: Interpolated Coordinate System Backend

This tutorial is under construction.

Overview

This advanced tutorial demonstrates how to implement a custom coordsys_backend that interpolates between two parent coordinate systems in SE(3), using linear interpolation (lerp) for translations and spherical linear interpolation (slerp) for orientations.

The Code

#include <cmath>
#include <memory>
#include <print>
import ninbot;

using namespace nin;
using namespace nin::R3;

class coordsys_interp : public coordsys_backend<position_coordsys_traits>
{
public:
   using CT = position_coordsys_traits;

   coordsys<CT> parent_t_0, parent_t_1;
   double t;

   coordsys_interp( coordsys<CT> const& A,
                    coordsys<CT> const& B, double t = 0 )
   : parent_t_0{A.linked_copy()}, parent_t_1{B.linked_copy()}, t{t} { }

   CT::tf_data_type offset(unsigned hop_limit) const override
   {
      if (t == 0) return parent_t_0.tf_to_WCS(hop_limit);
      if (t == 1) return parent_t_1.tf_to_WCS(hop_limit);

      rigid_tf tf_0 = parent_t_0.tf_to_WCS(hop_limit);
      rigid_tf tf_1 = parent_t_1.tf_to_WCS(hop_limit);

      translation const& tr0 = tf_0.translation;
      translation const& tr1 = tf_1.translation;
      translation lerped_tr { lerp(tr0.Δx, tr1.Δx, t),
                              lerp(tr0.Δy, tr1.Δy, t),
                              lerp(tr0.Δz, tr1.Δz, t) };

      orientation_qty ori_0 = tf_0.rotation(orientation_qty{});
      orientation_qty ori_1 = tf_1.rotation(orientation_qty{});
      orientation_qty slerped_ori = slerp(ori_0, ori_1, t);

      rotation slerped_rot = {{}, slerped_ori};

      return rigid_tf{lerped_tr, slerped_rot};
   }

   std::shared_ptr<coordsys_backend<CT>> clone() const override
   {
      return std::make_shared<coordsys_interp>(parent_t_0, parent_t_1, t);
   }
};

int main()
{
   position_coordsys_child CS_A {WCS, { { 0_m, 0_m, 0_m}, {} }};
   position_coordsys_child CS_B {WCS, { {10_m, 0_m, 0_m}, {} }};

   coordsys_generic<coordsys_interp> CS_interp {CS_A, CS_B};

   point P = {CS_interp, {0_m, 0_m, 10_cm}};

   CS_interp->t = 0.0; std::println("t = 0.0: {}", P.map_to(WCS));
   CS_interp->t = 0.5; std::println("t = 0.5: {}", P.map_to(WCS));
   CS_interp->t = 0.8; std::println("t = 0.8: {}", P.map_to(WCS));

   return 0;
}

Building the code

cmake_minimum_required(VERSION 4.0)
project(interpolated-backend LANGUAGES CXX)

find_package(Ninbot REQUIRED)

add_executable(interpolated-backend interpolated-backend.c++)
target_link_libraries(interpolated-backend PRIVATE ninbot::ninbot)

Applications

This code can be used to integrate into POS the CS of a virtual object composed of two different objects. Then the CS would be the centroid of those two objects: t = M1/(M0+M1)

It can also be used as a switch of parent by fixing t to {0,1}: At t=0 the parent is the first parent_CS, at t=1 the parent is the sencod parent_cs. Then hooking another coordsys, so there is no need to change the parent directly, just switch t. Further optimizations are possible in this case. For example, you can attach a camera and you have simple camera switch method worry-free with just one camera.