{"id":410,"date":"2026-03-13T20:40:27","date_gmt":"2026-03-13T12:40:27","guid":{"rendered":"http:\/\/114.55.133.236\/?p=410"},"modified":"2026-05-17T13:34:32","modified_gmt":"2026-05-17T05:34:32","slug":"openclaw%e8%8e%b7%e5%8f%96%e6%91%84%e5%83%8f%e5%a4%b4%e7%94%bb%e9%9d%a2%e6%8e%a8%e6%b5%81%e9%a1%b9%e7%9b%ae","status":"publish","type":"post","link":"http:\/\/qianmo.icu\/index.php\/2026\/03\/13\/openclaw%e8%8e%b7%e5%8f%96%e6%91%84%e5%83%8f%e5%a4%b4%e7%94%bb%e9%9d%a2%e6%8e%a8%e6%b5%81%e9%a1%b9%e7%9b%ae\/","title":{"rendered":"OpenClaw\u83b7\u53d6\u6444\u50cf\u5934\u753b\u9762\u63a8\u6d41\u9879\u76ee"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u60f3\u4e86\u89e3\u66f4\u591a\u9879\u76ee\u8bf7\u641c\u7d22\u201c\u9879\u76ee\u201d\u6807\u7b7e\u6216\u8005\u8bbf\u95ee\u6211\u7684github\u4ed3\u5e93\u3002<br>\u4ed3\u5e93\u5730\u5740\uff1a<a href=\"https:\/\/github.com\/QianmoNai?tab=repositories\">QianmoNai&#8217;s Repositories<\/a><\/p>\n<\/blockquote>\n\n\n\n<h1 class=\"wp-block-heading\">\u57fa\u4e8e\u6811\u8393\u6d3edocker\u5bb9\u5668\u90e8\u7f72\u4e0b\u7684OpenClaw\u83b7\u53d6USB\u6444\u50cf\u5934\u63a8\u6d41\u753b\u9762\u9879\u76ee<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">1.\u9879\u76ee\u80cc\u666f<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u6700\u8fd1\u5c1d\u8bd5\u6269\u5c55\u4e00\u4e0bopenclaw\u7684\u73a9\u6cd5\uff0c\u4e8e\u662f\u5c31\u60f3\u7740\u80fd\u4e0d\u80fd\u7528usb\u6444\u50cf\u5934\u6765\u7ed9\u9f99\u867e\u201c\u957f\u773c\u775b\u201d\uff0c\u5173\u4e8e\u5982\u4f55\u5728\u6811\u8393\u6d3e\u4e2d\u90e8\u7f72openclaw\uff0c\u672c\u6587\u4e0d\u5c55\u793a\u90e8\u7f72\u8fc7\u7a0b\uff0c\u8be6\u7ec6\u53ef\u4ee5\u53c2\u8003\u8fd9\u4e2a\u89c6\u9891<a href=\"https:\/\/www.bilibili.com\/video\/BV1BNPCzCEP6\/?spm_id_from=333.337.search-card.all.click&amp;vd_source=87d06c8a30725ef270253f6a6038e128\">\u26a1\u6781\u81f4\u6311\u6218\uff1a\u6811\u8393\u6d3e\u4e0a\u90e8\u7f72 OpenClaw \u5c0f\u9f99\u867e\ud83e\udd9e\uff01<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2.\u9879\u76ee\u4ecb\u7ecd<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u9879\u76ee\u6240\u7528\u5230\u7684\uff1a\u90e8\u7f72\u5b8c\u6210\u5e76\u4e14\u6210\u529f\u63a5\u5165\u98de\u4e66\u7684openclaw\u3001\u6811\u8393\u6d3e5\uff088G\uff09\u3001usb\u6444\u50cf\u5934\u3002<\/p>\n\n\n\n<details class=\"wp-block-details has-pale-pink-color has-text-color has-link-color wp-elements-9cb10445376dbf52b530c9e328fb9b14 is-layout-flow wp-block-details-is-layout-flow\"><summary>\u5b9e\u73b0\u6548\u679c\uff08\u70b9\u6211\uff09<\/summary>\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194215-461x1024.jpg'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" fetchpriority=\"low\" data-original=\"http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194215-461x1024.jpg\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-411\"\/><\/div><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194232-461x1024.jpg'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" width=\"461\" height=\"1024\" fetchpriority=\"low\" data-original=\"http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194232-461x1024.jpg\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-412\"  sizes=\"(max-width: 461px) 100vw, 461px\" \/><\/div><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194312-461x1024.jpg'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" width=\"461\" height=\"1024\" fetchpriority=\"low\" data-original=\"http:\/\/114.55.133.236\/wp-content\/uploads\/2026\/03\/Screenshot_20260313_194312-461x1024.jpg\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-413\"  sizes=\"(max-width: 461px) 100vw, 461px\" \/><\/div><\/figure>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5982\u4e0a\u6240\u793a\uff0c\u6700\u7ec8\u6210\u679c\u662f\u80fd\u591f\u5f97\u5230\u4e00\u4e2a\u5c40\u57df\u7f51\u5185\u4efb\u610f\u8bbe\u5907\u90fd\u80fd\u5b9e\u65f6\u770b\u6444\u50cf\u5934\u7684\u63a8\u6d41\u7f51\u9875\uff0c\u548c\u4e00\u4e2a\u80fd\u611f\u77e5\u5916\u754c\u201c\u957f\u773c\u775b\u7684\u9f99\u867e\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u2460\u63a8\u6d41\u89c6\u9891\u7f51\u9875\u90e8\u5206\uff1a<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u5de5\u4f5c\u539f\u7406\uff1a<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u6444\u50cf\u5934 \u2192 ffmpeg (\u91c7\u96c6+MJPEG\u7f16\u7801) \u2192 Python HTTP\u670d\u52a1\u5668 \u2192 \u5ba2\u6237\u7aef\u6d4f\u89c8\u5668<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u6838\u5fc3\u4ee3\u7801\uff1a<\/h4>\n\n\n\n<details class=\"wp-block-details has-pale-pink-color has-text-color has-link-color wp-elements-e0afc5159f5d0b1226f43181594d6477 is-layout-flow wp-block-details-is-layout-flow\"><summary>Python HTTP\u670d\u52a1\u5668\u4ee3\u7801 \uff08\u70b9\u6211\uff09<\/summary>\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env python3\n\"\"\"\n\u6811\u8393\u6d3e\u6444\u50cf\u5934 MJPEG \u6d41\u670d\u52a1\u5668 - \u62a2\u5360\u5f0f\u5355\u5ba2\u6237\u7aef\n\u65b0\u5ba2\u6237\u7aef\u8fde\u63a5\u65f6\u7acb\u5373\u7ec8\u6b62\u65e7\u5ba2\u6237\u7aef\n\"\"\"\n\nimport subprocess\nimport socket\nimport sys\nimport signal\nimport threading\nimport time\nimport select\n\nHOST = '0.0.0.0'\nPORT = 8080\nDEVICE = '\/dev\/video0'\n\nBOUNDARY = b'--BoundaryString'\n\n# \u5168\u5c40\u8d44\u6e90\u5f15\u7528\ncurrent_proc = None\ncurrent_client = None\ncurrent_thread = None\nlock = threading.Lock()\n\ndef get_local_ip():\n    try:\n        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n        s.connect((\"8.8.8.8\", 80))\n        ip = s.getsockname()&#91;0]\n        s.close()\n        return ip\n    except:\n        return \"127.0.0.1\"\n\ndef kill_old(reset_global=True):\n    \"\"\"\u7ec8\u6b62\u65e7\u8d44\u6e90\"\"\"\n    global current_proc, current_client, current_thread\n    \n    with lock:\n        # \u4fdd\u5b58\u5f53\u524d\u8d44\u6e90\u5f15\u7528\n        proc = current_proc if reset_global else None\n        client = current_client if reset_global else None\n        thread = current_thread if reset_global else None\n        \n        # \u91cd\u7f6e\u5168\u5c40\u53d8\u91cf\uff08\u5148\u91cd\u7f6e\uff0c\u907f\u514d\u65b0\u8bf7\u6c42\u62ff\u5230\u65e7\u8d44\u6e90\uff09\n        if reset_global:\n            current_proc = None\n            current_client = None\n            current_thread = None\n    \n    # \u7ec8\u6b62\u7ebf\u7a0b\uff08\u5982\u679c\u9700\u8981\uff09\n    if thread and thread.is_alive():\n        # \u7ebf\u7a0b\u662f\u5b88\u62a4\u7ebf\u7a0b\uff0c\u4e3b\u8981\u9760\u5173\u95ed\u5ba2\u6237\u7aef\u548c\u8fdb\u7a0b\u6765\u7ec8\u6b62\n        pass\n    \n    # \u7ec8\u6b62ffmpeg\u8fdb\u7a0b\n    if proc:\n        try:\n            proc.terminate()\n            try:\n                proc.wait(timeout=0.5)\n            except subprocess.TimeoutExpired:\n                proc.kill()\n        except Exception as e:\n            print(f\"\u7ec8\u6b62\u65e7\u8fdb\u7a0b\u5931\u8d25: {e}\")\n    \n    # \u5173\u95ed\u5ba2\u6237\u7aef\u8fde\u63a5\n    if client:\n        try:\n            client.shutdown(socket.SHUT_RDWR)\n        except Exception:\n            pass\n        try:\n            client.close()\n            print(\"\u65e7\u5ba2\u6237\u7aef\u8fde\u63a5\u5df2\u5173\u95ed\")\n        except Exception as e:\n            print(f\"\u5173\u95ed\u65e7\u5ba2\u6237\u7aef\u5931\u8d25: {e}\")\n\ndef read_http_request(client):\n    \"\"\"\u8bfb\u53d6\u5e76\u89e3\u6790\u5ba2\u6237\u7aef\u7684HTTP\u8bf7\u6c42\uff08\u975e\u963b\u585e\uff09\"\"\"\n    try:\n        # \u8bbe\u7f6e\u975e\u963b\u585e\u6a21\u5f0f\uff0c\u907f\u514d\u5361\u6b7b\n        client.setblocking(False)\n        ready = select.select(&#91;client], &#91;], &#91;], 2.0)\n        if ready&#91;0]:\n            # \u8bfb\u53d6\u8bf7\u6c42\u5934\uff08\u6700\u591a4096\u5b57\u8282\uff09\n            request = client.recv(4096)\n            if b'GET' in request:\n                return True\n    except Exception:\n        pass\n    return False\n\ndef stream_to_client(client, proc):\n    \"\"\"\u5411\u5ba2\u6237\u7aef\u53d1\u9001\u89c6\u9891\u6d41\"\"\"\n    try:\n        while True:\n            # \u68c0\u67e5\u5ba2\u6237\u7aef\u662f\u5426\u8fd8\u8fde\u63a5\n            try:\n                # \u975e\u963b\u585e\u68c0\u67e5\u5ba2\u6237\u7aef\u662f\u5426\u5173\u95ed\n                client.setblocking(False)\n                data = client.recv(1, socket.MSG_PEEK)\n                if not data:\n                    break\n            except BlockingIOError:\n                pass\n            except Exception:\n                break\n            \n            # \u68c0\u67e5ffmpeg\u8fdb\u7a0b\u662f\u5426\u8fd8\u5728\u8fd0\u884c\n            if proc.poll() is not None:\n                print(\"ffmpeg\u8fdb\u7a0b\u5df2\u9000\u51fa\")\n                break\n            \n            client.sendall(BOUNDARY + b'\\r\\n')\n            client.sendall(b'Content-Type: image\/jpeg\\r\\n\\r\\n')\n            \n            frame = b''\n            found_start = False\n            \n            while True:\n                chunk = proc.stdout.read(8192)\n                if not chunk:\n                    return\n                \n                if not found_start:\n                    start = chunk.find(b'\\xff\\xd8')\n                    if start &gt;= 0:\n                        frame = chunk&#91;start:]\n                        found_start = True\n                    continue\n                \n                frame += chunk\n                end = frame.rfind(b'\\xff\\xd9')\n                if end &gt;= 0:\n                    frame = frame&#91;:end+2]\n                    break\n            \n            client.sendall(frame)\n            client.sendall(b'\\r\\n')\n            \n    except Exception as e:\n        print(f\"\u6d41\u53d1\u9001\u5f02\u5e38: {e}\")\n    finally:\n        # \u6e05\u7406\u5f53\u524d\u8fde\u63a5\u7684\u8d44\u6e90\n        kill_old(reset_global=False)\n\ndef main():\n    global current_proc, current_client, current_thread\n    \n    ip = get_local_ip()\n    print(f\"\u89c6\u9891\u6d41\u670d\u52a1\u5668\u5df2\u542f\u52a8\")\n    print(f\"\u8bbf\u95ee\u5730\u5740: http:\/\/{ip}:{PORT}\/\")\n    print(f\"\u6444\u50cf\u5934: {DEVICE}\")\n    print(f\"\u6a21\u5f0f: \u65b0\u5ba2\u6237\u7aef\u62a2\u5360\u8fde\u63a5\")\n    print(f\"\u6309 Ctrl+C \u505c\u6b62\\n\")\n    \n    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    server.bind((HOST, PORT))\n    server.listen(5)\n    # \u8bbe\u7f6e\u670d\u52a1\u5668\u5957\u63a5\u5b57\u8d85\u65f6\uff0c\u907f\u514daccept\u5361\u6b7b\n    server.settimeout(1.0)\n    \n    def handler(sig, frame):\n        print(\"\\n\u505c\u6b62\u670d\u52a1\u5668...\")\n        kill_old()\n        server.close()\n        sys.exit(0)\n    signal.signal(signal.SIGINT, handler)\n    \n    while True:\n        try:\n            client, addr = server.accept()\n        except socket.timeout:\n            continue\n        except Exception as e:\n            print(f\"\u63a5\u53d7\u8fde\u63a5\u5931\u8d25: {e}\")\n            continue\n        \n        print(f\"\\n\u65b0\u5ba2\u6237\u7aef\u8fde\u63a5: {addr}\")\n        \n        # 1. \u5148\u7ec8\u6b62\u6240\u6709\u65e7\u8d44\u6e90\uff08\u6838\u5fc3\uff1a\u5148\u6740\u65e7\u7684\uff0c\u518d\u5904\u7406\u65b0\u7684\uff09\n        kill_old()\n        \n        # 2. \u8bfb\u53d6\u5e76\u9a8c\u8bc1\u5ba2\u6237\u7aef\u7684HTTP\u8bf7\u6c42\n        if not read_http_request(client):\n            print(f\"\u5ba2\u6237\u7aef {addr} \u672a\u53d1\u9001\u6709\u6548HTTP\u8bf7\u6c42\uff0c\u5173\u95ed\u8fde\u63a5\")\n            client.close()\n            continue\n        \n        # 3. \u542f\u52a8\u65b0\u7684ffmpeg\u8fdb\u7a0b\n        cmd = &#91;\n            'ffmpeg',\n            '-f', 'v4l2',\n            '-input_format', 'mjpeg',\n            '-video_size', '1280x720',\n            '-framerate', '15',\n            '-i', DEVICE,\n            '-c:v', 'mjpeg',\n            '-q:v', '5',\n            '-f', 'image2pipe',\n            '-fflags', 'nobuffer',  # \u7981\u7528\u7f13\u51b2\uff0c\u964d\u4f4e\u5ef6\u8fdf\n            '-'\n        ]\n        \n        try:\n            proc = subprocess.Popen(\n                cmd, \n                stdout=subprocess.PIPE, \n                stderr=subprocess.DEVNULL,\n                preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)\n            )\n        except Exception as e:\n            print(f\"\u542f\u52a8ffmpeg\u5931\u8d25: {e}\")\n            client.close()\n            continue\n        \n        # 4. \u53d1\u9001HTTP\u54cd\u5e94\u5934\n        header = (\n            \"HTTP\/1.1 200 OK\\r\\n\"\n            \"Content-Type: multipart\/x-mixed-replace; boundary=BoundaryString\\r\\n\"\n            \"Cache-Control: no-cache\\r\\n\"\n            \"Pragma: no-cache\\r\\n\"\n            \"Connection: close\\r\\n\"  # \u660e\u786e\u5173\u95ed\u8fde\u63a5\n            \"\\r\\n\"\n        ).encode()\n        \n        try:\n            client.sendall(header)\n        except Exception as e:\n            print(f\"\u53d1\u9001\u54cd\u5e94\u5934\u5931\u8d25: {e}\")\n            proc.terminate()\n            client.close()\n            continue\n        \n        # 5. \u6ce8\u518c\u65b0\u8d44\u6e90\u5230\u5168\u5c40\u53d8\u91cf\n        with lock:\n            current_proc = proc\n            current_client = client\n        \n        # 6. \u542f\u52a8\u6d41\u53d1\u9001\u7ebf\u7a0b\n        t = threading.Thread(target=stream_to_client, args=(client, proc))\n        t.daemon = True\n        t.start()\n        \n        with lock:\n            current_thread = t\n        \n        print(f\"\u5ba2\u6237\u7aef {addr} \u62a2\u5360\u6210\u529f\uff0c\u6d41\u5df2\u5f00\u59cb\u4f20\u8f93\")\n\nif __name__ == '__main__':\n    main()\n<\/code><\/pre>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u91c7\u96c6\u89c6\u9891<\/strong>\uff1a \u7528 ffmpeg \u4ece \/dev\/video0 \u91c7\u96c6 MJPEG \u683c\u5f0f<\/li>\n\n\n\n<li><strong>HTTP \u54cd\u5e94<\/strong>\uff1a \u8fd4\u56de multipart\/x-mixed-replace \u7c7b\u578b\uff0c\u6d4f\u89c8\u5668\u53ef\u5b9e\u65f6\u663e\u793a<\/li>\n\n\n\n<li><strong>\u5e27\u89e3\u6790<\/strong>\uff1a\u4ece ffmpeg \u8f93\u51fa\u4e2d\u63d0\u53d6\u5b8c\u6574 JPEG \u5e27 (\\xff\\xd8 \u5230 \\xff\\xd9)<\/li>\n\n\n\n<li><strong>\u62a2\u5360\u673a\u5236<\/strong>\uff1a \u65b0\u5ba2\u6237\u7aef\u8fde\u63a5\u65f6\uff0c\u7ec8\u6b62\u65e7\u7684 ffmpeg \u8fdb\u7a0b\u548c socket<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u540c\u65f6\u53d1\u73b0\u5355\u5355\u53ea\u6709\u670d\u52a1\u5668\u4ee3\u7801\u8fd0\u884c\u5c0f\u6982\u7387\u4f1a\u51fa\u73b0\u8fdb\u7a0b\u5361\u6b7b\u7684\u60c5\u51b5\uff0c\u53ef\u80fd\u662f\u89c6\u9891\u6d41\u963b\u585e\uff1f\u6240\u4ee5\u52a0\u4e0a\u4e86\u4e00\u4e2a\u7684\u4e13\u95e8\u7528\u4e8e\u89c6\u9891\u670d\u52a1\u5668\u81ea\u52a8\u76d1\u63a7\u4e0e\u91cd\u542f\u7a0b\u5e8f\uff0c\u5728\u670d\u52a1\u5668\u51fa\u73b0\u95ee\u9898\u7684\u65f6\u5019\u91cd\u542f\u670d\u52a1\u5668\uff0c\u540e\u7eed\u4e5f\u53ea\u9700\u8981\u5f00\u673a\u81ea\u542f\u52a8\u8fd9\u4e2a\u7a0b\u5e8f\u5373\u53ef\u3002<\/p>\n\n\n\n<details class=\"wp-block-details has-pale-pink-color has-text-color has-link-color wp-elements-0634e9d7feb3427e157af18596eebf2b is-layout-flow wp-block-details-is-layout-flow\"><summary>\u89c6\u9891\u670d\u52a1\u5668\u81ea\u52a8\u76d1\u63a7\u4e0e\u91cd\u542f\u7a0b\u5e8f\u4ee3\u7801\uff08\u70b9\u6211\uff09<\/summary>\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env python3\n\"\"\"\n\u89c6\u9891\u670d\u52a1\u5668\u76d1\u63a7\u7a0b\u5e8f\n\u6bcf\u9694\u4e00\u6bb5\u65f6\u95f4\u68c0\u6d4b\u670d\u52a1\u5668\u8fd0\u884c\u72b6\u6001\uff0c\u51fa\u73b0\u95ee\u9898\u5c31\u91cd\u542f\n\"\"\"\n\nimport subprocess\nimport time\nimport socket\nimport sys\nimport os\n\nSERVER_SCRIPT = \"\/home\/qianmo\/mjpeg_server.py\"\nHOST = \"127.0.0.1\"\nPORT = 8080\nCHECK_INTERVAL = 60  # \u68c0\u6d4b\u95f4\u9694(\u79d2)\nMAX_RETRIES = 1      # \u6700\u5927\u91cd\u8bd5\u6b21\u6570\n\ndef is_process_running(name):\n    \"\"\"\u68c0\u67e5\u8fdb\u7a0b\u662f\u5426\u8fd0\u884c\"\"\"\n    try:\n        result = subprocess.run(\n            &#91;\"pgrep\", \"-f\", name],\n            capture_output=True,\n            text=True\n        )\n        return result.returncode == 0\n    except:\n        return False\n\ndef is_port_responding(host, port, timeout=3):\n    \"\"\"\u68c0\u67e5\u7aef\u53e3\u662f\u5426\u54cd\u5e94\"\"\"\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(timeout)\n        sock.connect((host, port))\n        sock.close()\n        return True\n    except:\n        return False\n\ndef check_http_response(host, port, timeout=3):\n    \"\"\"\u68c0\u67e5HTTP\u54cd\u5e94\u662f\u5426\u6b63\u5e38\"\"\"\n    try:\n        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        sock.settimeout(timeout)\n        sock.connect((host, port))\n        sock.send(b\"GET \/ HTTP\/1.0\\r\\n\\r\\n\")\n        response = sock.recv(1024)\n        sock.close()\n        return b\"200 OK\" in response\n    except:\n        return False\n\ndef stop_server():\n    \"\"\"\u505c\u6b62\u670d\u52a1\u5668\"\"\"\n    print(\"\u505c\u6b62\u670d\u52a1\u5668...\")\n    subprocess.run(&#91;\"pkill\", \"-f\", \"mjpeg_server.py\"], capture_output=True)\n    subprocess.run(&#91;\"pkill\", \"-f\", \"ffmpeg.*video0\"], capture_output=True)\n    time.sleep(1)\n\ndef start_server():\n    \"\"\"\u542f\u52a8\u670d\u52a1\u5668\"\"\"\n    print(f\"\u542f\u52a8\u670d\u52a1\u5668: {SERVER_SCRIPT}\")\n    subprocess.Popen(\n        &#91;\"python3\", SERVER_SCRIPT],\n        stdout=subprocess.DEVNULL,\n        stderr=subprocess.DEVNULL,\n        start_new_session=True\n    )\n    time.sleep(2)\n\ndef restart_server():\n    \"\"\"\u91cd\u542f\u670d\u52a1\u5668\"\"\"\n    stop_server()\n    start_server()\n\ndef get_timestamp():\n    \"\"\"\u83b7\u53d6\u65f6\u95f4\u6233\"\"\"\n    return time.strftime(\"%Y-%m-%d %H:%M:%S\")\n\ndef main():\n    print(f\"\u89c6\u9891\u670d\u52a1\u5668\u76d1\u63a7\u7a0b\u5e8f\u542f\u52a8\")\n    print(f\"\u76d1\u63a7\u5730\u5740: {HOST}:{PORT}\")\n    print(f\"\u68c0\u6d4b\u95f4\u9694: {CHECK_INTERVAL}\u79d2\")\n    print(f\"\u6309 Ctrl+C \u505c\u6b62\\n\")\n    \n    consecutive_failures = 0\n    \n    while True:\n        timestamp = get_timestamp()\n        \n        # \u68c0\u67e5\u8fdb\u7a0b\n        process_ok = is_process_running(\"mjpeg_server.py\")\n        port_ok = is_port_responding(HOST, PORT)\n        http_ok = check_http_response(HOST, PORT)\n        \n        if process_ok and port_ok and http_ok:\n            consecutive_failures = 0\n            print(f\"&#91;{timestamp}] \u72b6\u6001\u6b63\u5e38\")\n        else:\n            consecutive_failures += 1\n            print(f\"&#91;{timestamp}] \u68c0\u6d4b\u5f02\u5e38 (\u5931\u8d25\u6b21\u6570: {consecutive_failures})\")\n            print(f\"  - \u8fdb\u7a0b\u8fd0\u884c: {'\u662f' if process_ok else '\u5426'}\")\n            print(f\"  - \u7aef\u53e3\u54cd\u5e94: {'\u662f' if port_ok else '\u5426'}\")\n            print(f\"  - HTTP\u6b63\u5e38: {'\u662f' if http_ok else '\u5426'}\")\n            \n            if consecutive_failures &gt;= MAX_RETRIES:\n                print(f\"&#91;{timestamp}] \u8fde\u7eed\u5931\u8d25{consecutive_failures}\u6b21\uff0c\u91cd\u542f\u670d\u52a1\u5668...\")\n                restart_server()\n                consecutive_failures = 0\n                print(f\"&#91;{timestamp}] \u670d\u52a1\u5668\u5df2\u91cd\u542f\\n\")\n        \n        time.sleep(CHECK_INTERVAL)\n\nif __name__ == \"__main__\":\n    try:\n        main()\n    except KeyboardInterrupt:\n        print(\"\\n\u76d1\u63a7\u7a0b\u5e8f\u5df2\u505c\u6b62\")\n        sys.exit(0)\n<\/code><\/pre>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u591a\u7ef4\u5ea6\u68c0\u6d4b<\/strong>\uff1a\u8fdb\u7a0b\u5b58\u6d3b + \u7aef\u53e3\u8fde\u901a + HTTP \u54cd\u5e94\uff0c\u907f\u514d\u5355\u4e00\u68c0\u6d4b\u7ef4\u5ea6\u7684\u8bef\u5224\uff1b<\/li>\n\n\n\n<li><strong>\u81ea\u52a8\u6062\u590d<\/strong>\uff1a\u5f02\u5e38\u8fbe\u5230\u9608\u503c\u540e\u81ea\u52a8\u91cd\u542f\u670d\u52a1\u5668\uff0c\u65e0\u9700\u4eba\u5de5\u5e72\u9884\uff1b<\/li>\n\n\n\n<li><strong>\u65e5\u5fd7\u6e05\u6670<\/strong>\uff1a\u8f93\u51fa\u5e26\u65f6\u95f4\u6233\u7684\u72b6\u6001\u65e5\u5fd7\uff0c\u4fbf\u4e8e\u5b9a\u4f4d\u95ee\u9898\uff1b<\/li>\n\n\n\n<li><strong>\u9c81\u68d2\u6027<\/strong>\uff1a\u6355\u83b7\u5f02\u5e38\uff08\u5982\u7f51\u7edc\u8d85\u65f6\u3001\u547d\u4ee4\u6267\u884c\u5931\u8d25\uff09\uff0c\u907f\u514d\u76d1\u63a7\u7a0b\u5e8f\u81ea\u8eab\u5d29\u6e83\uff1b<\/li>\n\n\n\n<li><strong>\u540e\u53f0\u8fd0\u884c<\/strong>\uff1a\u542f\u52a8\u670d\u52a1\u5668\u65f6\u91cd\u5b9a\u5411\u8f93\u51fa\uff0c\u4e14\u65b0\u5efa\u4f1a\u8bdd\uff0c\u4fdd\u8bc1\u670d\u52a1\u72ec\u7acb\u8fd0\u884c\u3002<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u5176\u4ed6\uff1a<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u8981\u8ba9\u5c40\u57df\u7f51\u4e0b\u5176\u4ed6\u8bbe\u5907\u80fd\u591f\u8bbf\u95ee\u8981\u5f00\u653e\u9632\u706b\u5899\uff088080\u7aef\u53e3\uff09<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow 8080\/tcp<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u8bbf\u95ee\u5730\u5740\u2014\u2014http:\/\/\u6811\u8393\u6d3eip:8080\/<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u2460openclaw\u5bb9\u5668\u90e8\u5206\uff1a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6700\u7b80\u5355\u7684\u65b9\u6cd5\u662f\u628a\u63a8\u6d41\u5730\u5740\u53d1\u7ed9openclaw\u7136\u540e\u8ba9\u5b83\u81ea\u5df1\u60f3\u529e\u6cd5\u628a\u753b\u9762\u7ed9\u4f60\uff0c\u6700\u540e\u522b\u5fd8\u8bb0\u628a\u6d41\u7a0b\u603b\u7ed3\u6dfb\u52a0\u5230TOOLS.md\u4e2d\u8ba9\u9f99\u867e\u80fd\u8bb0\u4f4f\u64cd\u4f5c\u6d41\u7a0b\u3002\u6211\u6dfb\u52a0\u7684\u5185\u5bb9\u5982\u4e0b\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>### \u5c40\u57df\u7f51\u6444\u50cf\u5934\n\n**\u5730\u5740\uff1a** http:\/\/192.168.1.112:8080\/\n\n**\u67e5\u770b\u6d41\u7a0b\uff1a**\n1. \u4f7f\u7528 browser \u5de5\u5177\u6253\u5f00\u5730\u5740\uff08profile: openclaw\uff09\n2. \u5237\u65b0\u9875\u9762\uff08navigate \u540c\u4e00\u5730\u5740\uff09\n3. \u622a\u56fe\uff08screenshot\uff09\n4. \u7528 image \u5de5\u5177\u5206\u6790\u753b\u9762\u5185\u5bb9<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.\u9879\u76ee\u91cd\u70b9\u5185\u5bb9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u2460\u63a8\u6d41\u6838\u5fc3\u662f ffmpeg \u91c7\u96c6 MJPEG \u683c\u5f0f\u89c6\u9891\uff0c\u7ed3\u5408 Python HTTP \u670d\u52a1\u5668\u5b9e\u73b0\u6d4f\u89c8\u5668\u5b9e\u65f6\u64ad\u653e\uff1b<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2461\u8bbe\u8ba1\u4e86\u62a2\u5360\u5f0f\u5355\u5ba2\u6237\u7aef\u673a\u5236\uff0c\u4fdd\u8bc1\u540c\u4e00\u65f6\u95f4\u53ea\u6709\u4e00\u4e2a\u6700\u65b0\u8bbf\u95ee\u7684\u5ba2\u6237\u7aef\u80fd\u8bbf\u95ee\u6444\u50cf\u5934\u6d41\uff1b<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2462OpenClaw \u96c6\u6210\u53ea\u9700\u914d\u7f6e\u63a8\u6d41\u5730\u5740\u548c\u9884\u8bbe\u5de5\u5177\u6d41\u7a0b\uff0c\u5373\u53ef\u5b9e\u73b0\u6444\u50cf\u5934\u753b\u9762\u7684\u83b7\u53d6\u4e0e\u5206\u6790\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u60f3\u4e86\u89e3\u66f4\u591a\u9879\u76ee\u8bf7\u641c\u7d22\u201c\u9879\u76ee\u201d\u6807\u7b7e\u6216\u8005\u8bbf\u95ee\u6211\u7684github\u4ed3\u5e93\u3002\u4ed3\u5e93\u5730\u5740\uff1aQianmoNai&#8217;s R [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":411,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[31,29,30,17],"class_list":["post-410","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-article","tag-ai","tag-openclaw","tag-30","tag-17"],"_links":{"self":[{"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/posts\/410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/comments?post=410"}],"version-history":[{"count":11,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/posts\/410\/revisions"}],"predecessor-version":[{"id":628,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/posts\/410\/revisions\/628"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/media\/411"}],"wp:attachment":[{"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/media?parent=410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/categories?post=410"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/qianmo.icu\/index.php\/wp-json\/wp\/v2\/tags?post=410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}