Levitador Magnético

En este post se describe el diseño, implementación y puesta en marcha de un sistema de levitación magnética basado en control electrónico. El objetivo principal del proyecto es lograr la suspensión estable de un imán mediante la regulación del campo magnético generado por un electroimán. Para ello, se ha desarrollado un sistema que integra sensores, electrónica de potencia y un controlador digital, permitiendo no solo el funcionamiento del levitador, sino también su ajuste y análisis experimental. A lo largo de esta memoria se detallan tanto las decisiones de diseño como el comportamiento del sistema.

A continuación, se listan los componentes del sistema para tenerlos como referencia y poder seguir las explicaciones de qué papel juega cada uno dentro del proyecto:

U1 = Arduino Nano

U2 = LM2596 (buck 12V -> 7.5V)

U3 = TC4427 (driver MOSFET)

U4 = IRLZ44N (MOSFET N)

U5 = Pantalla OLED I2C (SDA, SCL, GND, VCC)

U6 = SS495A (sensor Hall analógico)

B1 = Botón

L1 = Bobina / electroimán

D1 = SB5100

R1 = 10 ohm

R2 = 47 kohm

C1 = 100 nF

C2 = 100 nF

C3 = 10 uF

C4 = 1000 uF

C5 = 100 uF

C6 = 100 nF

P1 = Potenciómetro Setpoint

P2 = Potenciómetro PwmBase

P3 = Potenciómetro Kd

En primer lugar, la alimentación del sistema parte de una fuente de 12 V, necesaria para energizar la bobina (L1), que es el elemento actuador encargado de generar el campo magnético. Esta tensión es acondicionada mediante un convertidor reductor LM2596 (U2), el cual transforma los 12 V a aproximadamente 7.5 V para alimentar al Arduino Nano (U1) a través de su pin VIN. Este módulo permite una conversión eficiente y estable, reduciendo disipación térmica respecto a reguladores lineales. Para estabilizar la línea de entrada se utiliza el condensador C4 (1000 µF), que filtra variaciones bruscas de corriente generadas por la bobina. A su vez, los condensadores C5 (100 µF) y C6 (100 nF) se encargan del desacople en la línea de 5 V del Arduino, eliminando ruido de baja y alta frecuencia respectivamente.

La medición de posición se realiza mediante el sensor Hall analógico SS495A (U6), el cual entrega una tensión proporcional al campo magnético detectado. Este componente es clave para cerrar el lazo de control, ya que permite conocer la posición del objeto levitado. Su salida se conecta a la entrada analógica A0 del Arduino. El condensador C1 (100 nF) se incorpora como desacople local para reducir ruido en la señal del sensor, mejorando la estabilidad de la medición.

El procesamiento y control del sistema lo realiza el Arduino Nano (U1), que ejecuta el algoritmo de control. Para ajustar los parámetros del controlador, se incluyen tres potenciómetros: P1 (Setpoint) permite definir la posición deseada de levitación, P2 (PWM Base) establece una corriente base en la bobina, y P3 (Kd) ajusta cómo de fuerte es la corrección que aplica controlador. Estos potenciómetros funcionan como divisores de tensión entre 5 V y GND, entregando valores analógicos a las entradas A1, A2 y A3 del microcontrolador. Además, se incorpora un botón (B1) conectado al pin digital D5, para encender y apagar el levitador sin desconectar el sistema.

Para la visualización de variables del sistema ajustadas mediante los potenciómetros se utiliza una pantalla OLED (U5) con interfaz I²C, conectada a los pines SDA (A4) y SCL (A5).

La etapa de potencia es la encargada de manejar la corriente de la bobina. El Arduino genera una señal PWM que es aplicada al driver TC4427 (U3), el cual actúa como adaptador de potencia, proporcionando la corriente necesaria para conmutar rápidamente el MOSFET. Este driver es esencial para asegurar tiempos de conmutación rápidos y evitar pérdidas en el transistor. Los capacitores C2 (100 nF) y C3 (10 µF) se utilizan para desacoplar la alimentación del driver, garantizando estabilidad durante la conmutación.

El elemento de conmutación principal es el MOSFET IRLZ44N (U4), que controla la corriente que circula por la bobina. Este transistor se elige por su baja resistencia en conducción y capacidad de ser manejado a nivel lógico, es decir que con voltajes relativamente bajos de entorno a 5V es capaz de funcionar a pleno rendimiento. La resistencia R1 (10 Ω) se coloca en serie con la compuerta para limitar la corriente de carga y evitar oscilaciones, mientras que R2 (47 kΩ) asegura que el MOSFET permanezca apagado en ausencia de señal.

Finalmente, se elige el diodo Schottky SB5100 (D1) como componente para proteger al MOSFET frente a los picos de tensión generados por la naturaleza inductiva de la bobina al interrumpir la corriente. Sin este componente, podrían producirse daños severos en el transistor. Va implícito que al ser un Schottky es adecuado para funcionar altas velocidades de conmutación, proporcionando un margen muy amplio. En conjunto, todos estos elementos permiten implementar un sistema de control robusto, donde la medición precisa, el procesamiento digital y la conmutación eficiente de potencia trabajan de forma integrada para lograr la levitación estable del objeto.

Esquema de conexiones

Conexiones en formato netlist:

# Alimentación de entrada

NET_12V_IN:

  U2.VIN+

  L1.1

  D1.K

  U3.VDD

  C4.+

NET_GND:

  U1.GND

  U2.VIN-

  U3.GND

  U4.S

  U5.GND

  U6.GND

  B1.2

  P1.1

  P2.1

  P3.1

  R2.2

  C1.2

  C2.2

  C3.-

  C4.-

  C5.-

  C6.2

# Salida del buck

NET_7V5:

  U2.VOUT+

  U1.VIN

  C5.+

  C6.1

# Alimentación 5V lógica

NET_5V:

  U1.5V

  U6.VCC

  U5.VCC

  P1.3

  P2.3

  P3.3

  C1.1

# Sensor Hall

NET_HALL_OUT:

  U6.OUT

  U1.A0

# Pantalla OLED I2C

NET_I2C_SDA:

  U5.SDA

  U1.A4

NET_I2C_SCL:

  U5.SCL

  U1.A5

# Potenciómetros

NET_P1_WIPER:

  P1.2

  U1.A1

NET_P2_WIPER:

  P2.2

  U1.A2

NET_P3_WIPER:

  P3.2

  U1.A3

# Botón

NET_BUTTON:

  B1.1

  U1.D5

# Control hacia driver

NET_PWM_CTRL:

  U1.D9

  U3.INA

# Salida driver a compuerta MOSFET

NET_GATE_DRIVE:

  U3.OUTA

  R1.1

NET_GATE:

  R1.2

  U4.G

  R2.1

# Etapa de potencia

NET_DRAIN:

  U4.D

  L1.2

  D1.A

# Desacoples del TC4427

NET_TC4427_DECOUPLED_VDD:

  U3.VDD

  C2.1

  C3.+

Ajuste y validación experimental

El siguiente paso consistió en la validación experimental del sistema. Inicialmente, se desarrollaron programas básicos con el objetivo de verificar el correcto funcionamiento individual de cada bloque: lectura del sensor Hall, generación de señal PWM, respuesta del driver y conmutación del MOSFET. Tras confirmar que todos los elementos operaban correctamente, se procedió a implementar versiones más avanzadas del software orientadas a la calibración del sistema. Este proceso implicó ajustar progresivamente los parámetros de control y analizar la respuesta del levitador bajo distintas condiciones, con el fin de obtener un comportamiento estable y repetible.

CALIBRACIÓN:

Para caracterizar el sistema y comprender su comportamiento, se realizaron múltiples pruebas experimentales tomando un total de 1000 muestras por cada condición evaluada. En una primera etapa, se analizó la respuesta del sensor Hall con PWM igual a 0, es decir, sin acción del electroimán, lo que permitió establecer una relación base entre la distancia y la señal medida. Los resultados muestran una respuesta altamente estable, con variaciones pico a pico muy reducidas (entre 2 y 3 unidades), lo que confirma la buena calidad de la medición y la baja influencia del ruido en el sistema.

Posteriormente, se realizaron ensayos fijando la distancia y variando el valor del PWM. Estos experimentos permitieron observar cómo el campo magnético generado por la bobina afecta directamente la lectura del sensor. A medida que aumenta el PWM, la señal media disminuye, lo cual es coherente con el incremento del campo magnético que compensa el peso del objeto. Sin embargo, también se observa un aumento significativo en la variación pico a pico, lo que indica una mayor inestabilidad del sistema a medida que se incrementa la acción del actuador.

Un aspecto clave identificado durante las pruebas es la existencia de umbrales de inestabilidad. Por ejemplo, a una distancia de 18.4 mm, el sistema permanece estable hasta aproximadamente PWM = 40, mientras que a PWM = 60 el objeto se vuelve inestable y “sale disparado”. Este comportamiento se repite a otras distancias, como en 24.8 mm, donde el sistema tolera hasta PWM = 80 antes de volverse inestable. Esto evidencia la naturaleza altamente no lineal del sistema y la necesidad de un ajuste cuidadoso de los parámetros de control.

En conjunto, estos ensayos permitieron no solo validar el funcionamiento del sistema, sino también obtener datos fundamentales para la implementación del control. La construcción de estas tablas experimentales resulta esencial para comprender las zonas de operación estable, identificar límites físicos del sistema y ajustar correctamente los parámetros del controlador, sentando así las bases para lograr una levitación precisa y estable.

Tablas de calibración:

Muestras de cada prueba = 1000

——————————

PWM = 0

Distancia fija = 18.4 mm

Distancia fija = 24.8 mm

Código

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SH110X.h>

/* =========================================================

   PINES

   ========================================================= */

const int HALL_PIN    = A0;

const int POT_SP_PIN  = A1;

const int POT_PWR_PIN = A2;

const int POT_STB_PIN = A3;

const int BUTTON_PIN  = 5;

const int PWM_PIN     = 9;

/* =========================================================

   OLED

   ========================================================= */

Adafruit_SH1106G display(128, 64, &Wire, -1);

const uint8_t OLED_ADDR = 0x3C;

/* =========================================================

   CONSTANTES

   ========================================================= */

const int HALL_OVERSAMPLE = 8;

const int POT_OVERSAMPLE = 4;

const unsigned long CONTROL_PERIOD_US = 2000;

const unsigned long UI_PERIOD_MS      = 200;

const unsigned long POT_PERIOD_MS     = 40;

const int HALL_MIN_SAFE = 20;

const int HALL_MAX_SAFE = 490;

const int PWM_MIN = 0;

const int PWM_MAX = 180;

const float MAX_STEP = 4.0;

const float HALL_ALPHA    = 0.22;

const float KP = 0.33;

/* =========================================================

   ESTADO DEL CONTROL

   ========================================================= */

float hallFilt = 0.0;

float hallPrev = 0.0;

float setpoint = 365.0;

float setpointFilt = 365.0;

float Kd = 9.0;

float pwmBase = 90.0;

float pwmCmd  = 0.0;

bool armed = false;

/* =========================================================

   BOTÓN

   ========================================================= */

bool lastButtonState = HIGH;

unsigned long lastDebounceMs = 0;

const unsigned long DEBOUNCE_MS = 40;

/* =========================================================

   TIEMPOS

   ========================================================= */

unsigned long lastControlUs = 0;

unsigned long lastUiMs = 0;

unsigned long lastPotMs = 0;

/* =========================================================

   UI CACHE

   ========================================================= */

int potSpRaw  = 0;

int potPwrRaw = 0;

int potStbRaw = 0;

int lastArmed  = -1;

int lastSp     = -1;

int lastPwmB   = -1;

int lastKd10   = -1;

bool forceRedraw = true;

/* =========================================================

   UTILIDADES

   ========================================================= */

int readAnalogAvg(int pin, int n) {

  unsigned long acc = 0;

  for (int i = 0; i < n; i++) acc += analogRead(pin);

  return acc / n;

}

/* =========================================================

   CONTROL

   ========================================================= */

void disarmControl() {

  armed = false;

  pwmCmd = 0;

  analogWrite(PWM_PIN, 0);

  forceRedraw = true;

}

void armControl() {

  if (hallFilt >= HALL_MIN_SAFE && hallFilt <= HALL_MAX_SAFE){

    armed = true;

    pwmCmd = pwmBase;

    hallPrev = hallFilt;

    forceRedraw = true;

  }

}

void updateControl() {

  int hallRaw = readAnalogAvg(HALL_PIN,HALL_OVERSAMPLE);

  hallFilt = (1.0 – HALL_ALPHA) * hallFilt + HALL_ALPHA * hallRaw;

  if (!armed) {

    analogWrite(PWM_PIN, 0);

  }else if(hallFilt >=  HALL_MIN_SAFE && hallFilt <= HALL_MAX_SAFE){

    float error = hallFilt – setpoint;

    float vel   = hallFilt – hallPrev;

    float target = pwmBase + KP * error + Kd * vel;

    target = constrain(target, PWM_MIN, PWM_MAX);

    float delta = constrain(target – pwmCmd, -MAX_STEP, MAX_STEP);

    pwmCmd = constrain(pwmCmd + delta, PWM_MIN, PWM_MAX);

    analogWrite(PWM_PIN, (int)(pwmCmd + 0.5f));

  }else{

    disarmControl();

  }

  hallPrev = hallFilt;

}

/* =========================================================

   POTENCIÓMETROS

   ========================================================= */

void updatePots() {

  potSpRaw  = readAnalogAvg(POT_SP_PIN, POT_OVERSAMPLE);

  potPwrRaw = readAnalogAvg(POT_PWR_PIN, POT_OVERSAMPLE);

  potStbRaw = readAnalogAvg(POT_STB_PIN, POT_OVERSAMPLE);

  float sp = map(potSpRaw, 0, 1023, 3000, 4200) / 10.0;

  setpointFilt = 0.6 * setpointFilt + 0.4 * sp;

  setpoint = setpointFilt;

  pwmBase = map(potPwrRaw, 0, 1023, 850, 1000) / 10.0;

  Kd      = map(potStbRaw, 0, 1023, 80, 105) / 10.0;

}

/* =========================================================

   BOTÓN

   ========================================================= */

bool buttonPressed() {

  bool pressed = (digitalRead(BUTTON_PIN) == LOW);

  bool result = false;

  if (pressed && !lastButtonState && (millis() – lastDebounceMs > DEBOUNCE_MS)) {

    lastDebounceMs = millis();

    result = true;

  }

  lastButtonState = pressed;

  return result;

}

/* =========================================================

   UI

   ========================================================= */

void drawBar(int x, int y, int w, int h, int value) {

  int fill = map(value, 0, 1023, 0, w);

  fill = constrain(fill, 0, w);

  display.drawRect(x, y, w, h, SH110X_WHITE);

  if (fill > 2)

    display.fillRect(x + 1, y + 1, fill – 2, h – 2, SH110X_WHITE);

}

bool uiNeedsRedraw() {

  return (forceRedraw || lastArmed != armed || lastSp != (int)setpoint

  || lastPwmB != (int)pwmBase || lastKd10 != (int)(Kd * 10));

}

void drawUI() {

  display.clearDisplay();

  display.setTextSize(1);

  display.setTextColor(SH110X_WHITE);

  display.setCursor(0, 0);

  display.print(armed ? F(«ON») : F(«OFF»));

  display.setCursor(40, 0);

  display.print(armed ? F(«NOT EDITABLE») : F(«EDITABLE»));

  display.setCursor(0, 14);

  display.print(F(«P1 SP «));

  display.print((int)setpoint);

  drawBar(64, 14, 60, 8, potSpRaw);

  display.setCursor(0, 30);

  display.print(F(«P2 PB «));

  display.print((int)pwmBase);

  drawBar(64, 30, 60, 8, potPwrRaw);

  display.setCursor(0, 46);

  display.print(F(«P3 KD «));

  display.print(Kd, 1);

  drawBar(64, 46, 60, 8, potStbRaw);

  display.display();

  lastArmed = armed;

  lastSp = (int)setpoint;

  lastPwmB = (int)pwmBase;

  lastKd10 = (int)(Kd * 10);

  forceRedraw = false;

}

/* =========================================================

   SETUP

   ========================================================= */

void setup() {

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pinMode(PWM_PIN, OUTPUT);

  analogWrite(PWM_PIN, 0);

  Wire.begin();

  display.begin(OLED_ADDR, true);

  hallFilt = readAnalogAvg(HALL_PIN,HALL_OVERSAMPLE);

  hallPrev = hallFilt;

  updatePots();

  drawUI();

}

/* =========================================================

   LOOP

   ========================================================= */

void loop() {

  if (buttonPressed()) {

    armed ? disarmControl() : armControl();

  }

  unsigned long nowUs = micros();

  if (nowUs – lastControlUs >= CONTROL_PERIOD_US) {

    lastControlUs += CONTROL_PERIOD_US;

    updateControl();

  }

  unsigned long nowMs = millis();

  if (!armed){

    if(nowMs – lastPotMs >= POT_PERIOD_MS){

      lastPotMs = nowMs;

      updatePots();

    }

    if(nowMs – lastUiMs >= UI_PERIOD_MS){

      lastUiMs = nowMs;

      if (uiNeedsRedraw()) drawUI();

    }

  }else if(forceRedraw) drawUI();

}

Problemas encontrados

-Elegir los componentes adecuados para el proyecto. Al final, se ha sobreestimado la cantidad de componentes necesarios.

-La protoboard no era la plataforma adecuada para nuestro HW, ya que no era lo suficientemente estable para las exigencias del sistema, por lo que hemos tenido que pasar a soldadura.

– Una vez montado todo ha habido que resoldar algunas piezas varias veces hasta dar con el punto

– Una vez todo soldado y montado ha habido que estar varios días enteros hasta conseguir las configuraciones correctas y optimas de los parámetros para mantener el sistema funcional y estable a lo largo del tiempo.

– La subestimación de la dificultad del proyecto ha llevado a invertir tiempo extra en perfeccionarlo, aunque no ha supuesto en una extensión respecto al periodo de entrega.

Costes y materiales

MaterialPrecio totalCantidadPrecio unidad(euros)
Bobina22.59122.59
Placa Nano12.9926.495
Cargador 12.6V15.59  
Regulador de Voltaje lm2596s7.9932.663
Jack DC Hembra Switch(Alimentación)   
Sensor Efecto Campo SS495A19.8029.90
MOSFET IRLZ44N7.6551.53
Controlador MOSFET TC44274.5931.53
Diodo Schottky de potencia SB51003.25100.325
Condensadores de cerámica multicapa (MLCC), con alimentación K104K15X7RF5UL21.80200.09
Condensadores de cerámica multicapa (MLCC), con alimentación 1C10X7R104K100B2.14200.107
4 IMAN DISCO NEODIMIO 19X3MM 10KG 818758408.0918.09
Placa de soldadura EPLZON «x2.05» y 3.5 «x2.0 5»  10.99110.99
Pantalla LCD OLED13.9926.995
Potenciómetro B5K B10K B20K B50K B100K Ohm GTIWUNG8.991 kit de 20 pot.8.99
MASILLA MADERA AL AGUA3.05  13.05
SPRAY MATE ECONOMICO 400ML NEGRO3.42  13.42
Total146.92  

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *