Program Listing for File PinVelControl.cc

Program Listing for File PinVelControl.cc#

Return to documentation for file (doxygen_docs/GeneralKModels/PinVelControl.cc)

#include "Karana/GeneralKModels/PinVelControl.h"
#include "Karana/KCore/Allocator.h"
#include "Karana/SOADyn/CompoundSubhinge.h"

/**
 * @file
 * @brief PinVelControl implementation.
 */

namespace Karana::Models {

    namespace kd = Karana::Dynamics;

    PinVelControl::PinVelControl(std::string_view name,
                                 const kc::ks_ptr<kd::ModelManager> &mm,
                                 const kc::ks_ptr<kd::PhysicalSubhinge> &sh,
                                 const kc::ks_ptr<ProfileGenerator<km::ArrayVec>> &traj)
        : KModel<PinVelControl, PinVelControlParams>(name, mm)
        , traj(traj)
        , _sh(sh) {
        if (sh->subhingeType() != kd::SubhingeType::PIN) {
            throw std::runtime_error(
                std::format("Subhinge for PinVelControl {} must be PIN", name));
        }
        params = std::allocate_shared<PinVelControlParams>(kc::Allocator<PinVelControlParams>{},
                                                           std::format("{}_params", name));

        // Resize the generalized force vector appropriately
        _t.resize(_sh->nU());

        // Start the internal state at zero so the integral gain can be calculated appropriately
        _e_prev.resize(sh->nQ());
        _e_prev.setZero();

        _integral_contrib.resize(sh->nQ());
        _integral_contrib.setZero();

        _prev_t = km::Ktime{0};

        // keep track of multibody objects used by this model
        if (auto psh = kc::dynamic_pointer_cast<kd::PhysicalSubhinge>(sh); psh) {
            _multibody_objs.physical_subhinges.push_back(psh);
        } else if (auto csh = kc::dynamic_pointer_cast<kd::CompoundSubhinge>(sh); csh) {
            _multibody_objs.compound_subhinges.push_back(csh);
        }
    };

    kc::ks_ptr<PinVelControl>
    PinVelControl::create(std::string_view name,
                          const kc::ks_ptr<kd::ModelManager> &mm,
                          const kc::ks_ptr<kd::PhysicalSubhinge> &sh,
                          const kc::ks_ptr<ProfileGenerator<km::ArrayVec>> &traj) {
        kc::ks_ptr<PinVelControl> pid =
            std::allocate_shared<PinVelControl>(kc::Allocator<PinVelControl>{}, name, mm, sh, traj);
        mm->registerModel(pid);
        return pid;
    }

    void PinVelControl::preDeriv(const km::Ktime &t, const km::Vec &) {
        _t = params->kp * (traj->getQ(t) - _sh->getU().array()) + _integral_contrib;

        if (debug_model) [[unlikely]] {
            stdDebugMsg(
                std::format("error {}", km::dumpString(traj->getQ(t) - _sh->getU().array())));
            stdDebugMsg(std::format("output torque {}", km::dumpString(_t)));
        }
        _sh->accumT(_t);
    }

    void PinVelControl::preModelStep(const km::Ktime &t, const km::Vec &) {
        // Calculate the integral contribution for this step
        _integral_contrib += params->ki * (_e_prev * km::ktimeToSeconds(t - _prev_t));

        // Calculate new error for next time
        _e_prev = traj->getQ(t) - _sh->getU().array();

        // Set _prev_t to t for next time
        _prev_t = t;

        if (debug_model) [[unlikely]] {
            stdDebugMsg(std::format("preModelStep integral error {}", km::dumpString(_e_prev)));
        }
    };

    const kc::ks_ptr<kd::PhysicalSubhinge> &PinVelControl::getSubhinge() const { return _sh; }

    // Destructor included for MacOS builds. Must have a key-function out-of-line to avoid dulpicate
    // symbols.
    PinVelControl::~PinVelControl(){};

    PinVelControlParams::PinVelControlParams(std::string_view name)
        : KModelParams(name) {
        kp = km::notReadyNaN;
        ki = km::notReadyNaN;
    }

    bool PinVelControlParams::isReady() const {
        bool flag = true;
        if (km::isNotReadyNaN(kp)) {
            kc::warn("Parameter kp is not ready.");
            flag = false;
        }
        if (km::isNotReadyNaN(ki)) {
            kc::warn("Parameter ki is not ready.");
            flag = false;
        }
        return flag;
    }

    kc::ks_ptr<kc::BaseVars> PinVelControlParams::_getVars() const {
        return PinVelControlParamsVars::create(kc::static_pointer_cast<const PinVelControlParams>(
            kc::ks_ptr<const Base>(shared_from_this())));
    };

    PinVelControlParamsVars::PinVelControlParamsVars(
        const kc::ks_ptr<const PinVelControlParams> &model)
        : Karana::Core::BaseVars(model) {

        kp = Karana::Core::Var_T<double>::create(
            "kp", [model] { return model->kp; }, "Proportional gain", "");

        ki = Karana::Core::Var_T<double>::create(
            "ki", [model] { return model->ki; }, "Integral gain", "");
    }

    PinVelControlParamsVars::~PinVelControlParamsVars(){};

    kc::ks_ptr<PinVelControlParamsVars>
    PinVelControlParamsVars::create(const kc::ks_ptr<const PinVelControlParams> &model) {
        return std::allocate_shared<PinVelControlParamsVars>(
            kc::Allocator<PinVelControlParamsVars>{}, model);
    }

    kc::NestedVars PinVelControlParamsVars::getAllVars() const {
        auto out = Karana::Core::BaseVars::getAllVars();
        kc::addVarsToStruct(out, kp, ki);

        return out;
    }

} // namespace Karana::Models