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)");