Program Listing for File PID.cc

Program Listing for File PID.cc#

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

#include "Karana/GeneralKModels/PID.h"
#include "Karana/KCore/Allocator.h"

/**
 * @file
 * @brief PID implementation.
 */

namespace Karana::Models {

    namespace kd = Karana::Dynamics;

    PID::PID(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<PID, PIDParams>(name, mm)
        , traj(traj)
        , _sh(sh)
        , _is_attitude(sh->subhingeType() == kd::SubhingeType::SPHERICAL) {
        params = std::allocate_shared<PIDParams>(kc::Allocator<PIDParams>{},
                                                 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};
    };

    kc::ks_ptr<PID> PID::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<PID> pid = std::allocate_shared<PID>(kc::Allocator<PID>{}, name, mm, sh, traj);
        mm->registerModel(pid);
        return pid;
    }

    void PID::preDeriv(const km::Ktime &t, const km::Vec &) {
        if (_is_attitude) {
            // Attitude error
            km::Vec3 dark = (km::UnitQuaternion(km::RotationVector(traj->getQ(t))) *
                             km::UnitQuaternion(km::RotationVector(-_sh->getQ())))
                                .toRotationVector()
                                .toVec3();
            _t = params->kp * (dark.array()) + params->kd * (traj->getU(t) - _sh->getU().array()) +
                 _integral_contrib;

            if (debug_model) [[unlikely]] {
                stdDebugMsg(std::format("angle error {}", km::dumpString(dark)));
                stdDebugMsg(std::format("angular velocity error {}",
                                        km::dumpString(traj->getU(t) - _sh->getU().array())));
                stdDebugMsg(std::format("output torque {}", km::dumpString(_t)));
            }
        } else {
            _t = params->kp * (traj->getQ(t) - _sh->getQ().array()) +
                 params->kd * (traj->getU(t) - _sh->getU().array()) + _integral_contrib;

            if (debug_model) [[unlikely]] {
                stdDebugMsg(std::format("output torque {}", km::dumpString(_t)));
            }
        }
        _sh->setT(_t);
    }

    void PID::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
        if (_is_attitude) {
            _e_prev = (km::UnitQuaternion(km::RotationVector(traj->getQ(t))) *
                       km::UnitQuaternion(km::RotationVector(-_sh->getQ())))
                          .toRotationVector()
                          .toVec3();
        } else {
            _e_prev = traj->getQ(t) - _sh->getQ().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)));
        }
    };

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

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

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