Skip to content

File Motor.hpp

File List > demo-projets > stm32 > include > Motor.hpp

Go to the documentation of this file.

#pragma once

#include "utils/Debug.hpp"
#include "utils/Regulator.hpp"

#include <stdint.h>

inline const uint16_t motorLoopFreq = 100;

class Motor {
public:
    Motor() {
        m_lastEncTicks = 0;
        reset();
    }

    void reset() {
        m_velocityReg = Regulator(INT16_MAX, 150000, 300000, 20000);
        m_positionReg = Regulator(500, 1000, 0, 0);
        m_dither = 0;
        m_targetVelocity = 0;
        m_actualPower = 0;
        m_actualPosition = 0;
        m_targetPosition = 0;
        m_actualTicksPerLoop = 0;
        m_posEpsilon = 3;
        m_velEpsilon = 3;
        m_maxAccel = 2000 / motorLoopFreq;
        m_mode = MotorMode_POWER;
    }

    bool atTargetPosition() const {
        return uint32_t(abs(m_actualPosition - m_targetPosition))
            <= m_posEpsilon;
    }

    bool atStandstill() const {
        return uint32_t(abs(m_actualTicksPerLoop)) <= m_velEpsilon;
    }

    MotorMode mode() const { return m_mode; }
    void modeChange(MotorMode newMode) { m_mode = newMode; }

    void reportStat(CoprocStat_MotorStat& stat) {
        stat.mode = m_mode;
        stat.position = m_actualPosition;
        stat.power = m_actualPower;
        stat.velocity = m_actualTicksPerLoop * motorLoopFreq;
    };

    int16_t poll(uint16_t encTicks) {
        m_actualTicksPerLoop = encTicks - m_lastEncTicks;
        m_actualPosition += m_actualTicksPerLoop;
        m_lastEncTicks = encTicks;

        switch (m_mode) {
        case MotorMode_POSITION:
        case MotorMode_POSITION_IDLE: {
            // DEBUG("i:%ld e:%ld s:%ld -> v:%ld\n", m_positionReg.integrator(),
            //     m_positionReg.e(), m_actualPosition, m_positionReg.output());
            if (atTargetPosition() && atStandstill()) {
                m_positionReg.clear();
                m_targetVelocity = 0;
                modeChange(MotorMode_POSITION_IDLE);
            } else {
                auto action
                    = m_positionReg.process(m_targetPosition, m_actualPosition);

                // Limit ramp-up to max acceleration
                if (action > m_targetVelocity + m_maxAccel) {
                    m_targetVelocity += m_maxAccel;
                } else if (action < m_targetVelocity - m_maxAccel) {
                    m_targetVelocity -= m_maxAccel;
                } else {
                    m_targetVelocity = action;
                }
            }
        } // fallthrough
        case MotorMode_VELOCITY: {
            int16_t targetTicksPerLoop = m_targetVelocity / motorLoopFreq;
            uint16_t targetTicksRem = abs(m_targetVelocity % motorLoopFreq);
            if ((targetTicksRem * 4) / motorLoopFreq > m_dither) {
                targetTicksPerLoop += m_targetVelocity < 0 ? -1 : 1;
            }
            if (++m_dither >= 4) {
                m_dither = 0;
            }

            auto action = m_velocityReg.process(
                targetTicksPerLoop, m_actualTicksPerLoop);
            m_actualPower = action;
        } break;
        default:
            break;
        }
        return m_actualPower;
    }

    void setTargetPower(int16_t power) {
        if (m_mode != MotorMode_POWER) {
            modeChange(MotorMode_POWER);
        }
        m_actualPower = power;
    }

    void setTargetBrakingPower(int16_t brakingPower) {
        if (m_mode != MotorMode_BRAKE) {
            modeChange(MotorMode_BRAKE);
        }
        m_actualPower = brakingPower;
    }

    void setTargetVelocity(int16_t ticksPerSec) {
        if (m_mode != MotorMode_VELOCITY) {
            m_velocityReg.clear();
            modeChange(MotorMode_VELOCITY);
        }
        m_targetVelocity = ticksPerSec;
    }

    void homePosition(int32_t homedTicks) {
        m_targetPosition = homedTicks;
        m_actualPosition = homedTicks;
    }

    void setTargetPosition(
        const CoprocReq_MotorReq_SetPosition& req, bool additive) {
        if (m_mode != MotorMode_POSITION) {
            m_positionReg.clear();
            modeChange(MotorMode_POSITION);
        }
        if (additive) {
            m_targetPosition += req.targetPosition;
        } else {
            m_targetPosition = req.targetPosition;
        }
        m_positionReg.setMaxOutput(req.runningVelocity);
    }

    void setVelocityPid(const RegCoefs& coefs) {
        m_velocityReg.setP(coefs.p);
        m_velocityReg.setI(coefs.i);
        m_velocityReg.setD(coefs.d);
        m_velocityReg.clear();
    }

    void setPositionPid(const RegCoefs& coefs) {
        m_positionReg.setP(coefs.p);
        m_positionReg.setI(coefs.i);
        m_positionReg.setD(coefs.d);
        m_positionReg.clear();
    }

    void setConfig(const MotorConfig& config) {
        m_velEpsilon = config.velEpsilon;
        m_posEpsilon = config.posEpsilon;
        m_maxAccel = config.maxAccel / motorLoopFreq;
    }

private:
    Regulator m_velocityReg;
    Regulator m_positionReg;
    int16_t m_actualPower;
    int16_t m_targetVelocity;
    int32_t m_targetPosition;
    int32_t m_actualPosition;
    int16_t m_actualTicksPerLoop;
    uint16_t m_dither;
    uint16_t m_lastEncTicks;
    uint16_t m_posEpsilon;
    uint16_t m_velEpsilon;
    uint16_t m_maxAccel;
    MotorMode m_mode;
};