{"id":10269,"date":"2026-08-15T01:00:10","date_gmt":"2026-08-14T23:00:10","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/werkzeuge\/frigate-coral-tpu-capacity-planner\/"},"modified":"2026-08-15T01:00:10","modified_gmt":"2026-08-14T23:00:10","slug":"frigate-coral-tpu-capacity-planner","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/","title":{"rendered":"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">Eine Coral-TPU schafft eine feste Zahl von Inferenzen je Sekunde. Jede Kamera beansprucht \u00fcber ihre Erkennungsbildrate ihren Anteil, und sobald die Summe das Budget \u00fcbersteigt, hinkt die Erkennung hinterher &#8211; nie laut, nur zu sp\u00e4t. Dieser Planer rechnet das vorher aus.<\/p>\n<div style=\"display: flex; gap: 12px; flex-wrap: wrap;\">\n<div style=\"flex: 1; min-width: 200px;\">\n            <label for=\"fg-detektor\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Detektor<\/label><br \/>\n            <select id=\"fg-detektor\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\"><option value=\"7\">Coral M.2 oder PCIe (rund 7 ms)<\/option><option value=\"10\" selected>Coral USB (rund 10 ms)<\/option><option value=\"100\">CPU ohne Beschleuniger (rund 100 ms)<\/option><\/select>\n        <\/div>\n<div style=\"flex: 1; min-width: 150px;\">\n            <label for=\"fg-anzahl\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Anzahl der Detektoren<\/label><br \/>\n            <input type=\"text\" id=\"fg-anzahl\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"1\">\n        <\/div>\n<div style=\"flex: 1; min-width: 150px;\">\n            <label for=\"fg-inferenz\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Inferenzzeit (ms)<\/label><br \/>\n            <input type=\"text\" id=\"fg-inferenz\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"10\">\n        <\/div>\n<div style=\"flex: 1; min-width: 150px;\">\n            <label for=\"fg-bewegung\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Anteil der Zeit mit Bewegung (%)<\/label><br \/>\n            <input type=\"text\" id=\"fg-bewegung\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" value=\"20\">\n        <\/div>\n<\/p><\/div>\n<div style=\"color: var(--text-secondary, #a0a0b0); font-size: 0.85rem; margin: 18px 0 8px;\">Kameras mit ihrem Erkennungsstrom<\/div>\n<div id=\"fg-liste\" style=\"margin-bottom: 12px;\"><\/div>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap;\">\n        <button id=\"fg-add\" class=\"button\" type=\"button\" style=\"background: transparent; color: var(--text-primary, #e8e8ee); border: 1px solid var(--border, #2a2a35); padding: 10px 18px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Kamera hinzuf\u00fcgen<\/button><br \/>\n        <button id=\"fg-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Kapazit\u00e4t planen<\/button>\n    <\/div>\n<div id=\"fg-ergebnis\" style=\"margin-top: 22px;\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"Eine Coral-TPU schafft eine feste Zahl von Inferenzen je Sekunde. Jede Kamera beansprucht \u00fcber ihre Erkennungsbildrate ihren Anteil, und sobald die Summe das Budget \u00fcbersteigt, hinkt die Erkennung hinterher - nie laut, nur zu sp\u00e4t. Dieser Planer rechnet das vorher aus.\",\"label_detektor\":\"Detektor\",\"detektor_m2\":\"Coral M.2 oder PCIe (rund 7 ms)\",\"detektor_usb\":\"Coral USB (rund 10 ms)\",\"detektor_cpu\":\"CPU ohne Beschleuniger (rund 100 ms)\",\"label_anzahl\":\"Anzahl der Detektoren\",\"label_inferenz\":\"Inferenzzeit (ms)\",\"label_bewegung\":\"Anteil der Zeit mit Bewegung (%)\",\"label_kameras\":\"Kameras mit ihrem Erkennungsstrom\",\"ph_name\":\"Name der Kamera\",\"knopf\":\"Kapazit\u00e4t planen\",\"knopf_kamera\":\"Kamera hinzuf\u00fcgen\",\"knopf_weg\":\"Entfernen\",\"fehler_inferenz\":\"Die Inferenzzeit muss eine Zahl \u00fcber null sein.\",\"fehler_anzahl\":\"Es muss mindestens einen Detektor geben.\",\"fehler_bewegung\":\"Der Anteil muss zwischen 0 und 100 liegen.\",\"fehler_fps\":\"Die Erkennungsbildrate von %s ist keine Zahl \u00fcber null.\",\"fehler_aufloesung\":\"Die Aufl\u00f6sung von %s lie\u00df sich nicht lesen.\",\"fehler_keine_kamera\":\"Es wurde keine Kamera eingetragen.\",\"h_auslastung\":\"Auslastung\",\"h_kameras\":\"Anteil je Kamera\",\"h_befunde\":\"Befunde\",\"h_yaml\":\"Ger\u00fcst f\u00fcr frigate.yml\",\"einheit_inferenzen\":\"Inferenzen je Sekunde\",\"z_kapazitaet\":\"Leistung der Detektoren\",\"z_bedarf\":\"Bedarf, wenn alle Kameras gleichzeitig melden\",\"z_auslastung\":\"Auslastung in diesem Fall\",\"z_bedarf_echt\":\"Bedarf beim angegebenen Bewegungsanteil\",\"z_auslastung_echt\":\"Auslastung in diesem Fall\",\"z_noetig\":\"N\u00f6tige Detektoren f\u00fcr den ung\u00fcnstigsten Fall\",\"b_ueberlastet\":\"Mit %s Prozent ist das Budget \u00fcberschritten. Es braucht %n Detektoren oder weniger Bilder je Sekunde.\",\"b_knapp\":\"%s Prozent lassen keine Luft. Ein einziges Ereignis \u00fcber alle Kameras gen\u00fcgt, damit die Erkennung nachhinkt.\",\"b_reicht\":\"%s Prozent Auslastung: das Budget tr\u00e4gt auch den Fall, dass alle Kameras zugleich Bewegung melden.\",\"b_bewegung\":\"Beim angegebenen Bewegungsanteil liegt die Alltagslast bei rund %s Prozent. Das ist die Zahl, die die Oberfl\u00e4che zeigt, nicht die, mit der geplant wird.\",\"b_fps\":\"Mehr als f\u00fcnf Erkennungsbilder je Sekunde bringen selten etwas: %s. Eine Person, die durchs Bild geht, wird bei f\u00fcnf erfasst, und jedes weitere Bild kostet Inferenzbudget.\",\"b_aufloesung\":\"Ein Erkennungsstrom \u00fcber 1280x720 kostet CPU beim Dekodieren, ohne der Erkennung zu helfen: %s. Das Modell skaliert ohnehin jedes Bild auf seine eigene Eingangsgr\u00f6\u00dfe herunter, die Inferenzzeit bleibt gleich.\",\"b_modell\":\"Die Inferenzzeit h\u00e4ngt nicht von der Erkennungsaufl\u00f6sung ab, sondern vom Modell. Ein gr\u00f6\u00dferes Bild kostet Dekodierung, keine TPU.\",\"b_cpu\":\"Ohne Beschleuniger dauert jede Inferenz zehnmal so lang, und eine Handvoll Kameras gen\u00fcgt, um die Maschine auszulasten.\",\"b_mehrere\":\"Frigate verteilt die Kameras von allein auf die Detektoren, eine Zuordnung von Hand ist nicht n\u00f6tig.\",\"b_substrom\":\"Der Erkennungsstrom geh\u00f6rt auf den Substrom der Kamera. Auf dem Aufnahmestrom zu erkennen verschwendet Dekodierleistung f\u00fcr jedes einzelne Bild.\"};<\/p>\n<p>    \/\/ --- reine Rechenlogik ---<\/p>\n<p>    function zahl(wert) {\n        const roh = String(wert === undefined || wert === null ? '' : wert).trim().replace(',', '.');\n        if (roh === '') { return NaN; }\n        if (!\/^[0-9]+(\\.[0-9]+)?$\/.test(roh)) { return NaN; }\n        return parseFloat(roh);\n    }<\/p>\n<p>    function runde(wert, stellen) {\n        const faktor = Math.pow(10, stellen === undefined ? 0 : stellen);\n        return Math.round(wert * faktor) \/ faktor;\n    }<\/p>\n<p>    function kennung(name) {\n        return String(name).toLowerCase()\n            .replace(\/[\u00e4\u00e0\u00e1\u00e2]\/g, 'a').replace(\/[\u00f6\u00f2\u00f3\u00f4]\/g, 'o').replace(\/[\u00fc\u00f9\u00fa\u00fb]\/g, 'u')\n            .replace(\/\u00df\/g, 'ss').replace(\/[^a-z0-9]+\/g, '_').replace(\/^_+|_+$\/g, '');\n    }<\/p>\n<p>    function planen(e) {\n        const ergebnis = { ok: false, fehler: '', kameras: [], befunde: [], yaml: '' };\n        const eingabe = e || {};<\/p>\n<p>        const inferenz = zahl(eingabe.inferenz);\n        const anzahl = zahl(eingabe.anzahl);\n        const bewegung = zahl(eingabe.bewegung);<\/p>\n<p>        if (!isFinite(inferenz) || inferenz <= 0) { ergebnis.fehler = T.fehler_inferenz; return ergebnis; }\n        if (!isFinite(anzahl) || anzahl < 1) { ergebnis.fehler = T.fehler_anzahl; return ergebnis; }\n        if (!isFinite(bewegung) || bewegung < 0 || bewegung > 100) { ergebnis.fehler = T.fehler_bewegung; return ergebnis; }<\/p>\n<p>        const roh = Array.isArray(eingabe.kameras) ? eingabe.kameras : [];\n        const kameras = [];\n        for (let i = 0; i < roh.length; i++) {\n            const name = String(roh[i].name === undefined ? '' : roh[i].name).trim();\n            if (name === '') { continue; }\n            const fps = zahl(roh[i].fps);\n            if (!isFinite(fps) || fps <= 0) {\n                ergebnis.fehler = T.fehler_fps.replace('%s', name);\n                return ergebnis;\n            }\n            const teile = String(roh[i].aufloesung || '1280x720').split('x');\n            const breite = parseInt(teile[0], 10);\n            const hoehe = parseInt(teile[1], 10);\n            if (!isFinite(breite) || !isFinite(hoehe)) {\n                ergebnis.fehler = T.fehler_aufloesung.replace('%s', name);\n                return ergebnis;\n            }\n            kameras.push({ name: name, kennung: kennung(name) || 'kamera_' + (i + 1), fps: fps, breite: breite, hoehe: hoehe });\n        }\n\n        if (!kameras.length) { ergebnis.fehler = T.fehler_keine_kamera; return ergebnis; }\n\n        const kapazitaet = (1000 \/ inferenz) * anzahl;\n        const bedarfVoll = kameras.reduce(function (summe, k) { return summe + k.fps; }, 0);\n        const bedarfEcht = bedarfVoll * (bewegung \/ 100);\n\n        ergebnis.kapazitaet = runde(kapazitaet, 1);\n        ergebnis.bedarfVoll = runde(bedarfVoll, 1);\n        ergebnis.bedarfEcht = runde(bedarfEcht, 1);\n        ergebnis.auslastungVoll = runde(bedarfVoll \/ kapazitaet * 100, 1);\n        ergebnis.auslastungEcht = runde(bedarfEcht \/ kapazitaet * 100, 1);\n        ergebnis.noetig = Math.ceil(bedarfVoll \/ (1000 \/ inferenz));\n        ergebnis.kameras = kameras.map(function (k) {\n            return {\n                name: k.name,\n                kennung: k.kennung,\n                fps: k.fps,\n                aufloesung: k.breite + 'x' + k.hoehe,\n                anteil: runde(k.fps \/ bedarfVoll * 100, 1),\n                pixel: k.breite * k.hoehe\n            };\n        });\n\n        \/\/ --- Befunde ---\n        if (ergebnis.auslastungVoll > 100) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_ueberlastet.replace('%s', String(ergebnis.auslastungVoll)).replace('%n', String(ergebnis.noetig)) });\n        } else if (ergebnis.auslastungVoll > 80) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_knapp.replace('%s', String(ergebnis.auslastungVoll)) });\n        } else {\n            ergebnis.befunde.push({ schwer: false, text: T.b_reicht.replace('%s', String(ergebnis.auslastungVoll)) });\n        }\n        ergebnis.befunde.push({ schwer: false, text: T.b_bewegung.replace('%s', String(ergebnis.auslastungEcht)) });<\/p>\n<p>        const schnelle = kameras.filter(function (k) { return k.fps > 5; });\n        if (schnelle.length) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_fps.replace('%s', schnelle.map(function (k) { return k.name; }).join(', ')) });\n        }\n        const grosse = kameras.filter(function (k) { return k.breite > 1280; });\n        if (grosse.length) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_aufloesung.replace('%s', grosse.map(function (k) { return k.name; }).join(', ')) });\n        }\n        ergebnis.befunde.push({ schwer: false, text: T.b_modell });\n        if (inferenz >= 50) {\n            ergebnis.befunde.push({ schwer: true, text: T.b_cpu });\n        }\n        if (anzahl > 1) {\n            ergebnis.befunde.push({ schwer: false, text: T.b_mehrere });\n        }\n        ergebnis.befunde.push({ schwer: false, text: T.b_substrom });<\/p>\n<p>        \/\/ --- Ger\u00fcst f\u00fcr die Konfiguration ---\n        const zeilen = [];\n        zeilen.push('detectors:');\n        for (let i = 0; i < anzahl; i++) {\n            zeilen.push('  coral' + i + ':');\n            zeilen.push('    type: edgetpu');\n            zeilen.push('    device: ' + (inferenz >= 50 ? 'cpu' : (i === 0 ? 'usb' : 'usb:' + i)));\n        }\n        zeilen.push('');\n        zeilen.push('cameras:');\n        kameras.forEach(function (k) {\n            zeilen.push('  ' + k.kennung + ':');\n            zeilen.push('    ffmpeg:');\n            zeilen.push('      inputs:');\n            zeilen.push('        - path: rtsp:\/\/benutzer:geheim@192.168.1.50:554\/stream2');\n            zeilen.push('          roles: [detect]');\n            zeilen.push('        - path: rtsp:\/\/benutzer:geheim@192.168.1.50:554\/stream1');\n            zeilen.push('          roles: [record]');\n            zeilen.push('    detect:');\n            zeilen.push('      width: ' + k.breite);\n            zeilen.push('      height: ' + k.hoehe);\n            zeilen.push('      fps: ' + k.fps);\n            zeilen.push('    objects:');\n            zeilen.push('      track: [person, car]');\n            zeilen.push('');\n        });\n        ergebnis.yaml = zeilen.join('\\n');<\/p>\n<p>        ergebnis.ok = true;\n        return ergebnis;\n    }<\/p>\n<p>    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.FG = { planen: planen, zahl: zahl, kennung: kennung };\n    }<\/p>\n<p>    \/\/ --- Anbindung an das Dokument ---<\/p>\n<p>    const btn = document.getElementById('fg-btn');\n    const out = document.getElementById('fg-ergebnis');\n    const liste = document.getElementById('fg-liste');\n    const addBtn = document.getElementById('fg-add');\n    if (!btn || !out || !liste || !addBtn) { return; }<\/p>\n<p>    const KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 14px; margin-bottom: 18px;';\n    const UEBERSCHRIFT = 'color: var(--text-primary, #e8e8ee); font-weight: 700; margin-bottom: 8px;';\n    const FELD = 'padding: 10px; background: var(--bg-panel, #1e1e24); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace;';<\/p>\n<p>    function el(tag, css, inhalt) {\n        const knoten = document.createElement(tag);\n        if (css) { knoten.setAttribute('style', css); }\n        if (inhalt !== undefined && inhalt !== null) { knoten.textContent = inhalt; }\n        return knoten;\n    }<\/p>\n<p>    function wert(id) {\n        const knoten = document.getElementById(id);\n        return knoten ? knoten.value : '';\n    }<\/p>\n<p>    let zaehler = 0;\n    function zeileErzeugen(vorgabe) {\n        const v = vorgabe || {};\n        zaehler++;\n        const zeile = el('div', 'display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 8px;');\n        zeile.setAttribute('data-fg', 'zeile');<\/p>\n<p>        const name = document.createElement('input');\n        name.type = 'text';\n        name.id = 'fg-name-' + zaehler;\n        name.setAttribute('style', FELD + ' flex: 2; min-width: 150px;');\n        name.setAttribute('data-fg', 'name');\n        name.placeholder = T.ph_name;\n        name.value = v.name || '';\n        zeile.appendChild(name);<\/p>\n<p>        const aufloesung = document.createElement('select');\n        aufloesung.id = 'fg-res-' + zaehler;\n        aufloesung.setAttribute('style', FELD + ' min-width: 150px;');\n        aufloesung.setAttribute('data-fg', 'aufloesung');\n        ['640x360', '1280x720', '1920x1080', '2560x1440'].forEach(function (r) {\n            const option = document.createElement('option');\n            option.value = r;\n            option.textContent = r;\n            aufloesung.appendChild(option);\n        });\n        aufloesung.value = v.aufloesung || '1280x720';\n        zeile.appendChild(aufloesung);<\/p>\n<p>        const fps = document.createElement('input');\n        fps.type = 'text';\n        fps.id = 'fg-fps-' + zaehler;\n        fps.setAttribute('style', FELD + ' width: 90px;');\n        fps.setAttribute('data-fg', 'fps');\n        fps.placeholder = 'fps';\n        fps.value = v.fps || '5';\n        zeile.appendChild(fps);<\/p>\n<p>        const weg = el('button', 'background: transparent; color: var(--text-secondary, #a0a0b0); border: 1px solid var(--border, #2a2a35); padding: 10px 14px; border-radius: 6px; cursor: pointer;', T.knopf_weg);\n        weg.type = 'button';\n        weg.addEventListener('click', function () {\n            if (zeile.parentNode) { zeile.parentNode.removeChild(zeile); }\n            if (!liste.querySelector('[data-fg=\"zeile\"]')) { zeileErzeugen({}); }\n        });\n        zeile.appendChild(weg);<\/p>\n<p>        liste.appendChild(zeile);\n    }<\/p>\n<p>    function kamerasLesen() {\n        const knoten = liste.querySelectorAll('[data-fg=\"zeile\"]');\n        const daten = [];\n        for (let i = 0; i < knoten.length; i++) {\n            const name = knoten[i].querySelector('[data-fg=\"name\"]');\n            const aufloesung = knoten[i].querySelector('[data-fg=\"aufloesung\"]');\n            const fps = knoten[i].querySelector('[data-fg=\"fps\"]');\n            if (!name || !aufloesung || !fps) { continue; }\n            daten.push({ name: name.value, aufloesung: aufloesung.value, fps: fps.value });\n        }\n        return daten;\n    }\n\n    function zeile(kasten, name, inhalt, hervor) {\n        const z = el('div', 'display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap; padding: 5px 0;');\n        z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0);', name));\n        z.appendChild(el('span', 'color: ' + (hervor ? '#ffb86c' : 'var(--text-primary, #e8e8ee)') + '; font-family: monospace;', inhalt));\n        kasten.appendChild(z);\n    }\n\n    addBtn.addEventListener('click', function () { zeileErzeugen({}); });\n\n    btn.addEventListener('click', function () {\n        const erg = planen({\n            inferenz: wert('fg-inferenz'),\n            anzahl: wert('fg-anzahl'),\n            bewegung: wert('fg-bewegung'),\n            kameras: kamerasLesen()\n        });\n\n        out.textContent = '';\n        if (!erg.ok) {\n            out.appendChild(el('div', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left: 3px solid #ffb86c; color: var(--text-secondary, #a0a0b0); padding: 14px; border-radius: 6px;', erg.fehler));\n            return;\n        }\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_auslastung));\n        const kasten = el('div', KASTEN);\n        zeile(kasten, T.z_kapazitaet, erg.kapazitaet + ' ' + T.einheit_inferenzen);\n        zeile(kasten, T.z_bedarf, erg.bedarfVoll + ' ' + T.einheit_inferenzen);\n        zeile(kasten, T.z_auslastung, erg.auslastungVoll + ' %', erg.auslastungVoll > 80);\n        zeile(kasten, T.z_bedarf_echt, erg.bedarfEcht + ' ' + T.einheit_inferenzen);\n        zeile(kasten, T.z_auslastung_echt, erg.auslastungEcht + ' %', erg.auslastungEcht > 80);\n        zeile(kasten, T.z_noetig, String(erg.noetig), erg.noetig > 1);\n        out.appendChild(kasten);<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_kameras));\n        const tabelle = el('div', KASTEN);\n        erg.kameras.forEach(function (k) {\n            const z = el('div', 'display: flex; gap: 12px; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--border, #2a2a35);');\n            z.appendChild(el('span', 'flex: 2; color: var(--text-primary, #e8e8ee);', k.name));\n            z.appendChild(el('span', 'width: 110px; color: var(--text-secondary, #a0a0b0); font-family: monospace; font-size: 0.85rem;', k.aufloesung));\n            z.appendChild(el('span', 'width: 70px; color: var(--text-secondary, #a0a0b0); font-family: monospace; font-size: 0.85rem;', k.fps + ' fps'));\n            z.appendChild(el('span', 'width: 70px; color: var(--text-primary, #e8e8ee); font-family: monospace; font-size: 0.85rem;', k.anteil + ' %'));\n            tabelle.appendChild(z);\n        });\n        out.appendChild(tabelle);<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_befunde));\n        const befunde = el('div', KASTEN);\n        erg.befunde.forEach(function (b) {\n            const z = el('div', 'display: flex; gap: 10px; padding: 5px 0;');\n            z.appendChild(el('span', 'color: ' + (b.schwer ? '#ffb86c' : 'var(--text-secondary, #a0a0b0)') + '; font-family: monospace;', b.schwer ? '!' : '-'));\n            z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0); font-size: 0.9rem; line-height: 1.5;', b.text));\n            befunde.appendChild(z);\n        });\n        out.appendChild(befunde);<\/p>\n<p>        out.appendChild(el('div', UEBERSCHRIFT, T.h_yaml));\n        out.appendChild(el('pre', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); padding: 16px; border-radius: 6px; overflow-x: auto; font-family: monospace; font-size: 0.8rem; line-height: 1.5; white-space: pre; margin-bottom: 18px;', erg.yaml));\n    });<\/p>\n<p>    zeileErzeugen({ name: 'Einfahrt', aufloesung: '1280x720', fps: '5' });\n    zeileErzeugen({ name: 'Haustuer', aufloesung: '1280x720', fps: '5' });\n    zeileErzeugen({ name: 'Garten', aufloesung: '640x360', fps: '5' });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rechnet die Erkennungslast aller Kameras zusammen, stellt sie der Leistung einer Coral-TPU gegen\u00fcber und erzeugt ein passendes Ger\u00fcst f\u00fcr frigate.yml.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":9283,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[92451,92217,92208],"class_list":["post-10269","page","type-page","status-publish","hentry","tag-monitoring-cctv-pl-de","tag-docker-de","tag-raspberry-pi-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Rechnet die Erkennungslast aller Kameras zusammen, stellt sie der Leistung einer Coral-TPU gegen\u00fcber und erzeugt ein passendes Ger\u00fcst f\u00fcr frigate.yml.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/frigate-coral-tpu-capacity-planner\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/frigate-coral-tpu-capacity-planner\\\/\",\"name\":\"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-14T23:00:10+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/frigate-coral-tpu-capacity-planner\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/frigate-coral-tpu-capacity-planner\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/frigate-coral-tpu-capacity-planner\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/toolbox-2\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/","og_locale":"en_US","og_type":"article","og_title":"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog","og_description":"Rechnet die Erkennungslast aller Kameras zusammen, stellt sie der Leistung einer Coral-TPU gegen\u00fcber und erzeugt ein passendes Ger\u00fcst f\u00fcr frigate.yml.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/","name":"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-14T23:00:10+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/frigate-coral-tpu-capacity-planner\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/toolbox-2\/"},{"@type":"ListItem","position":3,"name":"Kapazit\u00e4tsplaner f\u00fcr Frigate und Coral-TPU"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10269"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10269\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9283"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10269"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}