diff --git a/docs/docs/frigate/installation.md b/docs/docs/frigate/installation.md index 8fade1a78..4dd21291a 100644 --- a/docs/docs/frigate/installation.md +++ b/docs/docs/frigate/installation.md @@ -165,6 +165,8 @@ devices: - /dev/dma_heap - /dev/rga - /dev/mpp_service +volumes: + - /sys/:/sys/:ro ``` or add these options to your `docker run` command: @@ -175,7 +177,8 @@ or add these options to your `docker run` command: --device /dev/dri \ --device /dev/dma_heap \ --device /dev/rga \ ---device /dev/mpp_service +--device /dev/mpp_service \ +--volume /sys/:/sys/:ro ``` #### Configuration diff --git a/frigate/stats/util.py b/frigate/stats/util.py index 7bdf92bf2..c372c2ecc 100644 --- a/frigate/stats/util.py +++ b/frigate/stats/util.py @@ -24,6 +24,7 @@ from frigate.util.services import ( get_intel_gpu_stats, get_jetson_stats, get_nvidia_gpu_stats, + get_rockchip_npu_stats, is_vaapi_amd_driver, ) from frigate.version import VERSION @@ -109,6 +110,7 @@ def get_processing_stats( stats_tasks = [ asyncio.create_task(set_gpu_stats(config, stats, hwaccel_errors)), asyncio.create_task(set_cpu_stats(stats)), + asyncio.create_task(set_npu_usages(config, stats)), ] if config.telemetry.stats.network_bandwidth: @@ -238,6 +240,19 @@ async def set_gpu_stats( all_stats["gpu_usages"] = stats +async def set_npu_usages(config: FrigateConfig, all_stats: dict[str, Any]) -> None: + stats: dict[str, dict] = {} + + for detector in config.detectors.values(): + if detector.type == "rknn": + # Rockchip NPU usage + rk_usage = get_rockchip_npu_stats() + stats["rockchip"] = rk_usage + + if stats: + all_stats["npu_usages"] = stats + + def stats_snapshot( config: FrigateConfig, stats_tracking: StatsTrackingTypes, hwaccel_errors: list[str] ) -> dict[str, Any]: diff --git a/frigate/util/services.py b/frigate/util/services.py index ce7041c26..fb0e80bdd 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -382,6 +382,23 @@ def get_intel_gpu_stats(sriov: bool) -> dict[str, str]: return results +def get_rockchip_npu_stats() -> dict[str, str]: + """Get stats using rk.""" + try: + with open("/sys/kernel/debug/rknpu/load", "r") as f: + npu_output = f.read() + core_loads = re.findall(r"Core\d+:\s*(\d+)%", npu_output) + except FileNotFoundError: + core_loads = None + + if not core_loads: + return None + + percentages = [int(load) for load in core_loads] + mean = round(sum(percentages) / len(percentages), 2) + return {"npu": mean, "mem": "-"} + + def try_get_info(f, h, default="N/A"): try: if h: diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json index c57848a04..211b86059 100644 --- a/web/public/locales/en/views/system.json +++ b/web/public/locales/en/views/system.json @@ -72,7 +72,9 @@ "toast": { "success": "Copied GPU info to clipboard" } - } + }, + "npuUsage": "NPU Usage", + "npuMemory": "NPU Memory" }, "otherProcesses": { "title": "Other Processes", diff --git a/web/src/types/stats.ts b/web/src/types/stats.ts index 8e41f2e38..611379be3 100644 --- a/web/src/types/stats.ts +++ b/web/src/types/stats.ts @@ -4,6 +4,7 @@ export interface FrigateStats { detectors: { [detectorKey: string]: DetectorStats }; embeddings?: EmbeddingsStats; gpu_usages?: { [gpuKey: string]: GpuStats }; + npu_usages?: { [npuKey: string]: NpuStats }; processes: { [processKey: string]: ExtraProcessStats }; service: ServiceStats; detection_fps: number; @@ -54,6 +55,11 @@ export type GpuStats = { pstate?: string; }; +export type NpuStats = { + npu: number; + mem: string; +}; + export type GpuInfo = "vainfo" | "nvinfo"; export type ServiceStats = { diff --git a/web/src/views/system/GeneralMetrics.tsx b/web/src/views/system/GeneralMetrics.tsx index 3596a7ad1..055550707 100644 --- a/web/src/views/system/GeneralMetrics.tsx +++ b/web/src/views/system/GeneralMetrics.tsx @@ -34,7 +34,7 @@ export default function GeneralMetrics({ const { data: initialStats } = useSWR( [ "stats/history", - { keys: "cpu_usages,detectors,gpu_usages,processes,service" }, + { keys: "cpu_usages,detectors,gpu_usages,npu_usages,processes,service" }, ], { revalidateOnFocus: false, @@ -369,8 +369,57 @@ export default function GeneralMetrics({ return Object.keys(series).length > 0 ? Object.values(series) : undefined; }, [statsHistory]); + // npu stats + + const npuSeries = useMemo(() => { + if (!statsHistory) { + return []; + } + + const series: { + [key: string]: { name: string; data: { x: number; y: number }[] }; + } = {}; + let hasValidNpu = false; + + statsHistory.forEach((stats, statsIdx) => { + if (!stats) { + return; + } + + Object.entries(stats.npu_usages || []).forEach(([key, stats]) => { + if (!(key in series)) { + series[key] = { name: key, data: [] }; + } + + if (stats.npu) { + hasValidNpu = true; + series[key].data.push({ x: statsIdx + 1, y: stats.npu }); + } + }); + }); + + if (!hasValidNpu) { + return []; + } + + return Object.keys(series).length > 0 ? Object.values(series) : []; + }, [statsHistory]); + // other processes stats + const hardwareType = useMemo(() => { + const hasGpu = gpuSeries.length > 0; + const hasNpu = npuSeries.length > 0; + + if (hasGpu && !hasNpu) { + return "GPUs"; + } else if (!hasGpu && hasNpu) { + return "NPUs"; + } else { + return "GPUs / NPUs"; + } + }, [gpuSeries, npuSeries]); + const otherProcessCpuSeries = useMemo(() => { if (!statsHistory) { return []; @@ -533,11 +582,13 @@ export default function GeneralMetrics({ )} - {(statsHistory.length == 0 || statsHistory[0].gpu_usages) && ( + {(statsHistory.length == 0 || + statsHistory[0].gpu_usages || + statsHistory[0].npu_usages) && ( <>
- GPUs + {hardwareType}
{canGetGpuInfo && (