O monitoramento de tempo de ciclo substitui a cronoanálise por uma medição de cada ciclo, em cada estação, o dia inteiro. Neste tutorial você vai capturar sinais de ciclo a partir de uma saída de CLP ou de um sensor com um ESP32, publicar cada ciclo via MQTT e executar um pequeno serviço de análise que calcula estatísticas móveis por estação e identifica o gargalo da linha em relação ao takt time.
Tempo de ciclo, takt time e por que a diferença importa
Três termos costumam ser confundidos:
| Termo | Definição | Origem |
|---|---|---|
| Takt time | Tempo disponível de produção dividido pela demanda do cliente; o ritmo que a linha precisa atingir | Planejamento |
| Tempo de ciclo ideal (de projeto) | Tempo mais rápido possível para um ciclo em condições ideais | Especificação da máquina, engenharia de processos |
| Tempo de ciclo real | Tempo medido entre conclusões consecutivas de ciclo em uma estação | Medição |
Quando o tempo de ciclo real de uma estação é maior que o takt, a linha não consegue atender à demanda sem horas extras. Quando ele é maior que o tempo de ciclo ideal, a diferença é uma perda de desempenho em termos de OEE. Uma cronoanálise dá um retrato de algumas dezenas de ciclos; o monitoramento contínuo mostra a distribuição completa, incluindo os ciclos lentos que acontecem no início do turno, depois de trocas de ferramenta ou com certos operadores e materiais.
Qual sinal usar
Escolha um sinal que marque o mesmo ponto de todos os ciclos:
- Saída de "ciclo concluído" do CLP, ou um bit que você possa mapear para uma saída livre. A opção mais precisa.
- Sensor de presença de peça na saída da estação, como um sensor fotoelétrico ou indutivo.
- Sinal de fixação ou de porta em máquinas carregadas manualmente, se uma sequência de abrir/fechar equivale a um ciclo.
- Assinatura de corrente de um spindle ou prensa, como último recurso quando não existe nenhum sinal discreto.
Todos os sinais de 24 V precisam chegar ao microcontrolador por meio de um optoacoplador ou de um módulo de entrada com conversão de nível adequada, nunca diretamente.
Projeto de topics
Uma mensagem por ciclo mantém os dados brutos intactos e permite que qualquer consumidor calcule suas próprias estatí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
O número de sequência permite que os consumidores detectem mensagens perdidas, e ct_ms é calculado no dispositivo a partir do seu próprio relógio, para que a latência de rede não distorça a medição.
Capturando ciclos em um ESP32
O sketch abaixo usa uma interrupção de hardware para registrar cada borda de subida com millis(), calcula o tempo desde a borda anterior e publica uma mensagem JSON por ciclo com o PubSubClient. Instale o PubSubClient pelo Gerenciador de Bibliotecas do Arduino. Se você é novo na placa, o tutorial de MQTT com ESP32 cobre o conjunto de ferramentas.
#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; // saída do optoacoplador, ativa em LOW
const unsigned long MIN_CYCLE_MS = 2000; // ignora repiques e pulsos duplos
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);
}
}
A proteção MIN_CYCLE_MS filtra repiques de contato e pulsos duplos; defina-a em aproximadamente metade do ciclo realista mais rápido. A primeira borda após a inicialização apenas dispara o relógio, então nenhum ciclo longo falso é reportado. O PubSubClient publica em QoS 0; se você precisa de entrega QoS 1 para cada ciclo, o AsyncMqttClient oferece isso, ou você pode acumular ciclos e publicá-los em lotes. Para produção, troque para WiFiClientSecure, porta 8883 e as credenciais emitidas pelo seu broker.
Analisando os dados de monitoramento de tempo de ciclo
O serviço de análise assina todas as estações, mantém os últimos 200 ciclos por estação na memória e publica um resumo retido a cada 30 segundos. Usar a mediana e o percentil 90 em vez da média evita que ciclos ocasionais muito longos (que na verdade são pequenas paradas) distorçam o quadro.
#!/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 # intervalos maiores são pequenas paradas, não ciclos
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))
Você pode testar sem 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
Observe que o servidor MQTT público permite 10 mensagens por segundo por client, muito mais do que qualquer estação real precisa. Tudo o que é publicado ali pode ser lido publicamente, então use-o apenas para testes e dados sintéticos.
Interpretando os resultados
| Observação | Significado provável | Próximo passo |
|---|---|---|
| Mediana acima do takt em uma estação | Gargalo real | Rebalancear o conteúdo de trabalho, adicionar capacidade, reduzir setup |
| Mediana abaixo do takt, mas p90 muito acima | Instabilidade, não capacidade | Procurar causas de ciclos lentos em material, operador ou qualidade |
| Todas as estações perto do takt, mas a linha ainda não atinge a produção | Pequenas paradas ou bloqueio/falta de alimentação | Combinar com estados de máquina e níveis de buffer |
| Tempo de ciclo subindo ao longo de semanas | Desgaste de ferramenta, degradação do equipamento | Correlacionar com dados de manutenção |
| Grande diferença entre turnos | Diferenças de método ou de quadro de pessoal | Padronizar o trabalho, treinar |
A estação gargalo é onde a melhoria compensa para a linha inteira; melhorar qualquer outra estação apenas acumula estoque. Fique atento ao gargalo mudando de lugar após as alterações, o que é sinal de uma linha balanceada.
Expandindo o monitoramento de tempo de ciclo para a linha inteira
Comece pela estação que todos suspeitam ser o gargalo e por uma estação de cada lado dela. Três estações bastam para ver se os ciclos lentos são causados pela própria estação ou por falta de alimentação e bloqueio vindos das vizinhas. Depois de comprovar a escolha do sinal e as configurações de debounce, copie o mesmo sketch para as estações restantes e altere apenas o nome da estação e o topic.
Algumas regras práticas facilitam a implantação:
- Nomeie as estações exatamente como no layout da linha. Operadores e engenheiros precisam reconhecer
st-20no dashboard sem consultar uma tabela. - Combine o ponto de ciclo de cada estação. Se uma estação conta na entrada da peça e a seguinte na saída, os números não são comparáveis.
- Registre o takt time junto com os dados. O takt muda quando a demanda muda; armazená-lo no resumo mantém os relatórios antigos interpretáveis.
- Verifique os relógios dos dispositivos.
ct_msvem do temporizador do próprio dispositivo e é confiável, mas qualquer timestamp adicionado para correlação precisa de gateways sincronizados por NTP. - Mostre os dados na linha. Uma tela com a mediana ao vivo por estação em relação ao takt inicia mais conversas de melhoria do que um relatório semanal.
No portal da ConnectMQ, o explorador de topics ao vivo é uma forma rápida de confirmar, durante o comissionamento, que todas as estações estão publicando, e uma regra de alerta por limite sobre o median_ms de uma estação registra cada vez que ele passa do takt.
Conectando a outras métricas
Os dados de ciclo são a perna de desempenho do OEE. Combinados com o tempo de operação do monitoramento de status de máquinas e com as contagens de peças boas da contagem de produção com IoT, você tem tudo o que precisa para calcular o OEE. Mantenha tudo em um único broker e em uma única hierarquia de topics, e cada novo KPI passa a ser um assinante, e não um novo projeto de integração. Se quiser entender o que um broker MQTT faz como o centro deste design, comece por aí; o guia de manufatura inteligente o coloca no roteiro mais amplo.
Comece com a ConnectMQ
Uma linha com dezenas de estações publicando cada ciclo precisa de um broker sempre acessível. A ConnectMQ é um broker MQTT na nuvem, um broker MQTT gerenciado com TLS, MQTT 5, retained messages, explorador de topics ao vivo e alertas por limite em campos como median_ms. Crie um broker Serverless gratuito, sem cartão de crédito, e conecte sua primeira estação.
Perguntas frequentes
Qual é a diferença entre tempo de ciclo e takt time?
O takt time é o ritmo exigido pela demanda do cliente, calculado a partir do tempo disponível e da demanda. O tempo de ciclo é quanto tempo uma estação realmente leva por unidade. Uma estação cujo tempo de ciclo excede o takt limita a produção da linha.
Devo usar a média ou a mediana do tempo de ciclo?
Use a mediana para o ciclo típico e um percentil alto, como o p90, para a variabilidade. A média é facilmente distorcida por alguns ciclos longos que, na verdade, são pequenas paradas.
Posso medir o tempo de ciclo sem acesso ao CLP?
Sim. Um sensor fotoelétrico na saída da estação, um sinal de porta ou de fixação, ou a assinatura de corrente do acionamento principal podem marcar cada ciclo. Os sinais do CLP são os mais precisos quando disponíveis.
Como trato intervalos muito longos entre ciclos?
Trate intervalos muito maiores que o takt como paradas, e não como ciclos. Exclua-os das estatísticas de ciclo e registre-os como tempo de parada usando os dados de estado das máquinas.



