Fabricación inteligente

Monitorización del tiempo de ciclo con IoT: de las señales del PLC al análisis del cuello de botella

Mide cada ciclo en lugar de muestrear con un cronómetro. Captura las señales de ciclo con un ESP32, publícalas por MQTT y calcula estadísticas móviles que revelan el verdadero cuello de botella de una línea.

Gráfico de monitorización del tiempo de ciclo que compara los tiempos de ciclo de cada puesto con el takt time y resalta el cuello de botella, alimentado con datos MQTT de la línea
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

La monitorización del tiempo de ciclo sustituye el estudio con cronómetro por la medición de cada ciclo, en cada puesto, durante todo el día. En este tutorial capturarás señales de ciclo desde una salida de PLC o un sensor con un ESP32, publicarás cada ciclo por MQTT y ejecutarás un pequeño servicio de análisis que calcula estadísticas móviles por puesto e identifica el cuello de botella de la línea frente al takt time.

Tiempo de ciclo, takt time y por qué importa la diferencia

Tres términos se confunden a menudo:

Término Definición Fuente
Takt time Tiempo de producción disponible dividido entre la demanda del cliente; el ritmo que debe alcanzar la línea Planificación
Tiempo de ciclo ideal (de diseño) Tiempo más rápido posible para un ciclo en condiciones ideales Especificación de la máquina, ingeniería de procesos
Tiempo de ciclo real Tiempo medido entre dos finalizaciones de ciclo consecutivas en un puesto Medición

Cuando el tiempo de ciclo real de un puesto es mayor que el takt, la línea no puede cubrir la demanda sin horas extra. Cuando es mayor que el tiempo de ciclo ideal, la diferencia es una pérdida de rendimiento en términos de OEE. Un estudio con cronómetro da una instantánea de unas pocas decenas de ciclos; la monitorización continua muestra la distribución completa, incluidos los ciclos lentos que ocurren al inicio del turno, tras los cambios de herramienta o con determinados operarios y materiales.

Qué señal usar

Elige una señal que marque el mismo punto de cada ciclo:

  1. Salida "ciclo completado" del PLC o un bit que puedas asignar a una salida libre. La opción más precisa.
  2. Sensor de presencia de pieza a la salida del puesto, como un sensor fotoeléctrico o inductivo.
  3. Señal de amarre o de puerta en máquinas de carga manual, si una secuencia de apertura/cierre equivale a un ciclo.
  4. Firma de corriente de un husillo o una prensa, como último recurso cuando no existe una señal discreta.

Todas las señales de 24 V deben llegar al microcontrolador a través de un optoacoplador o de un módulo de entrada con adaptación de nivel adecuado, nunca directamente.

Diseño de topics

Un mensaje por ciclo mantiene intactos los datos en bruto y permite a cualquier consumidor calcular sus propias estadísticas:

connectmq/plant1/assembly/line1/st-10/cycle       {"seq":1532,"ct_ms":41250,"ts":1760600000}
connectmq/plant1/assembly/line1/st-20/cycle       ...
connectmq/plant1/assembly/line1/cycle-stats       retained summary for all stations

El número de secuencia permite a los consumidores detectar mensajes perdidos, y ct_ms se calcula en el dispositivo con su propio reloj, de modo que la latencia de red no distorsiona la medición.

Capturar ciclos con un ESP32

El sketch siguiente usa una interrupción de hardware para marcar cada flanco de subida con millis(), calcula el tiempo desde el flanco anterior y publica un mensaje JSON por ciclo con PubSubClient. Instala PubSubClient desde el gestor de librerías de Arduino. Si eres nuevo con la placa, el tutorial de ESP32 con MQTT cubre el entorno de desarrollo.

#include <WiFi.h>
#include <PubSubClient.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* BROKER    = "broker.connectmq.com";
const char* STATION   = "st-10";
const char* TOPIC     = "connectmq/demo/ctm-3k8p/line1/st-10/cycle";

const int CYCLE_PIN = 4;                   // optocoupler output, active LOW
const unsigned long MIN_CYCLE_MS = 2000;   // ignore bounces and double pulses

WiFiClient net;
PubSubClient mqtt(net);

volatile unsigned long lastEdge = 0;
volatile unsigned long pendingCt = 0;
volatile bool havePending = false;
unsigned long seq = 0;

void IRAM_ATTR onCycle() {
  unsigned long now = millis();
  unsigned long ct = now - lastEdge;
  if (ct < MIN_CYCLE_MS) return;
  if (lastEdge != 0) {
    pendingCt = ct;
    havePending = true;
  }
  lastEdge = now;
}

void connectMqtt() {
  while (!mqtt.connected()) {
    String id = String("ctm-3k8p-") + STATION;
    if (mqtt.connect(id.c_str())) {
      Serial.println("MQTT connected");
    } else {
      Serial.printf("MQTT failed, rc=%d\n", mqtt.state());
      delay(2000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(CYCLE_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(CYCLE_PIN), onCycle, FALLING);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);
  mqtt.setServer(BROKER, 1883);
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  if (havePending) {
    noInterrupts();
    unsigned long ct = pendingCt;
    havePending = false;
    interrupts();

    char payload[96];
    snprintf(payload, sizeof(payload), "{\"seq\":%lu,\"ct_ms\":%lu,\"up_ms\":%lu}",
             ++seq, ct, millis());
    mqtt.publish(TOPIC, payload);
    Serial.println(payload);
  }
}

La protección MIN_CYCLE_MS filtra los rebotes de contacto y los pulsos dobles; ajústala aproximadamente a la mitad del ciclo realista más rápido. El primer flanco tras el arranque solo pone en marcha el reloj, así que no se informa de un ciclo largo falso. PubSubClient publica con QoS 0; si necesitas entrega QoS 1 para cada ciclo, AsyncMqttClient lo admite, o puedes almacenar los ciclos y publicarlos por lotes. Para producción, cambia a WiFiClientSecure, el puerto 8883 y las credenciales que emita tu broker.

Analizar los datos de monitorización del tiempo de ciclo

El servicio de análisis se suscribe a todos los puestos, guarda en memoria los últimos 200 ciclos de cada uno y publica un resumen retenido cada 30 segundos. Usar la mediana y el percentil 90 en lugar de la media evita que los ciclos ocasionales muy largos (que en realidad son microparadas) distorsionen la imagen.

#!/usr/bin/env python3
"""Rolling cycle time statistics and bottleneck detection."""
import json
import statistics
import threading
import time
from collections import defaultdict, deque

import paho.mqtt.client as mqtt

PREFIX = "connectmq/demo/ctm-3k8p/line1"
TAKT_MS = 45_000
STOP_THRESHOLD_MS = 3 * TAKT_MS        # longer gaps are short stops, not cycles

cycles = defaultdict(lambda: deque(maxlen=200))
lock = threading.Lock()


def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(f"{PREFIX}/+/cycle", qos=1)


def on_message(client, userdata, msg):
    station = msg.topic.split("/")[-2]
    ct = json.loads(msg.payload)["ct_ms"]
    if ct <= STOP_THRESHOLD_MS:
        with lock:
            cycles[station].append(ct)


def p90(values):
    return statistics.quantiles(values, n=10)[-1] if len(values) >= 10 else max(values)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="ctm-stats-3k8p")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()

while True:
    time.sleep(30)
    with lock:
        stats = {
            st: {"n": len(v), "median_ms": int(statistics.median(v)), "p90_ms": int(p90(v))}
            for st, v in cycles.items() if v
        }
    if not stats:
        continue
    bottleneck = max(stats, key=lambda s: stats[s]["median_ms"])
    summary = {
        "ts": int(time.time()),
        "takt_ms": TAKT_MS,
        "bottleneck": bottleneck,
        "over_takt": [s for s, v in stats.items() if v["median_ms"] > TAKT_MS],
        "stations": stats,
    }
    client.publish(f"{PREFIX}/cycle-stats", json.dumps(summary), qos=1, retain=True)
    print(json.dumps(summary, indent=2))

Puedes probarlo sin hardware publicando ciclos sintéticos:

for i in $(seq 1 20); do
  mosquitto_pub -h broker.connectmq.com -p 1883 -i ctm-sim-$RANDOM \
    -t connectmq/demo/ctm-3k8p/line1/st-20/cycle \
    -m "{\"seq\":$i,\"ct_ms\":$((44000 + RANDOM % 6000))}"
  sleep 1
done

Ten en cuenta que el servidor MQTT público gratuito admite 10 mensajes por segundo por cliente, mucho más de lo que necesita cualquier puesto real. Todo lo que se publica allí es legible públicamente, así que úsalo solo para pruebas y datos sintéticos.

Interpretar los resultados

Observación Significado probable Siguiente paso
Mediana por encima del takt en un puesto Cuello de botella real Reequilibrar el contenido de trabajo, añadir capacidad, reducir la preparación
Mediana por debajo del takt pero p90 muy por encima Inestabilidad, no falta de capacidad Buscar causas de ciclos lentos en material, operarios o calidad
Todos los puestos cerca del takt y la línea sigue sin cumplir Microparadas o bloqueos/falta de alimentación Combinar con estados de máquina y niveles de buffer
El tiempo de ciclo sube poco a poco durante semanas Desgaste de herramientas, equipo que se degrada Correlacionar con los datos de mantenimiento
Gran diferencia entre turnos Diferencias de método o de dotación Estandarizar el trabajo, formar

El puesto cuello de botella es donde la mejora compensa para toda la línea; mejorar cualquier otro puesto solo genera inventario. Vigila si el cuello de botella se desplaza tras los cambios, lo que indica una línea equilibrada.

Desplegar la monitorización del tiempo de ciclo en una línea

Empieza por el puesto que todos sospechan que es el cuello de botella y por un puesto a cada lado. Tres puestos bastan para ver si los ciclos lentos se deben al propio puesto o a la falta de alimentación y los bloqueos de sus vecinos. Una vez validadas la elección de la señal y la configuración antirrebote, copia el mismo sketch en los demás puestos y cambia solo el nombre del puesto y el topic.

Unas cuantas reglas prácticas facilitan el despliegue:

  1. Nombra los puestos exactamente como en el layout de la línea. Operarios e ingenieros deben reconocer st-20 en el dashboard sin una tabla de equivalencias.
  2. Acuerda el punto de ciclo de cada puesto. Si un puesto cuenta a la entrada de la pieza y el siguiente a la salida, las cifras no son comparables.
  3. Registra el takt time con los datos. El takt cambia cuando cambia la demanda; guardarlo en el resumen mantiene interpretables los informes antiguos.
  4. Comprueba los relojes de los dispositivos. ct_ms procede del temporizador del propio dispositivo y es fiable, pero cualquier marca de tiempo que añadas para correlacionar necesita gateways sincronizados por NTP.
  5. Muestra los datos en la línea. Una pantalla con la mediana en directo de cada puesto frente al takt inicia más conversaciones de mejora que un informe semanal.

En el portal de ConnectMQ, el explorador de topics en directo es una forma rápida de confirmar durante la puesta en marcha que todos los puestos publican, y una regla de alerta por umbral sobre el median_ms de un puesto registra cada vez que supera el takt.

Conexión con otras métricas

Los datos de ciclo son la pata de rendimiento del OEE. Combinados con el tiempo de funcionamiento de la monitorización del estado de las máquinas y los recuentos de piezas buenas del conteo de producción con IoT, tienes todo lo necesario para calcular el OEE. Mantenlo todo en un único broker y una única jerarquía de topics, y cada nuevo KPI se convierte en un suscriptor en lugar de en un nuevo proyecto de integración. Si quieres entender qué hace un broker MQTT como centro de este diseño, empieza por ahí; la guía de fabricación inteligente lo sitúa en la hoja de ruta general, y los tiempos de ciclo por puesto también pueden visualizarse sobre un gemelo digital 3D de la línea.

Empieza con ConnectMQ

Una línea con decenas de puestos que publican cada ciclo necesita un broker siempre accesible. ConnectMQ es un broker MQTT en la nube y un broker MQTT gestionado con TLS, MQTT 5, retained messages, un explorador de topics en directo y alertas por umbral sobre campos como median_ms. Crea un broker Serverless gratuito, sin tarjeta de crédito, y conecta tu primer puesto.

Preguntas frecuentes

¿Cuál es la diferencia entre tiempo de ciclo y takt time?

El takt time es el ritmo que exige la demanda del cliente, calculado a partir del tiempo disponible y la demanda. El tiempo de ciclo es lo que tarda realmente un puesto por unidad. Un puesto cuyo tiempo de ciclo supera el takt limita la producción de la línea.

¿Debo usar el tiempo de ciclo medio o la mediana?

Usa la mediana para el ciclo típico y un percentil alto, como el p90, para la variabilidad. La media se distorsiona fácilmente con unos pocos ciclos largos que en realidad son microparadas.

¿Puedo medir el tiempo de ciclo sin acceso al PLC?

Sí. Un sensor fotoeléctrico a la salida del puesto, una señal de puerta o de amarre, o la firma de corriente del accionamiento principal pueden marcar cada ciclo. Las señales del PLC son las más precisas cuando están disponibles.

¿Cómo gestiono los huecos muy largos entre ciclos?

Trata los huecos mucho más largos que el takt como paradas y no como ciclos. Exclúyelos de las estadísticas de ciclo y regístralos como tiempo de parada usando los datos de estado de las máquinas.

Artículos relacionados

OEE vs TEEP vs OOE: ¿qué indicador deberías usar?

OEE, OOE y TEEP comparten los mismos factores de rendimiento y calidad, pero se miden frente a bases de tiempo distintas. Esta comparativa resuelve un único ejemplo y enumera las señales MQTT que necesita cada indicador.

8 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.