How to control a motor using Sin() function with Arduino UNO R3?

I'm using arduino, l298n and a motor with encoder to get its frequency response. I'm using a python code to get all data from arduino and plotting the data using octave. But i need to have control of the frequency of my Sin() function. When i plot it the sin() looks nice, but when i measure its period, it's too different from what i setted in arduino. I'm not sure if this has something to do with arduino's frequency and how many times attachInterrupt() is called. I also suspect of my velocity calculation in python. In summary im looking for guidance in this project. My objective is to plot bode diagram, through this motor's frequency response, and then get the motor's transfer function (approximated)
These are my codes.

this is arduino's:

//#define PWM_PIN 9  // Define o pino PWM a ser usado
#define PWM_freq 1000 // 
#define AMPLITUDE 127  // Amplitude máxima do sinal PWM (0 a 255)
#define MOTOR_PIN_1 7 // Pino do motor (IN1)
#define MOTOR_PIN_2 6 // Pino do motor (IN2)
#define MOTOR_ENABLE 5 // Pino do enable da ponte H
#define chA 3 // Pino canal A do encoder
#define chB 2 // Pino canal B do encoder

int chA_antigo = 0;
int chB_antigo = 0;
volatile int contador = 0;
double tempoAtual = 0;
double tempoAnterior = 0;
double PWM_PERIOD = ((1/PWM_freq)*1000)/360; // Periodo em millissegundos

void setup() {
  // Configuração do pino PWM
  //pinMode(PWM_PIN, OUTPUT);
  pinMode(MOTOR_PIN_1, OUTPUT);
  pinMode(MOTOR_PIN_2, OUTPUT);
  pinMode(MOTOR_ENABLE, OUTPUT);
  pinMode(chA, INPUT);
  pinMode(chB, INPUT);
  attachInterrupt(digitalPinToInterrupt(chA), leituraEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(chB), leituraEncoder, CHANGE);
  
  // Configuração da frequência PWM
  TCCR1B = (TCCR1B & 0xF8) | 0x01; // Configura a frequência PWM para 31.37kHz
  Serial.begin(115200);
}

void loop() {
  //tempoAtual =millis();
  if (tempoAtual <=20000){  // if Para acionar o motor por apenas 2500 millissegundos

    for (int i = 0; i < 360; i++) {
      tempoAtual = millis();
      // Convertendo graus para radianos
      float radianos = i * (PI / 180.0);
      // Calculando o valor do seno e mapeando para o intervalo de 0 a 255
      int valor_pwm = (sin(radianos) * AMPLITUDE/2) + 1.5*AMPLITUDE;  // Máximo de 254 ~= 255 e mínimo 190,5 (digital do duty cycle do PWM)
      // Escrevendo o valor PWM no pino
      controlaMotor(0,1,valor_pwm);
      //Serial.println(valor_pwm);
      //analogWrite(PWM_PIN, valor_pwm);

      Serial.println(String(contador) + ", " + String(tempoAtual-tempoAnterior) + ", "+ String(valor_pwm));
    
      contador = 0; // Reinicia o contador
      tempoAnterior = tempoAtual; // Atualiza o tempo
      // Pequeno atraso para a visualização
      //delayMicroseconds(PWM_PERIOD);
      delay(PWM_PERIOD);
    } // Fazendo o motor girar em alguma direção na velocidade 255

  }
  else{ // Desliga o motor e para de enviar
    controlaMotor(0,0,0); 
    Serial.end();
  }
  //delay(1);
}

void controlaMotor (bool in1, bool in2, float pwm){
  if (in1 == 1 && in2 == 0){
    digitalWrite(MOTOR_PIN_1, HIGH);
    digitalWrite(MOTOR_PIN_2, LOW);
  }
  else if (in1 == 0 && in2 == 1){
    digitalWrite(MOTOR_PIN_1, LOW);
    digitalWrite(MOTOR_PIN_2, HIGH);
  }
  else if (in1 == 0 && in2 == 0){
    digitalWrite(MOTOR_PIN_1, LOW);
    digitalWrite(MOTOR_PIN_2, LOW);
  }
  else if (in1 == 1 && in2 == 1){
    digitalWrite(MOTOR_PIN_1, HIGH);
    digitalWrite(MOTOR_PIN_2, LOW);
  }
  analogWrite(MOTOR_ENABLE, pwm);
}


void leituraEncoder() {
  int chA_atual = digitalRead(chA);
  int chB_atual = digitalRead(chB);

  if (chA_antigo == 0 && chB_antigo == 0) {
    if(chA_atual == 1 && chB_atual == 1) contador = contador + 2;
    else if(chA_atual == 1 && chB_atual == 0) contador--;
    else if(chA_atual == 0 && chB_atual == 1) contador++;
  }
  else if (chA_antigo == 0 && chB_antigo == 1) {
    if(chA_atual == 1 && chB_atual == 1) contador ++;
    else if(chA_atual == 1 && chB_atual == 0) contador = contador - 2;
    else if(chA_atual == 0 && chB_atual == 1) contador = contador;
    else contador--;
  }
  else if (chA_antigo == 1 && chB_antigo == 0) {
    if(chA_atual == 1 && chB_atual == 1) contador--;
    else if(chA_atual == 1 && chB_atual == 0) contador = contador;
    else if(chA_atual == 0 && chB_atual == 1) contador = contador - 2;
    else contador ++;
  }
  else if (chA_antigo = 1 && chB_antigo == 1) {
    if(chA_atual == 1 && chB_atual == 1) contador = contador;
    else if(chA_atual == 1 && chB_atual == 0) contador++;
    else if(chA_atual == 0 && chB_atual == 1) contador--;
    else contador = contador + 2;
  }

  chA_antigo = chA_atual;
  chB_antigo = chB_atual;

}

this is python's:

import serial, datetime, time
from math import pi

ser = serial.Serial(
    port="COM7",
    baudrate=115200,
    timeout=0
)

time.sleep(1.5)

# Lists tempo, velocidade e input de senóide
tempo = list()
velocidade = list()
sin_input = list()

# Buffer de velocidade para cálculo de média móvel
M = 50
inv_M = 1/M
buffer = [0] * M
soma_buffer = sum(buffer)

# Resolução do encoder
enc_res = 200

# Pico de tensão com PWM em 255
pico_tensao = 11.0

# Tempo de execução em segundos
tempo_execucao = 5

# Variável de tempo atual
tempo_atual = 0

print("Conectando a porta: " + ser.portstr)

# Contador de execuções
i = 0

while True:
    if ser.in_waiting <= 0:
        continue
    enc_output = ser.readline().decode("ascii").rstrip("\n").rstrip("\r").rstrip().split(", ")
    if len(enc_output) != 3 :
        continue

    try:
        passos = float(enc_output[0])
        delta_tempo = float(enc_output[1])/1000
        pwm = float(enc_output[2])

        # if i > 0:
        # Senóide de tensão de input baseada no duty cycle do PWM (de 0 a 255)
        sin_input.append(((pwm/255) * pico_tensao) if pwm > 100 else sin_input[-1])
    except ValueError as e:
        continue

    tempo_atual += delta_tempo
    tempo.append(tempo_atual)

    mov_ang = (passos/enc_res) * 2 * pi

    buffer[i%M] = 0 if delta_tempo == 0 else mov_ang/delta_tempo


    # Atualiza o vetor de velocidade a partir da média
    soma_buffer = sum(buffer)
    velocidade.append(soma_buffer*inv_M)

    if(tempo[i] > tempo_execucao): break

    i += 1

with open(f'./output/output{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.txt', 'w') as file:
    file.writelines([f'{str(velocidade)},{str(tempo)},{str(sin_input)}\n' for velocidade, tempo, sin_input in zip(velocidade, tempo, sin_input)])
    file.close()

ser.close()
exit(1)

this is octave's:

clear all
close all

[vel, tempo, sin] = textread('./output/output2024-05-23_12-42-13.txt', "%f,%f,%f")


figure(1)
plot(tempo, vel)
xlabel("Tempo (s)");
ylabel("Velocidade (rad/s)")
figure(2)
plot(tempo, sin)
xlabel("Tempo(s)");
ylabel("Tensão (V)");

That is controlled entirely by how often your code outputs the next PWM value.

Specifically, by the value of PWM_PERIOD in this line:

    delay(PWM_PERIOD);

But delay() takes a long integer argument, not a double

double PWM_PERIOD = ((1/PWM_freq)*1000)/360; // Periodo em millissegundos

and (1/PWM_freq) defaults to an integer divide, in which case 1/1000 == 0.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.