PID controlled brushed motor behaving unexpectedly

I am using a rhino motor with an inbuilt encoder along with a Cytron motor driver. I want to build precise position control. That is I put in an angle it should go to that angle, just like a servo.

I used the following code to make the initial setup and also to tune the PID values. It generates a sin wave and makes the motor follow it. My plan was to then try to match the actual sin wave with the motor encoder output, to PID tune it.

#include <PID_v1.h>

// Motor driver pins
#define DIR_PIN 19
#define PWM_PIN 18

// Encoder pins (Modify as per your setup)
#define ENCODER_A 7
#define ENCODER_B 8

volatile long encoderCount = 0;

// PID parameters
double setpoint, input, output;
double Kp = 2.5, Ki = 0 , Kd = 0; // Tune these values
PID motorPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

// Angle generation
int angle = 0;
int angleStep = 1;
bool increasing = true;

void encoderISR() {
    if (digitalRead(ENCODER_A) == digitalRead(ENCODER_B)) {
        encoderCount++;
    } else {
        encoderCount--;
    }
}

void setup() {
    Serial.begin(9600);
    pinMode(DIR_PIN, OUTPUT);
    pinMode(PWM_PIN, OUTPUT);
    pinMode(ENCODER_A, INPUT_PULLUP);
    pinMode(ENCODER_B, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderISR, CHANGE);
    
    motorPID.SetMode(AUTOMATIC);
    motorPID.SetOutputLimits(-200, 200);
}

void loop() {
    // Handle Serial Input for PID tuning
    if (Serial.available()) {
        String command = Serial.readStringUntil('\n');
        command.trim();
        if (command.startsWith("Kp")) {
            Kp = command.substring(3).toFloat();
            motorPID.SetTunings(Kp, Ki, Kd);
        } else if (command.startsWith("Ki")) {
            Ki = command.substring(3).toFloat();
            motorPID.SetTunings(Kp, Ki, Kd);
        } else if (command.startsWith("Kd")) {
            Kd = command.substring(3).toFloat();
            motorPID.SetTunings(Kp, Ki, Kd);
        }
    }

    // Generate sine wave setpoint
    setpoint = sin(radians(angle)) * 100.0; // Scale as needed
    
    // Read encoder as input
    input = encoderCount;
    
    // Compute PID output
    motorPID.Compute();
    
    // Write to motor
    motorWrite(output);
    
    // Print for plotting with labels
    Serial.print("Setpoint:");
    Serial.print(setpoint);
    Serial.print(", Input:");
    Serial.print(input);
    Serial.print(", Output:");
    Serial.print(output);
    Serial.print(", Ylimtop:");
    Serial.print(400);
    Serial.print(", Ylimbottom:");
    Serial.println(-400);

    // Update angle
    if (increasing) {
        angle += angleStep;
        if (angle >= 360) increasing = false;
    } else {
        angle -= angleStep;
        if (angle <= 0) increasing = true;
    }
    
    delay(10); // Adjust sampling rate
}

void motorWrite(double speed) {
    int pwmValue = map(abs(speed), 0, 200, 0, 255);
    digitalWrite(DIR_PIN, speed > 0 ? HIGH : LOW);
    analogWrite(PWM_PIN, constrain(pwmValue, 0, 255));
}

When I run this code the motor seems to go back and forth like expected, but sometimes it goes the same direction twice. And the bigger problem is almost always after sometime the output pid value maxes out to -200 and then doesn't recover. The motor just keeps spinning in its max speed in one direction and doesn't respond to anything.

Does anyone know why the motor is behaving the way it is? I have been stuck here for a while now, and I don't understand where it is wrong. Any help would be very much appreciated.

If you have a theory as to why your idea should work, and some documentation describing what a "rhino motor" is and does, and how it might be connected to the mysterious Cytron motor driver, consider enlightening forum members.

Hint: have a look at the "How to get the best out of this forum" post, linked at the head of every forum category.

Maybe not the answer to the full problem, but here you do not correct angle out of bounds. You just change direction.