• Autor de la entrada:
  • Tiempo de lectura:17 minutos de lectura

Recopilación de trucos y métodos más habituales para trazar diagramas de Bode con Octave.

Realizar diagramas de Bode con Octave es sencillo siempre y cuando no se necesiten detalles específicos, es en ese punto cuando comienzan las búsquedas más o menos exitosas por internet, se pretende reunir las respuestas a las consultas más habituales.

Diagrama de Bode básico

Para las pruebas se comienza suponiendo la función de transferencia del filtro de primer orden

\[G(s)=\frac{\omega_0}{s + \omega_0}\]

Un código como el siguiente devuelve un diagrama de Bode sencillo que sirve perfectamente para ver de forma general el comportamiento del sistema en el dominio de la frecuencia (se debe cargar el paquete control con pkg load control).

wo = 100;
f1 = tf([wo], [1 wo])
bode(f1)
Bode simple con Octave
Bode simple con Octave

Formato del trazado

El comando bode(Gs) permite modificadores sin necesidad de complicar en exceso el código mediante duplas «propiedad»-valor, estos en teoría son los mismos que plot(), sin embargo hasta al menos la versión 7.1.0 esto no es del todo cierto.

  • linestyle
    • ‘-’ Línea continua (por defecto)
    • ‘–’ Línea
    • ‘:’ Línea de puntos
    • ‘-.’ Use dash-dotted lines
  • marker
    • ‘+’, ‘o’, ‘*’, ‘.’, ‘x’, ‘s’, ‘d’, ‘^’, ‘v’, ‘>’, ‘<’, ‘p’, ‘h’
  • linewidth (no funciona en Bode(Gs))
    • Ancho en número decimal, 1 por defecto
  • markersize
    • Valores numéricos positivos
  • color
    • ‘k’ negro
    • ‘r’ rojo
    • ‘g’ verde
    • ‘b’ azul
    • ‘m’ magenta
    • ‘c’ cian
    • ‘w’ blanco

Rango de análisis

Puede que el rango analizado por defecto no sea adecuado, para modificarlo se puede usar el siguiente código

wo = 100;
F = tf([wo], [1 wo])

n = 1000; %Puntos análisis frecuencia
w_i = 0;  %Frecuencia inicial 10^x
w_f = 5;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);

bode(F, rango)

El número de puntos de análisis es orientativo y depende de las necesidades de la representación, para conseguir la ilusión de continuidad es recomendable usar al menos 100 puntos/década para trazado con línea y 1000 puntos/década para trazado con puntos. En otras ocasiones, por ejemplo para comparar varios diagramas en la misma gráfica, se pueden cambiar los marcadores entre ellas necesitando menos de 20 puntos/década.

Mostrar frecuencia en hercios

Algo habitual puede ser mostrar la frecuencia del eje x en hercios (Hz) en lugar de radianes/segundo (rad/s), hay un método muy simple trabajando con los datos del diagrama una vez se ha generado. En cualquier caso, la función de transferencia trabajará en rad/s, debe tenerse en cuenta para evitar el error habitual de generarlas pasando una frecuencia en hercios.

wo = 100;
F = tf([wo], [1 wo])
bode(F)

XdataObjs = findobj(gcf,'-property','XData');
set(XdataObjs(2), 'XData', get(XdataObjs(2),'XData')/(2*pi));
set(XdataObjs(4), 'XData', get(XdataObjs(4),'XData')/(2*pi));
xlabel('Frequency (Hz)');
Bode en hercios con Octave
Bode en hercios con Octave

Sin embargo, este método no es muy recomendable ya que no permite mucha flexibilidad y la posición de los objetos puede variar. Para realizar manipulaciones en un diagrama de Bode lo más recomendable es extraer los datos del mismo a matrices, operar sobre esas matrices y luego pintar los datos como se desee con el comando semilogx() (en el que si funcionan correctamente los modificadores), para ello se puede utilizar un código como el siguiente

wo = 100;
F = tf([wo], [1 wo])

[mag, pha, w] = bode(F);

mag = 20*log10(mag);
w = w/(2*pi());

subplot(2, 1, 1)
semilogx(w, mag)
grid on
ylabel("Ganancia (dB)")
xlabel("Frecuencia (Hz)")

subplot(2, 1, 2)
semilogx(w, pha)
grid on
ylabel("Fase (°)")
xlabel("Frecuencia (Hz)")

Por último, para que el diagrama no aparezca cortado, puede ajustarse el rango del eje x aumentando el rango de análisis y luego recortar la gráfica:

wo = 100;
F = tf([wo], [1 wo])

n = 100; %Puntos análisis frecuencia
w_i = -1;  %Frecuencia inicial 10^x
w_f = 5;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);

[mag, pha, w] = bode(F, rango);

mag = 20*log10(mag);
w = w/(2*pi());

subplot(2, 1, 1)
semilogx(w, mag)
axis([min(w), max(w), min(mag), 10])
grid on
ylabel("Ganancia (dB)")
xlabel("Frecuencia (Hz)")

subplot(2, 1, 2)
semilogx(w, pha)
axis([min(w), max(w), -90, 0])
grid on
ylabel("Fase (°)")
xlabel("Frecuencia (Hz)")
Bode en hercios con Octave y plot()
Bode en hercios con Octave y plot()

Funcionamiento comando subplot(x, y, z)

El comando subplot() permite gestionar distintas gráficas en una misma ventana generada con Octave, sus argumentos más básicos son subplot(x, y, z) donde

  • x: número de filas en la ventana
  • y: número de columnas en la ventana
  • z: elemento a manipular, siguiendo el orden de la siguiente imagen
Gestión de ventanas con subplot en Octave
Gestión de ventanas con subplot() en Octave

Valores por unidad

En muchas ocasiones se quiere mostrar el comportamiento de un sistema comparándolo con algún parámetro del mismo, por ejemplo, se podría querer conocer la forma del diagrama de Bode en torno a la frecuencia de corte wo de un filtro.

Bastará dividiendo la matriz con los puntos de frecuencia entre wo.

wo = 100;
F = tf([wo], [1 wo])

n = 100; %Puntos análisis frecuencia
w_i = -1;  %Frecuencia inicial 10^x
w_f = 5;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);

[mag, pha, w] = bode(F, rango);

mag = 20*log10(mag);
w = w/wo;

subplot(2, 1, 1)
semilogx(w, mag)
axis([min(w), max(w), min(mag), 10])
grid on
ylabel("Ganancia (dB)")
xlabel("Frecuencia (Hz)")

subplot(2, 1, 2)
semilogx(w, pha)
axis([min(w), max(w), -90, 0])
grid on
ylabel("Fase (°)")
xlabel("Frecuencia (Hz)")
Bode con valores por unidad
Bode con valores por unidad

Añadir marcas a trazados

Es habitual que en un diagrama de Bode se quiera destacar una frecuencia y magnitud, en el filtro de ejemplo se podría querer destacar la frecuencia de corte, su magnitud y su desfase.

Trazar rectas con comando plot()

Se pueden trazar rectas que comienzan y finalizan en los puntos (x0, y0) y (x1, y1) con el comando plot() de la siguiente forma

plot([x0 x1], [y0 y1], «modificador», parámetro)

Añadir texto en gráficas de Octave

Para añadir un texto en las coordenadas x0, y0 se usa el comando text(), se debe recordar que todos los cambios se realizan en la última gráfica activa, por lo que podría ser necesario utilizar el comando subplot()

text(x0, y0, «texto a añadir»)

wo = 100;
F = tf([wo], [1 wo])

n = 1000; %Puntos análisis frecuencia
w_i = -1;  %Frecuencia inicial 10^x
w_f = 5;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);

[mag, pha, w] = bode(F, rango);

mag = 20*log10(mag);
w = w/wo;

subplot(2, 1, 1)
hold on
semilogx(w, mag, "marker", '.', 'r', "markersize", 10)
axis([min(w), max(w), min(mag), 10])
plot([min(w) 1], [0 0], 'k', "linestyle", '--', "linewidth", 1.1)
plot([1 max(w)], [0 min(mag)], 'k', "linestyle", '--', "linewidth", 1.1)
plot([min(w) 1], [-3 -3], 'k', "linestyle", ':', "linewidth", 1)
plot([1 1], [-3 min(mag)], 'k', "linestyle", ':', "linewidth", 1)
grid on
ylabel("Ganancia (dB)")
xlabel("Frecuencia (w/w_0)")

subplot(2, 1, 2)
hold on
semilogx(w, pha, "marker", '.', 'r', "markersize", 10)
axis([min(w), max(w), -90, 0])
plot([min(w) 0.1], [0 0], 'k', "linestyle", '--', "linewidth", 1.1)
plot([0.1 10], [0 -90], 'k', "linestyle", '--', "linewidth", 1.1)
plot([10 max(w)], [-90 -90], 'k', "linestyle", '--', "linewidth", 1.1)
plot([min(w) 1], [-45 -45], 'k', "linestyle", ':', "linewidth", 1)
plot([1 1], [-45 -90], 'k', "linestyle", ':', "linewidth", 1)
grid on
ylabel("Fase (°)")
xlabel("Frecuencia (w/w_0)")
Bode con marcas de referencia
Bode con marcas de referencia

Trazado difuminado en diagramas de Bode con Octave

Aunque se trabajen con funciones de transferencia en tiempo continuo, se debe tener en cuenta que Octave es un software ejecutándose en una máquina digital y por tanto, en un sistema discreto. La forma en la que ciertos programas simulan el comportamiento en tiempo continuo es aumentando la frecuencia de muestreo de forma que no se noten los saltos entre una muestra y otra. Si esto es lo que hace Octave, ¿Qué tiene que ver ahora?

Diagrama de Bode difuminado al aumentar ancho de línea
Diagrama de Bode difuminado al aumentar ancho de línea

En Octave, al aumentar el grosor de un trazado cuando los puntos que lo forman están muy cerca, se produce este efecto de difuminado. Una forma de solucionarlo es decirle a Octave que utilice marcadores tipo punto mediante bode(Gs, marker ‘.’), de esta forma todos los puntos del trazado se volverán completamente sólidos.

wo = 100;
F = tf([wo], [1 wo])

n = 10000; %Puntos análisis frecuencia
w_i = -1;  %Frecuencia inicial 10^x
w_f = 5;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);

[mag, pha, w] = bode(F, rango);

mag = 20*log10(mag);
w = w/wo;

subplot(2, 1, 1)
hold on
semilogx(w, mag, "marker", '.', "markersize", 10)
axis([min(w), max(w), min(mag), 10])
grid on
ylabel("Ganancia (dB)")
xlabel("Frecuencia (w/w_0)")

subplot(2, 1, 2)
hold on
semilogx(w, pha, "marker", '.', "markersize", 10)
axis([min(w), max(w), -90, 0])
grid on
ylabel("Fase (°)")
xlabel("Frecuencia (w/w_0)")

Una alternativa muy eficaz al marcador punto es el marcador ‘o’. En este caso habrá que reducir el tamaño del mismo con «markersize», 0.5 u otro valor al gusto.

Varios diagramas en la misma ventana

En el caso que sea necesario representar varios diagramas de Bode como en el caso de un sistema SOGI, donde se tienen dos respuestas distintas para una misma entrada, se necesitará colocar varios diagramas en paralelo como en la siguiente imagen

Diagrama de Bode sistema SOGI
Dos diagramas de bode con distintas configuraciones en la misma ventana

En esta imagen además de tener dos diagramas de Bode se tienen varias configuraciones para cada uno. La forma más práctica de lograr esto es mediante matrices dinámicas, así no será necesario especificar a priori el número de elementos que se trazarán en cada gráfica. El código para ello es el siguiente

clear; close all;  clc; clf;
pkg load control

wo = 2*pi*50;   %Parámetro frecuencia resonancia del SOGI
k = [0.6, 0.7, 0.8, 1.5];   %Parámetro k del SOGI

%Variables análisis
n = 1000; %Puntos análisis frecuencia
w_i = 1;  %Frecuencia inicial 10^x
w_f = 4;  %Frecuencia final 10^x
rango = logspace(w_i, w_f, n);
%Matrices resultados analisis frecuencia
d_bode = zeros(2, n, length(k)); %[mag, phas] x n x nk
q_bode = zeros(2, n, length(k)); %[mag, phas] x n x nk
mag = 1; pha = 2; %Posición de los valores en matriz

%Strings gráficas
titulos = {"Ganancia Directo";"Ganancia Quadratura";
           "Fase Directo";"Fase Quadratura"};
legend_str = cell([1 length(k)]);  %Texto para leyendas
for i=1:length(k)
  legend_str(1,i) = sprintf("K=%.2f", k(i));
endfor

%Análisis TF según k
for i=1:length(k)
  Hd=tf([k(i)*wo 0],[1 k(i)*wo wo^2]);  %TF SOGI directa
  Hq=tf([k(i)*wo^2],[1 k(i)*wo wo^2]);  %TF SOGI cuadratura
  [d_bode(mag,:,i), d_bode(pha,:,i)] = bode(Hd, rango);
  [q_bode(mag,:,i), q_bode(pha,:,i)] = bode(Hq, rango);
endfor

%Convierte magnitude a dB
for j=1:length(k)
  for i=1:n
    d_bode(mag,i,j)=20*log10(d_bode(mag,i,j));
    q_bode(mag,i,j)=20*log10(q_bode(mag,i,j));
  endfor
endfor

%Dibuja graficas
for j=1:length(k)
  hold on
  subplot(2, 2, 1)
  semilogx(rango, d_bode(mag,:,j))

  hold on
  subplot(2, 2, 2)
  semilogx(rango, q_bode(mag,:,j))

  hold on
  subplot(2, 2, 3)
  semilogx(rango, d_bode(pha,:,j))

  hold on
  subplot(2, 2, 4)
  semilogx(rango, q_bode(pha,:,j))
endfor
%Formato gráficas
for j=1:4
  subplot(2, 2, j)
  grid on
  xlabel("Frequency [rad/s]");
  legend(legend_str)
  title(titulos(j))
  axis("tight")
  if(j<3)
    ylabel("Magnitude [dB]");
  else
    ylabel("Phase [deg]");
  endif
endfor