pcall en Dobot Lua : une gestion des erreurs qui n'arrête pas votre cellule robotisée

Une erreur Lua non gérée termine le programme du robot, immédiatement : la cellule s'arrête, la pièce reste coincée dans le préhenseur et l'équipe appelle la maintenance. Lua fournit pourtant un outil qui empêche exactement cela : pcall. À travers des exemples DobotStudio, cet article montre comment protéger les commandes de mouvement, réessayer les erreurs de façon délibérée, définir des objets d'erreur personnalisés et rendre les défauts visibles de manière centralisée, pour qu'une erreur d'exécution ne devienne pas un arrêt de production.

Pourquoi la gestion des erreurs fonctionne autrement dans un programme robot

Dans une application de bureau, un processus qui plante est agaçant. Dans une cellule robotisée, cela signifie : le programme s'interrompt en plein cycle, les axes s'arrêtent dans une position indéfinie, la pièce et le préhenseur sont dans un état que personne n'a prévu. Le redémarrage ne coûte pas que des minutes, il faut souvent une personne pour dégager le robot à la main.

Sources d'erreur typiques au quotidien avec un Dobot : une pièce posée de travers et le mouvement bute sur un fin de course, une connexion TCP vers le système de caméra se coupe, un fichier sur la carte SD ne s'ouvre pas, un capteur renvoie une valeur inattendue. Rien de tout cela ne doit terminer le programme si l'erreur est interceptée au bon endroit.

pcall en 60 secondes

pcall signifie « protected call » : la fonction transmise est exécutée, mais une erreur en son sein ne termine pas le programme. À la place, pcall renvoie deux valeurs : un indicateur de succès et, en cas d'échec, le message d'erreur.

-- Schéma de base : appel protégé
local ok, err = pcall(function()
    fonction_risquee()
end)

if not ok then
    print("Erreur interceptée : " .. tostring(err))
    -- le programme continue, c'est nous qui décidons de la suite
end

Protéger les commandes de mouvement

Le point d'entrée le plus pratique est un wrapper autour des commandes de mouvement. Au lieu d'appeler MovJ et MovL directement, encapsulez-les dans une fonction qui intercepte les erreurs, les journalise et ramène la cellule dans un état défini :

-- Global.lua : mouvement protégé avec journal
function mouvement_sur(nom, mouvement)
    local ok, err = pcall(mouvement)
    if not ok then
        log_local("ERROR", "Mouvement '" .. nom .. "' échoué : " .. tostring(err))
        DO(PREHENSEUR, OFF)   -- déposer la pièce de façon contrôlée
        Sync()
        return false
    end
    return true
end

-- Utilisation dans le cycle
if not mouvement_sur("Approche dépôt", function() MovJ(P_DEPOT) end) then
    return  -- terminer le cycle proprement au lieu d'interrompre en plein mouvement
end

Réessayer avec mesure : des réessais bornés

Toute erreur n'est pas définitive. Une connexion réseau coupée vers le système de caméra ou un capteur qui rebondit brièvement revient souvent une seconde plus tard. Ces cas justifient un réessai, mais toujours avec une borne supérieure et un temps d'attente, sinon le programme martèle sans retenue en cas d'échec :

-- Réessai borné : 3 tentatives max., 1 s de pause
function avec_reessai(nom, tentatives, pause_ms, fn)
    for tentative = 1, tentatives do
        local ok, err = pcall(fn)
        if ok then return true end
        log_local("WARN", nom .. " tentative " .. tentative .. "/" .. tentatives
            .. " échouée : " .. tostring(err))
        Wait(pause_ms)
    end
    log_local("ERROR", nom .. " définitivement échoué après " .. tentatives .. " tentatives")
    return false
end

-- Exemple : déclenchement caméra via TCP, tolère les coupures brèves
local ok = avec_reessai("Déclenchement caméra", 3, 1000, function()
    local socket = TCPCreate(false, CAMERA_IP, CAMERA_PORT)
    TCPStart(socket, 0)
    TCPWrite(socket, "TRIGGER")
    TCPDestroy(socket)
end)

Utiliser error() à bon escient : des objets d'erreur personnalisés

pcall n'intercepte pas seulement les erreurs d'exécution, mais aussi tout ce que vous levez vous-même avec error(). Cela rend les contrôles de plausibilité élégants : le contrôle lève, le gestionnaire central décide. Une table est admise comme valeur d'erreur ; l'erreur transporte alors une information structurée au lieu d'un simple texte :

-- Le contrôle lève un objet d'erreur structuré
function controle_piece()
    if DI(CAPTEUR_PIECE) ~= ON then
        error({ code = "PAS_DE_PIECE", text = "Aucune pièce en position" })
    end
end

local ok, err = pcall(controle_piece)
if not ok then
    if type(err) == "table" and err.code == "PAS_DE_PIECE" then
        -- cas attendu : alimentation vide, attendre le réapprovisionnement
        log_local("WARN", err.text)
        attendre_reapprovisionnement()
    else
        -- tout le reste est une vraie erreur
        log_local("ERROR", tostring(type(err) == "table" and err.text or err))
        retour_position_initiale()
    end
end

Rendre les erreurs visibles : journalisation centrale avec Leif

Les erreurs interceptées qui n'atterrissent que sur la console du robot, personne ne les voit. Seule la journalisation centrale transforme la gestion des erreurs en système d'alerte précoce : si les réessais s'accumulent sur un poste, un problème mécanique s'annonce bien avant l'arrêt réel de la cellule. L'article Journalisation avec Dobot Lua montre pas à pas comment étendre la fonction log_local de ces exemples pour envoyer les messages vers Leif. Dans Leif, vous voyez alors en direct tous les messages WARN et ERROR de vos robots, avec alertes vers Microsoft Teams, et la chronologie des incidents rejoue pour chaque message l'image caméra et la ligne de programme.

Liste de contrôle pour des programmes Dobot robustes

  • Faire passer chaque commande de mouvement par un wrapper pcall, jamais à nu dans le cycle
  • En cas d'erreur, amener d'abord préhenseur et pièce dans un état défini
  • Des réessais toujours bornés avec temps d'attente, puis passage ordonné à l'état sûr
  • Séparer les états attendus (alimentation vide, porte ouverte) des vraies erreurs avec des objets error()
  • Journaliser chaque erreur interceptée, WARN pour les réessayables, ERROR pour les définitives
  • Collecter les messages de façon centralisée pour repérer les accumulations avant l'arrêt de la cellule

Questions fréquentes

Que fait exactement pcall en Lua ?

pcall exécute une fonction en mode protégé. Si une erreur s'y produit, le programme ne s'interrompt pas ; pcall renvoie false et le message d'erreur. Sans erreur, il renvoie true ainsi que les valeurs de retour de la fonction.

Quand utiliser xpcall plutôt que pcall ?

xpcall accepte en plus votre propre gestionnaire d'erreur, qui s'exécute au moment même de l'erreur et peut par exemple capturer une trace d'appels avec debug.traceback. Pour la plupart des programmes robots, pcall suffit ; xpcall vaut la peine quand vous voulez savoir exactement où cela a cassé.

pcall ralentit-il le programme du robot ?

Le surcoût d'un appel pcall est négligeable face à un mouvement de robot de l'ordre de la milliseconde. Dans des boucles serrées sans mouvements, n'enveloppez pas chaque micro-opération individuellement ; enveloppez des blocs cohérents.

Parlons-en.

Premier entretien gratuit : nous revenons vers vous au plus vite.