Select Page
AI 取代分析師?Claude Financial Services Plugins 完整解析

AI 取代分析師?Claude Financial Services Plugins 完整解析

一、什麼是 Claude for Financial Services Plugins?

Claude 是由 Anthropic 推出的 AI 助手,而 Claude for Financial Services Plugins 則是專為金融產業打造的擴充工具組。

該插件系統讓 Claude 不只是聊天工具,而是直接進化為:

👉 投資分析師
👉 財務建模專家
👉 研究助理
👉 Deal sourcing 引擎

透過整合多個金融資料供應商與自動化工作流,Claude 能夠執行完整的金融分析流程。


⚙️ 核心亮點:41 個技能 + 38 個斜線指令

這套系統最強大的地方在於:

🔹 41 個自動觸發技能(Skills)

當你輸入自然語言時,Claude 會自動判斷並執行對應任務,例如:

  • 自動建立 DCF 模型
  • 解析財報(10-K、10-Q)
  • 預測公司未來現金流
  • 計算估值(WACC、IRR)
  • 分析市場趨勢

👉 幾乎等於「AI 自動跑完整投資分析流程」


🔹 38 個 Slash Commands(斜線指令)

你也可以直接用指令操作,例如:

/dcf
/valuation
/earnings-analysis
/company-profile
/deal-sourcing

👉 類似「金融版 CLI + Copilot」


🧠 Claude 可以做哪些金融工作?

以下是實際應用場景:


📊 1️⃣ DCF 建模(Discounted Cash Flow)

https://media.wallstreetprep.com/uploads/2018/04/13150201/dcf1.jpg
https://cdn.corporatefinanceinstitute.com/assets/Discounted-Cash-Flow-DCF-Formula-2.png
https://cdn.corporatefinanceinstitute.com/assets/Valuation-Modeling-in-Excel.png

Claude 可自動:

  • 抓取公司財務數據
  • 預測未來營收與現金流
  • 計算折現率(WACC)
  • 輸出估值結果

👉 傳統需要 2–3 小時 → 現在幾分鐘完成


📑 2️⃣ 財報分析(Financial Statements Analysis)

https://cdn.corporatefinanceinstitute.com/assets/Net-Income-and-Retained-Earnings-1024x696.png
https://www.geckoboard.com/uploads/Cashflow-dashboard-example.png
https://media.wallstreetprep.com/uploads/2021/12/09124400/FB-Table-of-Contents.jpg

Claude 可自動解析:

  • Income Statement
  • Balance Sheet
  • Cash Flow

並產出:

  • 關鍵指標(ROE、毛利率)
  • 成長趨勢
  • 風險警示

🏢 3️⃣ 公司研究(Equity Research)

https://s3.amazonaws.com/thumbnails.venngage.com/template/ff356a68-3ca0-4696-a175-0e5b3354d9b3.png
https://cdn.corporatefinanceinstitute.com/assets/Comparable-company-analysis.png
https://www.marketsandmarkets.com/Images/graph-analytics-market1.jpg

4

輸入公司名稱即可:

  • 自動產出公司報告
  • 分析競爭對手
  • 市場定位與護城河
  • 投資建議(Bull / Bear case)

🔍 4️⃣ Deal Sourcing(投資機會發掘)

https://cdn.prod.website-files.com/65d48bc2b64ae3248b634894/66ebd82b61399462d231a183_66ebd7d8921e5c7861746860_Guide%2520to%2520Increasing%2520Venture%2520Capital%2520Deal%2520Flow%2520in%25202024_1.png
https://images.squarespace-cdn.com/content/v1/5cd8b72c65a707a8fbfe11d2/36700356-3692-4fbb-9fcd-18f25fbdf949/Startup%2BFunding%2BStages.png
https://cdn.prod.website-files.com/5a710020b54d350001949426/60a22b4667b6fc9dfcb7a0ef_Deal%20Sourcing%20to%20Closing.jpeg

Claude 能:

  • 搜尋潛在投資標的
  • 篩選符合條件公司
  • 分析市場機會
  • 建立 pipeline

👉 對 VC / PE / 投資銀行極具價值


🔗 整合 11 個資料供應商

根據官方設計,Claude plugins 已整合多個金融數據來源,例如:

  • 市場數據(股價、交易量)
  • 財報資料
  • 宏觀經濟指標
  • 公司基本面資料

👉 AI 不再「幻想」,而是基於真實數據分析


🧩 技術架構概念(你會有興趣的重點)

從工程角度看,這其實是一個:

Claude (LLM)
   ↓
Plugin Orchestrator
   ↓
Skills (41 modules)
   ↓
Data Providers (11 sources)
   ↓
Structured Financial Output

👉 本質就是「金融版 AI Agent」


⚡ 為什麼這個東西很關鍵?

這代表一個重大趨勢:

🔥 AI 正在取代「重複性高的金融分析工作」

過去:

  • 分析師花 80% 時間整理資料
  • 20% 做判斷

現在:

  • AI 做 80% 分析
  • 人類專注策略與決策

🚀 未來發展

Claude Financial Plugins 只是開始,接下來可能會出現:

  • 自動生成投資簡報(Pitch Deck)
  • 即時交易策略 AI
  • 完整 AI 投資顧問

👉 最終形態:AI 投資銀行

相關文章

CherryNio AI 評測:一站式整合 AI 平台,省下所有訂閱費用

CherryNio AI 評測:一站式整合 AI 平台,省下所有訂閱費用

CherryNio AI(CherryChat.org) 是一個提供 一站式整合 AI 服務平台,聚合了多個頂級大語言模型,如 Sora2、GPT-5、Claude 4.5、Gemini 2.5 Pro 等,讓使用者在同一個介面內即可呼叫不同模型進行聊天、翻譯、分析與客製化應用。

CherryNio 不僅是一個 AI 聊天介面,還能透過 API 金鑰中轉與整合服務,讓開發者在自己的應用中也能使用這些模型。


📌 為什麼 CherryNio AI 可以替代所有 AI 訂閱?

你可能會為 ChatGPT、Gemini、Claude、甚至 Perplexity 分別付費訂閱。但 CherryNio AI 將這些 AI 能力整合在同一個平台,用更彈性的付費方式替代多個訂閱,大幅降低成本並提升效率。


🧪 案例一:沉浸式翻譯

透過 CherryNio 的 沉浸式翻譯功能(類似瀏覽器翻譯插件),你可以把外語內容即時翻譯並呈現在同一個視窗中,不需跳來跳去切換工具。這對長篇網頁閱讀與即時對話翻譯超級實用。


🛒 案例二:Nano Banana

Nano Banana 是影片中提到的一個實際使用案例,可理解為結合 CherryNio 的 AI 能力,用以 生成或優化產品描述/創意寫作等工作流程,展現平台在不同任務上的彈性應用。


🖱 案例三:Cursor 替代品

許多使用者會用 Cursor 來進行程式碼輔助、資料分析等 AI 工作。CherryNio 提供整合式接口與多模型支援,讓你可以在單一平台內呼叫不同模型執行類似 Cursor 的任務,不再需要額外訂閱 Cursor


🔍 案例四:Perplexity 替代品

Perplexity 是一個主打資料檢索與摘要的 AI 工具。在 CherryNio 中,只要選擇合適的模型和 prompt,就可以達到類似的效果:從大量資料中萃取資訊與整理答案,甚至結合多個模型輸出更豐富的答案。


📚 案例五:本地知識庫

CherryNio 支援建立 本地知識庫或整合 API 查詢功能,讓使用者能夠基於自有資料來源進行檢索與對話。這對於企業內部知識管理、客服智能回覆甚至技術文檔搜索都非常有幫助,更是一種 替代雲端知識庫訂閱的方式


💡 使用模式與付費方式

CherryNio AI 的付費方式通常不是傳統的年費訂閱,而是 透過 Token 或套餐方式彈性付費,讓使用者按需支付,減少不必要的訂閱浪費。

參考資料

https://chat.cherrychat.org

Llama-3.2-90B-Vision 實戰:本地端影片偵探與多模態分析完整解析

Llama-3.2-90B-Vision 實戰:本地端影片偵探與多模態分析完整解析

前言:為什麼要做「影片偵探」?

隨著監控攝影機、行車紀錄器、門禁與智慧製造設備的大量部署,「影片」早已成為企業與政府最重要的資料來源之一。然而,影片資料無法被傳統文字搜尋系統理解,長時間的人工作業也帶來極高成本。

這正是「影片偵探(Video Detective)」的價值所在——
讓 AI 看懂影片內容、理解畫面語意、並能以自然語言回答問題

而在 2024 年 9 月 25 日,Meta 推出的 Llama-3.2-90B-Vision(Vision 3.2 90B),正式讓「本地端、多模態、可商業應用」的影片理解系統成為可能。


Llama-3.2-90B-Vision 模型簡介

Vision 3.2 90B 是 Meta 在 Llama 3.2 系列中最強大的開源多模態模型(VLM),也是目前開源社群中少數能與封閉模型正面競爭的選擇。

核心規格與特點

  • 模型規模
    900 億個參數(90B),為 Llama 3.2 系列最大版本
  • 視覺處理能力
    • 圖像理解
    • 圖像描述(Image Captioning)
    • 視覺問答(VQA)
    • 圖表與截圖解析(Excel / PDF / 監控畫面)
  • 技術架構
    採用 Adapter Weights,將視覺編碼器整合至 Llama 3 語言模型
  • 上下文長度
    高達 128K tokens,可處理長影片摘要與多畫面分析
  • 效能表現
    在多模態基準測試中,接近 GPT-4o-mini、Claude 3 Haiku
  • 硬體需求
    通常需要多張 NVIDIA A100 / H100 GPU 進行推理

官方資源:


什麼是「影片偵探」?

「影片偵探」並不是單一模型,而是一套 多階段 AI 架構

  1. 影格抽取(Frame Sampling)
  2. 物件 / 人臉偵測(YOLO / yolo face)
  3. 視覺語意理解(Llama-3.2-90B-Vision)
  4. 語意摘要與事件推論
  5. 自然語言查詢與回應

使用者可以直接詢問:

「這段影片中,有沒有可疑人物?」
「哪一段出現未授權進入?」
「請總結這 2 小時監控畫面的重點事件」


為何選擇 Llama-3.2-90B-Vision?

1️⃣ 本地端部署(On-Premise)

對於以下場景尤其重要:

  • 政府監控系統
  • 金融機構
  • 工廠產線
  • 企業內部資安影像

資料不出內網、不上雲端,符合資安與法規要求。

透過 Ollama 即可在本地部署 Vision 3.2:

👉 https://ollama.com/library/llama3.2-vision:90b

參考教學:


2️⃣ 高度客製化的影片理解流程

你可以自由設計:

  • 先用 YOLO / yolo face 做人臉與物件偵測
  • 再將關鍵影格丟給 Llama-3.2-90B-Vision 理解語意
  • 根據產業需求自訂 Prompt 與規則

例如:

  • 工地安全(是否未戴安全帽)
  • 店面防盜(可疑行為模式)
  • 智慧製造(異常操作流程)

3️⃣ 可合法商業應用的開源模型

與多數封閉 API 不同,Llama 3.2 系列具備商業使用條款,適合:

  • SaaS 影片分析平台
  • 企業內部 AI 系統
  • 客製化專案與整合案

同時避免:

  • API 呼叫成本失控
  • 雲端延遲
  • 資料外洩風險

架構範例:影片偵探系統設計

線上體驗與 API 使用方式

如果暫時沒有高階 GPU,也可以使用 NVIDIA 官方提供的線上版本:


實作系統

影片偵探 Prompt 範例(可直接用)

Prompt 1:單張關鍵影格「事件描述 + 可疑度」

用途:把 YOLO 的偵測結果 + 影格一起丟給 Vision 3.2,產出可索引的敘述。
(強烈建議把 YOLO metadata 一起餵,讓 VLM 不用「瞎猜」類別與位置)

你是一個「影片偵探」AI,負責為監控影格做事件描述與風險判讀。
請根據:
1) 影格畫面內容
2) 我提供的偵測結果(物件類別、bbox、信心值)
輸出結構化 JSON,方便後續索引與查詢。

規則:
- 不要編造畫面中不存在的事物。
- 若畫面資訊不足,請在 unsure 字段說明原因。
- suspicious_score 0~100:越高越可疑,但必須用畫面線索與偵測結果解釋。
- 需同時輸出一段適合全文檢索的繁體中文摘要 summary_zh。

輸入:
timestamp: {{timestamp}}
camera_id: {{camera_id}}
detections: {{yolo_detections_json}}
image: <提供影像>

輸出 JSON 欄位:
{
  "timestamp": "...",
  "camera_id": "...",
  "scene_summary": "...",
  "entities": [
    {"type": "...", "count": 1, "notable": "..."}
  ],
  "actions": ["..."],
  "suspicious_score": 0,
  "suspicious_reasons": ["..."],
  "unsure": ["..."],
  "summary_zh": "..."
}

Prompt 2:多張影格「跨時間軸事件摘要」

用途:把同一事件的多張關鍵影格(例如 5~12 張)一次丟給 128K context,做「時間序列推論」

你是一個監控「影片偵探」AI。以下提供同一段時間序列的多張關鍵影格,
每張都有 timestamp 與偵測結果。請完成:

1) 以時間順序描述發生了什麼事(避免臆測)。
2) 是否存在可疑行為?若有,指出關鍵時間點與依據。
3) 產出適合儲存到事件資料庫的 JSON(含 event_type、start/end、關聯track_id)。

輸入格式(重複多次):
- frame_i.timestamp: ...
- frame_i.detections: ...
- frame_i.image: <圖>

輸出:
- timeline_bullets(條列,含時間)
- event_json:
{
  "event_type": "...",
  "start_time": "...",
  "end_time": "...",
  "actors": [{"track_id":"...","type":"person/vehicle","notes":"..."}],
  "evidence": [{"timestamp":"...","what":"..."}],
  "risk_level": "low/medium/high",
  "search_keywords_zh": ["..."]
}

Prompt 3:使用者查詢「找片段 + 佐證」

用途:RAG 從索引庫找出候選事件後,叫模型做最後整合回答。

你是影片偵探助理。你會收到:
- 使用者問題
- 檢索得到的候選事件清單(含摘要、時間、相機、佐證影格描述)

請:
1) 直接回答問題(繁體中文)
2) 附上最相關的事件時間範圍(start~end)
3) 列出 1~3 個佐證點(引用候選事件的 evidence)
4) 若資訊不足,說明缺什麼

輸入:
question: {{user_question}}
candidates: {{retrieved_events_json}}

輸出:
{
  "answer_zh": "...",
  "best_matches": [
    {"camera_id":"...","time_range":"...","why":"...","evidence":["..."]}
  ],
  "missing_info": ["..."]
}

YOLO + Vision 3.2 實作流程範例

下面是一個「可落地」的最小可行流程(MVP)

Step 0:選擇部署模式

  • 本地端(推薦):Ollama 跑 llama3.2-vision:90b(需強 GPU/多卡)
  • 先用線上 API 驗證流程:NVIDIA 的 Llama-3.2-90B-Vision Instruct(把 pipeline 跑通再回本地)

Step 1:影片 → 影格抽取(Frame Sampling)

策略建議:

  • 監控:每秒 1~2 張 + 場景切分(scene change)
  • 行車:依速度/光流變化調整取樣
  • 事件型:偵測到人/車才加密取樣

Python(OpenCV)骨架:

import cv2
import os

def extract_frames(video_path, out_dir, fps_sample=1):
    os.makedirs(out_dir, exist_ok=True)
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS) or 30
    step = max(int(fps / fps_sample), 1)

    idx = 0
    frame_id = 0
    while True:
        ok, frame = cap.read()
        if not ok:
            break
        if frame_id % step == 0:
            out_path = os.path.join(out_dir, f"frame_{idx:06d}.jpg")
            cv2.imwrite(out_path, frame)
            idx += 1
        frame_id += 1

    cap.release()
    return idx

Step 2:影格 → YOLO / yolo face 偵測(產生 metadata)

可以用:

  • YOLOv8 / YOLOv10 做物件、人、車
  • yolo face(或更強的人臉模型)做臉部框
  • 之後再加 ByteTrack/DeepSORT 形成 track_id

偵測輸出建議格式(JSON):

[
  {"type":"person","conf":0.92,"bbox":[x1,y1,x2,y2]},
  {"type":"car","conf":0.88,"bbox":[x1,y1,x2,y2]},
  {"type":"face","conf":0.81,"bbox":[x1,y1,x2,y2]}
]

Step 3:挑關鍵影格(降低 VLM 成本)

常見挑選法:

  • 有偵測到 person/vehicle 才送 VLM
  • 同一 track_id 每 N 秒只取 1 張代表影格
  • 有事件觸發(越線/闖入/停留過久)才送 VLM

Step 4:送進 Vision 3.2 做語意理解(Ollama 版本)

若你用 Ollama,本質上是:影像 + prompt +(可選)metadata → 回傳描述 JSON。

概念請長這樣:

  1. 把影格讀成 base64 或直接用 Ollama SDK 的 image 欄位
  2. prompt 內含 timestamp、camera_id、detections
  3. 要求模型「輸出 JSON」

依你使用的 Ollama client 調整:

import json

def build_prompt(ts, cam, detections):
    return f"""
你是一個影片偵探AI。請依據影像與偵測結果輸出JSON(繁體中文)。

timestamp: {ts}
camera_id: {cam}
detections: {json.dumps(detections, ensure_ascii=False)}

輸出JSON欄位:
{{
  "timestamp": "...",
  "camera_id": "...",
  "scene_summary": "...",
  "actions": ["..."],
  "suspicious_score": 0,
  "suspicious_reasons": ["..."],
  "summary_zh": "..."
}}
"""

# 送給 Ollama 的地方,依你使用的套件/HTTP API 填上:
# response = ollama.chat(model="llama3.2-vision:90b", messages=[...], images=[...])

Step 5:事件彙整 + 建索引(讓你能「用中文搜影片」)

你至少要存三種資料:

  1. frame-level:每張影格摘要 + detections
  2. event-level:跨影格彙整(start/end、actors、evidence)
  3. 索引:
    • 向量索引(embedding)→ 語意搜尋
    • 關鍵字索引(全文)→ 精準查詢(例如「紅色帽子」「白色車」)

Step 6:查詢(RAG)→ 回答 + 佐證時間軸

流程:

  • 使用者問:「昨天晚上 10 點到 11 點,有沒有人在後門徘徊?」
  • 檢索 event/frame 索引找候選
  • 把候選 evidence 丟給 Prompt 3 做最終回答
  • 回傳:時間範圍 + 相機 + 佐證描述 +(可選)影格連結

你可以直接複製用的「最小資料結構」

frame_record

{
  "frame_id": "cam01_20251216_220501_000123",
  "camera_id": "cam01",
  "timestamp": "2025-12-16T22:05:01",
  "image_path": "/frames/cam01/frame_000123.jpg",
  "detections": [],
  "vlm_json": {},
  "summary_zh": "..."
}

event_record

{
  "event_id": "evt_20251216_cam01_0007",
  "camera_id": "cam01",
  "start_time": "2025-12-16T22:05:00",
  "end_time": "2025-12-16T22:06:30",
  "event_type": "徘徊/闖入/越線/遺留物/群聚",
  "actors": [{"track_id":"p12","type":"person"}],
  "evidence": [
    {"timestamp":"2025-12-16T22:05:15","what":"人物在後門附近停留,反覆靠近門把"}
  ],
  "risk_level": "medium",
  "search_keywords_zh": ["後門","徘徊","可疑人物"]
}

事件規則怎麼定義(越線 / 闖入 / 停留)

A. 共同前置:座標系與 ROI 設定

建議每台攝影機都做一份設定檔(JSON/YAML):

  • ROI 多邊形:例如「後門區域」「禁入區」「收銀台區」
  • Tripwire 線段:例如「門檻線」「圍籬線」
  • 方向向量(可選):判斷越線方向(外→內 / 內→外)

camera_rules.json(示例)

重點:事件規則「不要靠 VLM 猜」,VLM 用在「補語意」,規則用在「可重現、可商業合規」。

B. 越線(Tripwire Crossing)

定義:同一個 track_id 的「腳點/中心點」從線段一側移動到另一側,且跨越時間在合理範圍。

實作要點

  • 取 bbox 底部中心點(foot point)更準:
    foot = ((x1+x2)/2, y2)
  • 記錄上一幀 foot 在直線的「符號」(line side sign)
  • sign 改變(+ → – 或 – → +)= 可能越線
  • 若需要方向:檢查 crossing 前後的位置落在哪個 side

事件輸出

  • event_type: 越線
  • evidence: crossing 發生的 timestamp + 當下影格 id

C. 闖入(Intrusion into Restricted Zone)

定義:目標(person/vehicle)進入「restricted_zone」ROI,且停留超過 N 幀或 N 秒(避免抖動誤判)。

實作要點

  • 以 foot point 是否在多邊形內判斷(比中心點穩)
  • debounce:例如連續 5 幀在區域內才算「進入成立」
  • 退出同理:連續 5 幀不在區域內才算退出

事件輸出

  • event_type: 闖入
  • start_time = 進入成立時間
  • end_time = 退出成立時間

D. 停留 / 徘徊(Loitering)

定義:在指定 zone 內停留超過 min_seconds,且移動速度低於門檻(或移動距離很小但在區域內反覆)。

實作要點

  • per track_id 維護:
    • time_in_zone
    • path_length(foot 點累積距離)
    • avg_speed = path_length / time_in_zone
  • 停留成立條件(示例):
    • time_in_zone >= 20savg_speed <= 15 px/s
  • 可擴充「徘徊」:
    • 在 zone 內來回穿越同一條小線段超過 K 次
    • 或在 zone 內出入(進/出)頻繁

2) 追蹤器接 YOLO(track_id 穩定)

A. 建議組合

  • 偵測:YOLO(person/vehicle/bag…)
  • 追蹤:ByteTrack(速度快、穩)或 DeepSORT(更重但 ReID 強)
  • ID 穩定策略
    1. 只追固定類別:例如只追 person/vehicle(別把雜訊類也追)
    2. 信心值門檻 + NMS:減少跳框
    3. 遮擋容忍:追蹤器允許短暫 lost 再接回
    4. ReID(選配):若常遮擋、跨鏡頭才需要

B. 最小資料流(你系統該長這樣)

frame
  -> YOLO detections: [x1,y1,x2,y2, conf, cls]
  -> Tracker.update(detections)
  -> tracks: [track_id, bbox, cls, conf, state]
  -> event_rules.update(tracks, timestamp)

C. 追蹤輸出建議格式

[
  {"track_id":"p12","type":"person","conf":0.92,"bbox":[...],"foot":[...]}
]

關鍵:後續事件規則都以 track_id 為核心。VLM 的 actors 也引用 track_id,才能串起「時間序列」。

3) 向量索引與查詢(embedding schema、RAG prompt)

A. 你要索引什麼?(兩層:frame-level + event-level)

  • frame-level:細碎、量大,用於精確定位
  • event-level:濃縮、量小,用於查詢與回覆(最重要)

B. 建議的 Embedding Schema(event-level)

每個 event 存:

  • id, camera_id, start_time, end_time
  • event_type
  • actors(含 track_id、類別、特徵詞)
  • summary_zh(VLM + 規則融合)
  • evidence[](時間戳 + 發生描述 + frame_id)
  • keywords_zh[](用於 BM25)
  • embedding(由 summary_zh + keywords_zh 拼成)

event_embedding_text 建議組合

[事件類型] 闖入
[地點] cam01 後門 restricted_zone
[摘要] 一名人物在22:05進入禁入區,停留約35秒後離開。
[關鍵詞] 後門, 禁入區, 闖入, 可疑人物

C. 檢索策略(Hybrid Search)

  1. 關鍵字/BM25:找「後門」「白色車」「紅帽」這種明確詞
  2. 向量/語意:找「徘徊」「鬼鬼祟祟」「疑似踩點」這種抽象語意
  3. 合併 rerank:取 topK events,再丟給 LLM 做最終回答

D. RAG Prompt(查詢 → 回答 + 時間軸 + 點影格)

「可商業用」模板(配之前的 Prompt 3 進化版):

你是影片偵探助理。你會收到:
- 使用者問題 question(繁體中文)
- 候選事件 candidates(由索引檢索得到,含 summary_zh、時間、相機、evidence、frame_link)
你的任務是產生「可行動」的答案:

要求:
1) 先用 3~6 句回答使用者,務必引用 candidates 的內容,不要臆測。
2) 產生時間軸 timeline(依時間排序),每點包含:start~end、camera、event_type、1句描述、frame_link(若有)。
3) 如果 candidates 不足以回答,回 missing_info,並說明需要補哪段時間/哪個相機/哪種事件資料。
4) 全部輸出 JSON,方便前端直接渲染。

輸入:
question: {{question}}
candidates: {{candidates_json}}

輸出 JSON:
{
  "answer_zh": "...",
  "timeline": [
    {
      "camera_id": "...",
      "time_range": "YYYY-MM-DD HH:MM:SS ~ HH:MM:SS",
      "event_type": "...",
      "description": "...",
      "evidence": ["..."],
      "frame_links": ["..."]
    }
  ],
  "missing_info": ["..."]
}

4) 簡單 Web UI(查詢 → 回傳時間軸 → 點開影格)

我給你一個最小 MVP 架構:

A. API 設計(後端)

  • GET /api/search?q=...&from=...&to=...&camera=...
    • 回傳 RAG 結果 JSON(answer_zh + timeline)
  • GET /frames/<frame_id>.jpg
    • 靜態提供影格圖(或簽名 URL)

回傳 JSON(示例)

{
  "answer_zh":"在 22:05~22:06 期間,cam01 後門區域出現 1 次闖入事件...",
  "timeline":[
    {
      "camera_id":"cam01",
      "time_range":"2025-12-16 22:05:00 ~ 22:06:30",
      "event_type":"闖入",
      "description":"人物 p12 進入 restricted_zone 停留約35秒後離開。",
      "evidence":["22:05:15 人物靠近門把","22:06:10 人物離開區域"],
      "frame_links":["/frames/cam01_20251216_220515_000130.jpg"]
    }
  ],
  "missing_info":[]
}

B. 前端(純 HTML+JS 就能跑)

把這段存成 index.html,指向你的 API:

<!doctype html>
<html lang="zh-Hant">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>影片偵探 Demo</title>
  <style>
    body { font-family: system-ui, -apple-system, "Noto Sans TC", sans-serif; margin: 16px; }
    .row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
    input, select, button { padding: 8px; }
    .card { border: 1px solid #ddd; border-radius: 12px; padding: 12px; margin: 10px 0; }
    .timeline-item { cursor: pointer; padding: 10px; border-radius: 10px; }
    .timeline-item:hover { background: #f5f5f5; }
    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
    img { max-width: 100%; border-radius: 12px; border: 1px solid #eee; }
    pre { background:#fafafa; padding:10px; border-radius:10px; overflow:auto; }
  </style>
</head>
<body>
  <h1>影片偵探 Demo</h1>

  <div class="row">
    <input id="q" size="40" placeholder="例如:後門有沒有人徘徊?白色車出現在哪?" />
    <input id="from" type="datetime-local" />
    <input id="to" type="datetime-local" />
    <select id="camera">
      <option value="">全部相機</option>
      <option value="cam01">cam01</option>
      <option value="cam02">cam02</option>
    </select>
    <button id="btn">查詢</button>
  </div>

  <div class="grid">
    <div>
      <div class="card">
        <h3>答案</h3>
        <div id="answer">尚未查詢</div>
      </div>

      <div class="card">
        <h3>時間軸</h3>
        <div id="timeline"></div>
      </div>

      <div class="card">
        <h3>除錯(回傳 JSON)</h3>
        <pre id="raw">{}</pre>
      </div>
    </div>

    <div>
      <div class="card">
        <h3>影格預覽</h3>
        <div id="frameInfo">點選左側時間軸項目查看影格</div>
        <img id="frame" src="" alt="" style="display:none;" />
      </div>
    </div>
  </div>

<script>
  const $ = (id) => document.getElementById(id);

  function toISOParam(dtLocal) {
    // 直接傳給後端即可(你可在後端再轉時區)
    return dtLocal ? encodeURIComponent(dtLocal) : "";
  }

  function renderTimeline(items) {
    const root = $("timeline");
    root.innerHTML = "";
    if (!items || items.length === 0) {
      root.innerHTML = "<div>沒有找到事件</div>";
      return;
    }
    items.forEach((it, idx) => {
      const div = document.createElement("div");
      div.className = "timeline-item";
      div.innerHTML = `
        <div><b>${it.time_range}</b>|${it.camera_id}|${it.event_type}</div>
        <div>${it.description}</div>
      `;
      div.onclick = () => {
        const link = (it.frame_links && it.frame_links[0]) ? it.frame_links[0] : "";
        $("frameInfo").innerText = link ? `影格:${link}` : "此事件沒有影格連結";
        if (link) {
          $("frame").src = link;
          $("frame").style.display = "block";
        } else {
          $("frame").style.display = "none";
        }
      };
      root.appendChild(div);
    });
  }

  $("btn").onclick = async () => {
    const q = $("q").value.trim();
    const from = toISOParam($("from").value);
    const to = toISOParam($("to").value);
    const camera = encodeURIComponent($("camera").value);

    const url = `/api/search?q=${encodeURIComponent(q)}&from=${from}&to=${to}&camera=${camera}`;
    const res = await fetch(url);
    const data = await res.json();

    $("answer").innerText = data.answer_zh || "";
    renderTimeline(data.timeline || []);
    $("raw").innerText = JSON.stringify(data, null, 2);
  };
</script>
</body>
</html>

最小可跑的 Flask 後端實作(MVP)

最小可跑的 Flask 後端實作(MVP)

  • GET /api/search:用最簡單的「關鍵字 + 語意(可選)」搜尋事件,回傳前端需要的 answer_zh + timeline JSON
  • GET /frames/<path>:提供影格圖片(靜態檔)
  • 內建一份 data/events.json 假資料,讓你不用先把整個 pipeline 做完也能 Demo 起來
  • 前端就用你前面那份 index.html(我也一起放進 static/

你可以直接照以下檔案建立專案。

專案結構

video-detective-mvp/
  app.py
  requirements.txt
  data/
    events.json
  frames/
    cam01_20251216_220515_000130.jpg   (放你自己的影格檔)
  static/
    index.html

requirements.txt

Flask==3.0.3

data/events.json(假資料,可自行新增)

[
  {
    "event_id": "evt_20251216_cam01_0007",
    "camera_id": "cam01",
    "start_time": "2025-12-16T22:05:00",
    "end_time": "2025-12-16T22:06:30",
    "event_type": "闖入",
    "actors": [{"track_id":"p12","type":"person"}],
    "summary_zh": "一名人物進入後門禁入區,停留約35秒後離開。",
    "keywords_zh": ["後門","禁入區","闖入","可疑人物"],
    "evidence": [
      {"timestamp":"2025-12-16T22:05:15","what":"人物靠近門把並進入禁入區"},
      {"timestamp":"2025-12-16T22:06:10","what":"人物離開禁入區"}
    ],
    "frame_links": ["/frames/cam01_20251216_220515_000130.jpg"]
  },
  {
    "event_id": "evt_20251216_cam02_0011",
    "camera_id": "cam02",
    "start_time": "2025-12-16T21:40:10",
    "end_time": "2025-12-16T21:41:05",
    "event_type": "越線",
    "actors": [{"track_id":"v03","type":"vehicle"}],
    "summary_zh": "一輛車跨越門檻線由外往內進入區域。",
    "keywords_zh": ["越線","車輛","進入","門檻線"],
    "evidence": [
      {"timestamp":"2025-12-16T21:40:22","what":"車輛前輪跨越門檻線"}
    ],
    "frame_links": []
  },
  {
    "event_id": "evt_20251216_cam01_0009",
    "camera_id": "cam01",
    "start_time": "2025-12-16T23:10:00",
    "end_time": "2025-12-16T23:11:00",
    "event_type": "停留",
    "actors": [{"track_id":"p33","type":"person"}],
    "summary_zh": "一名人物在後門區域停留約60秒,移動緩慢,疑似徘徊。",
    "keywords_zh": ["後門","停留","徘徊","可疑"],
    "evidence": [
      {"timestamp":"2025-12-16T23:10:20","what":"人物在後門附近停留且來回踱步"}
    ],
    "frame_links": []
  }
]

static/index.html(直接用你前面 UI;這裡給一份可直接用的)

這份會呼叫 /api/search,點時間軸會開影格。

<!doctype html>
<html lang="zh-Hant">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>影片偵探 Demo</title>
  <style>
    body { font-family: system-ui, -apple-system, "Noto Sans TC", sans-serif; margin: 16px; }
    .row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
    input, select, button { padding: 8px; }
    .card { border: 1px solid #ddd; border-radius: 12px; padding: 12px; margin: 10px 0; }
    .timeline-item { cursor: pointer; padding: 10px; border-radius: 10px; }
    .timeline-item:hover { background: #f5f5f5; }
    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
    img { max-width: 100%; border-radius: 12px; border: 1px solid #eee; }
    pre { background:#fafafa; padding:10px; border-radius:10px; overflow:auto; }
  </style>
</head>
<body>
  <h1>影片偵探 Demo</h1>

  <div class="row">
    <input id="q" size="42" placeholder="例如:後門有沒有人徘徊?禁入區闖入?越線?" />
    <input id="from" type="datetime-local" />
    <input id="to" type="datetime-local" />
    <select id="camera">
      <option value="">全部相機</option>
      <option value="cam01">cam01</option>
      <option value="cam02">cam02</option>
    </select>
    <button id="btn">查詢</button>
  </div>

  <div class="grid">
    <div>
      <div class="card">
        <h3>答案</h3>
        <div id="answer">尚未查詢</div>
      </div>

      <div class="card">
        <h3>時間軸</h3>
        <div id="timeline"></div>
      </div>

      <div class="card">
        <h3>除錯(回傳 JSON)</h3>
        <pre id="raw">{}</pre>
      </div>
    </div>

    <div>
      <div class="card">
        <h3>影格預覽</h3>
        <div id="frameInfo">點選左側時間軸項目查看影格</div>
        <img id="frame" src="" alt="" style="display:none;" />
      </div>
    </div>
  </div>

<script>
  const $ = (id) => document.getElementById(id);

  function renderTimeline(items) {
    const root = $("timeline");
    root.innerHTML = "";
    if (!items || items.length === 0) {
      root.innerHTML = "<div>沒有找到事件</div>";
      return;
    }
    items.forEach((it) => {
      const div = document.createElement("div");
      div.className = "timeline-item";
      div.innerHTML = `
        <div><b>${it.time_range}</b>|${it.camera_id}|${it.event_type}</div>
        <div>${it.description}</div>
      `;
      div.onclick = () => {
        const link = (it.frame_links && it.frame_links[0]) ? it.frame_links[0] : "";
        $("frameInfo").innerText = link ? `影格:${link}` : "此事件沒有影格連結";
        if (link) {
          $("frame").src = link;
          $("frame").style.display = "block";
        } else {
          $("frame").style.display = "none";
        }
      };
      root.appendChild(div);
    });
  }

  $("btn").onclick = async () => {
    const q = $("q").value.trim();
    const from = $("from").value;
    const to = $("to").value;
    const camera = $("camera").value;

    const url = `/api/search?q=${encodeURIComponent(q)}&from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&camera=${encodeURIComponent(camera)}`;
    const res = await fetch(url);
    const data = await res.json();

    $("answer").innerText = data.answer_zh || "";
    renderTimeline(data.timeline || []);
    $("raw").innerText = JSON.stringify(data, null, 2);
  };
</script>
</body>
</html>

app.py(Flask 後端:搜尋 + 回傳 timeline + 靜態影格)

from __future__ import annotations

import json
import os
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

from flask import Flask, jsonify, request, send_from_directory


BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_PATH = os.path.join(BASE_DIR, "data", "events.json")
FRAMES_DIR = os.path.join(BASE_DIR, "frames")


def parse_dt(s: str) -> Optional[datetime]:
    """
    Accepts:
      - "" -> None
      - HTML datetime-local: "2025-12-16T22:05"
      - ISO: "2025-12-16T22:05:00"
    """
    if not s:
        return None
    s = s.strip()
    for fmt in ("%Y-%m-%dT%H:%M", "%Y-%m-%dT%H:%M:%S"):
        try:
            return datetime.strptime(s, fmt)
        except ValueError:
            pass
    # Fallback: try fromisoformat (may accept seconds/microseconds)
    try:
        return datetime.fromisoformat(s)
    except ValueError:
        return None


def load_events() -> List[Dict[str, Any]]:
    if not os.path.exists(DATA_PATH):
        return []
    with open(DATA_PATH, "r", encoding="utf-8") as f:
        return json.load(f)


def dt_in_range(start: datetime, end: datetime, q_from: Optional[datetime], q_to: Optional[datetime]) -> bool:
    if q_from and end < q_from:
        return False
    if q_to and start > q_to:
        return False
    return True


def text_score(query_tokens: List[str], event: Dict[str, Any]) -> int:
    """
    Minimal keyword scoring:
      - match in event_type, summary_zh, keywords_zh, evidence.what
    """
    blob_parts = []
    blob_parts.append(str(event.get("event_type", "")))
    blob_parts.append(str(event.get("summary_zh", "")))
    blob_parts.extend(event.get("keywords_zh", []) or [])
    for ev in event.get("evidence", []) or []:
        blob_parts.append(str(ev.get("what", "")))

    blob = " ".join(blob_parts).lower()
    score = 0
    for t in query_tokens:
        if not t:
            continue
        if t in blob:
            score += 10
    return score


def tokenize_zh(query: str) -> List[str]:
    """
    MVP 簡化版 tokenization:
    - 以空白切
    - 再把常見關鍵詞直接保留(你可改成 jieba/ckip/hfl 分詞)
    """
    q = (query or "").strip().lower()
    if not q:
        return []
    tokens = [x for x in q.replace(",", " ").replace(",", " ").split() if x]
    # 也把一些常見事件詞補入(避免使用者只打「徘徊」但資料用「停留」)
    synonyms = {
        "徘徊": ["停留"],
        "停留": ["徘徊"],
        "闖入": ["入侵", "禁入"],
        "越線": ["跨線", "穿越"],
        "後門": ["門口"]
    }
    expanded = []
    for t in tokens:
        expanded.append(t)
        for s in synonyms.get(t, []):
            expanded.append(s)
    return expanded


def build_timeline_item(e: Dict[str, Any]) -> Dict[str, Any]:
    def fmt_range(s: str, t: str) -> str:
        try:
            sdt = datetime.fromisoformat(s)
            tdt = datetime.fromisoformat(t)
            return f"{sdt:%Y-%m-%d %H:%M:%S} ~ {tdt:%Y-%m-%d %H:%M:%S}"
        except Exception:
            return f"{s} ~ {t}"

    evidence = []
    for ev in e.get("evidence", []) or []:
        ts = ev.get("timestamp", "")
        what = ev.get("what", "")
        if ts and what:
            evidence.append(f"{ts} {what}")
        elif what:
            evidence.append(what)

    return {
        "camera_id": e.get("camera_id", ""),
        "time_range": fmt_range(e.get("start_time", ""), e.get("end_time", "")),
        "event_type": e.get("event_type", ""),
        "description": e.get("summary_zh", ""),
        "evidence": evidence[:3],
        "frame_links": e.get("frame_links", []) or [],
    }


def build_answer(query: str, matched: List[Dict[str, Any]]) -> str:
    if not query.strip():
        return "請輸入查詢內容(例如:後門 徘徊、禁入區 闖入、越線)。"
    if not matched:
        return "沒有找到符合條件的事件。你可以嘗試放寬時間範圍、改用關鍵詞(例如:後門、闖入、停留、越線)。"

    # MVP:用最前面幾筆拼一段可讀答案(之後你可接 LLM/RAG)
    top = matched[:3]
    parts = []
    parts.append(f"找到 {len(matched)} 筆候選事件,最相關的如下:")
    for e in top:
        parts.append(f"- {e.get('camera_id','')}|{e.get('event_type','')}|{e.get('summary_zh','')}")
    return "\n".join(parts)


app = Flask(__name__, static_folder="static", static_url_path="/")


@app.get("/")
def index():
    # 直接用 static/index.html
    return app.send_static_file("index.html")


@app.get("/frames/<path:filename>")
def frames(filename: str):
    # 提供影格圖片
    return send_from_directory(FRAMES_DIR, filename)


@app.get("/api/search")
def api_search():
    q = request.args.get("q", "")
    camera = request.args.get("camera", "").strip()
    q_from = parse_dt(request.args.get("from", ""))
    q_to = parse_dt(request.args.get("to", ""))

    events = load_events()
    tokens = tokenize_zh(q)

    # 先做基本篩選(camera + time range)
    filtered = []
    for e in events:
        if camera and e.get("camera_id") != camera:
            continue
        s = parse_dt(e.get("start_time", "")) or datetime.min
        t = parse_dt(e.get("end_time", "")) or datetime.min
        if not dt_in_range(s, t, q_from, q_to):
            continue
        filtered.append(e)

    # 關鍵字打分排序(MVP)
    scored: List[Tuple[int, Dict[str, Any]]] = []
    for e in filtered:
        score = text_score(tokens, e)
        if tokens and score <= 0:
            continue
        scored.append((score, e))

    scored.sort(key=lambda x: x[0], reverse=True)
    matched = [e for _, e in scored][:20]

    resp = {
        "answer_zh": build_answer(q, matched),
        "timeline": [build_timeline_item(e) for e in matched],
        "missing_info": [] if matched else ["建議擴大時間範圍或指定相機,並使用關鍵字:後門、闖入、停留、越線。"],
    }
    return jsonify(resp)


if __name__ == "__main__":
    os.makedirs(FRAMES_DIR, exist_ok=True)
    app.run(host="0.0.0.0", port=5000, debug=True)

啟動方式

cd video-detective-mvp
python -m venv .venv
# Windows: .venv\Scripts\activate
source .venv/bin/activate

pip install -r requirements.txt
python app.py

打開瀏覽器:http://localhost:5000/ ,MVP 已經能跑 UI + 查詢 + 時間軸 + 點影格。接下來可以逐步替換:

build_answer() 換成 RAG(把 topK candidates 丟給 Llama-3.2-90B-Vision / 或先用任一 LLM)

data/events.json 換成資料庫(SQLite / PostgreSQL)

text_score() 換成 Hybrid Search

BM25(全文索引) + 向量索引(Qdrant/pgvector)

進階實用版本

SQLite 儲存 events(含初始化與匯入)
向量欄位介面(先 stub,未來可無痛換 Qdrant / pgvector)
事件詳情 API(/api/event/<id>,支援多影格 / 證據)
✅ SQLite(事件資料庫)
✅ Qdrant(Hybrid:向量語意 + 關鍵字)
/api/search + /api/event/<id> + /frames/...
✅ 前端 UI(查詢 → 時間軸 → 點事件 → 顯示 evidence + 多影格縮圖/預覽)
✅ ByteTrack「真正接法」我也給你兩種:可跑的簡易 IoU Tracker(保底) + ByteTrack
✅ Flask(單一服務)
✅ 影片抽幀(或直接讀 frames 資料夾)
✅ YOLO 偵測(Ultralytics)
✅ 追蹤(保底可跑 IoU Tracker;之後你只要替換 tracker 類別就能上 ByteTrack)
✅ 事件規則引擎(越線 / 闖入 / 停留)
✅ 自動寫入 SQLite(events.db)
✅ 自動重建 Qdrant 向量索引(可選:Qdrant 有開就做,沒開就跳過)
✅ 前端 UI 立刻能查到事件並點開影格

之後再補

cooldown 去重機制(避免事件爆量)

事件合併(同一個 track 的闖入/停留合併成單一 event,帶 start/end)

把 VLM(Ollama Vision 90B)用於事件摘要:只對「關鍵事件」抽 1~3 張影格做語意補強(成本更可控)


結語:Vision 3.2 90B 為開源影片 AI 帶來新世代

Llama-3.2-90B-Vision 不只是「能看圖的 LLM」,而是讓:

  • 影片搜尋
  • 監控分析
  • 行為理解
  • 事件推論

真正進入 本地端、可控、可商業化 的時代,對於想打造「影片偵探」的團隊而言,它是目前開源世界中最具實力的選擇之一

參考資料

https://mermaid.live

Gemini Nano Banana Pro 超強 15 大應用整理:設計師、行銷人、創作者必看

Gemini Nano Banana Pro 超強 15 大應用整理:設計師、行銷人、創作者必看

Nano Banana Pro 剛出,就馬上成為「圖像生成與視覺應用」領域的新標準,它不只是畫圖工具,而是一個高度可控、支援中文、能維持一致性的 AI 視覺引擎

以下整理 Google Nano Banana Pro 的 15 種超強應用場景,無論你是設計師、行銷企劃、教育工作者或產品經理,都能立即上手。

Google Nano Banana Pro

1️⃣ 簡報/企劃海報快速生成

只要輸入企劃主題與風格,Nano Banana 就能產出投影片主視覺、封面海報、提案插圖,大幅減少找素材與修圖時間。


2️⃣ 草圖秒變產品實景圖

手繪線稿、低擬真草圖,可直接轉為擬真產品照,特別適合工業設計、UI / UX、新創產品驗證。


3️⃣ 設計材質紋理

可精準生成木頭、金屬、皮革、布料、玻璃等高解析材質貼圖,支援不同光源與粗糙度設定。


4️⃣ 角色一致性

透過角色描述與參考設定,即使多次生成,也能維持臉型、服裝、風格高度一致,非常適合漫畫、品牌代言角色。


5️⃣ 品牌指南手冊

一次生成品牌色彩、字體風格、視覺範例,快速完成 Brand Book 視覺示意。


6️⃣ 生成各種尺寸

同一視覺可自動輸出 社群貼文、橫幅廣告、直式限動、網站 Banner 等多尺寸版本。


7️⃣ 食譜圖超清晰

針對食物細節表現極佳,油光、層次、質地自然,特別適合餐飲菜單、食譜部落格、外送平台


8️⃣ 多國語言菜單 Menu

結合 Gemini 的語言能力,可直接生成多國語言版本菜單圖片,且排版自然、不違和。


9️⃣ 景點/教材圖卡

可用於旅遊介紹、歷史教材、地理圖卡、兒童學習素材,風格可愛或寫實皆可。


🔟 風格轉換更精細

支援攝影風、插畫風、3D 風、日系、美式、復古等,且保留原圖構圖與細節


1️⃣1️⃣ 教學假桌面生成

快速生成「假作業系統畫面」、「教學用後台介面」,適合製作教學簡報與線上課程


1️⃣2️⃣ 腳本 → 連續劇照

輸入分鏡或劇本段落,即可生成連續一致的劇照畫面,對影視提案與動畫前期極有幫助。


1️⃣3️⃣ 中文超強

對繁體中文理解精準,無論是菜單、教材、標語、情境文字,都能自然呈現,不再需要英文轉譯


1️⃣4️⃣ 畫 3D 圖也可以

可生成擬 3D、等角視圖、產品爆炸圖概念,適合簡報與技術說明使用。


1️⃣5️⃣ 任意切換焦距

同一場景可切換廣角、標準、特寫、微距,視覺敘事能力大幅提升。


參考與官方資源

建立自己的 HopeJR 機器人教學|外骨骼與手套控制精準人形機械手臂

建立自己的 HopeJR 機器人教學|外骨骼與手套控制精準人形機械手臂

建立自己的 HopeJR 機器人!

建造您自己的 SO-101 機器人

你有想要一隻能做精密手部操作的人形機器人?

HopeJR 正是這樣一款開源專案:具備人手與機械手臂,搭配外骨骼(exoskeleton)與手套控制,能實現靈巧、精確的手部動作,非常適合高階操作任務。

以下帶大家從硬體到軟體,完整建立 HopeJR 的流程教學與心得。

HopeJR 是什麼?

  • HopeJR 是舉債於 LeRobot 專案下的一款開放源碼人型機械手臂與手的系統。使用者可以以手套與外骨骼裝置控制 HopeJR 的手與手臂動作。
  • 它包含幾個模組化的部分:機械手、機械臂、手套(glove)、外骨骼(exoskeleton)控制器。硬體、軟體皆為開源。

建置 HopeJR 教學

以下是步驟示範,讓你從零開始組裝與操作 HopeJR。

一、環境與先決條件

  • 有外骨骼與手套控制器(homunculus_glove/homunculus_arm 等)
  • 有機械手與機械手臂硬體模組
  • 一台能連接這些裝置的電腦,USB 接口、驅動支援
  • 安裝 Python 3.10+,PyTorch 等軟體環境,以及 LeRobot 套件

二、安裝 LeRobot 套件

git clone https://github.com/huggingface/lerobot.git
cd lerobot
pip install -e ".[hopejr]"

三、設備端與通訊埠設定(Device Configuration)

  1. 使用 lerobot-find-port 指令來找出每個 HopeJR 組件(手臂、手、手套、外骨骼控制器)的 USB 埠。
  2. 記錄每個模組對應的 port,例如 /dev/tty.usbmodemXXXX 或 Windows 上的類似 COM 埠,用於後續校正和遙控。

四、校正(Calibration)

為了讓控制更加精確,你必須為每個組件做校正:

  • 手(Hand)校正:把手指關節全部移動至可動範圍,設定最小/最大角度。
  • 手套(Glove)校正:同樣地,移動手套中每根手指的所有關節,記錄該裝置讀值的最小與最大。
  • 手臂(Arm)校正:肩膀、手肘、手腕的 pitch/yaw/roll 等關節都要校正動作範圍。
  • 外骨骼(Exoskeleton)校正:這部分讓人手動作透過外骨骼映射到 HopeJR 的機械手臂,在校正時要移動所有關節達到最大與最小。

校正結果會保存在本地,例如 ~/.cache/huggingface/lerobot/calibration/... 的路徑內。

五、遠端操作(Teleoperation)

校正完成後,就能使用遙控命令讓你手與手臂控制 HopeJR 機械手部分動作:

lerobot-teleoperate \
  --robot.type=hope_jr_hand \
  --robot.port=<手的 USB 埠> \
  --robot.id=blue \
  --robot.side=right \
  --teleop.type=homunculus_glove \
  --teleop.port=<手套 USB 埠> \
  --teleop.id=red \
  --teleop.side=right \
  --display_data=true \
  --fps=30

對手臂同樣使用遠端操作命令,只是 robot.type 填 hope_jr_arm,teleop.type 用 homunculus_arm。

六、錄製、重放與訓練(Record/Replay/Train)

  • Record:錄製你用手套/外骨骼控制手部或手臂的操作數據,配合影像或鏡頭視訊。這些資料可以用來訓練模型。
  • Replay:重放之前錄製的數據,測試操作是否準確。
  • Train:若你想讓你的 HopeJR 有自主行為或能在某些動作上優化,可以用 LeRobot 的訓練指令,搭配錄製的資料或模擬環境進行強化學習或 imitation learning。

HopeJR 的優勢與適用場景

優勢項目說明
精準控制外骨骼 + 手套 + 校正過程能實現手指、手腕、肩肘等關節的細緻動作。
模組開源HopeJR 所有硬體與軟體部分為開源,可以自行組裝或改裝。
適合高階任務適用於需要精細手部操作的任務,例如物件操作、握持/擺動、復健機器人等。
社群與生態支持LeRobot 有模型、範例、文檔與活躍社群可協助新手入門。

延伸閱讀

官網:

https://github.com/huggingface/lerobot

說明:

https://huggingface.co/docs/lerobot/hope_jr

硬體教學:

https://huggingface.co/docs/lerobot/so101

Google Nano Banana 公仔製作教學:一張自拍秒變桌上模型圖

Google Nano Banana 公仔製作教學:一張自拍秒變桌上模型圖

你是否曾想把自己的照片變成桌上擺放的公仔模型?現在可以透過 Google 最新的 Nano Banana 模型(亦稱 Gemini 2.5 Flash Image)!你只需在 Google AI StudioGemini App 上上傳人像照片,就能透過文字描述,生成一張效果逼真的「真人公仔照」,免費、免 VPN,簡單又好玩!

快速上手教學(適用 AI Studio 或 Gemini App)

  1. 前往 Google AI Studio 或使用 Gemini App
  2. 選擇模型為 Gemini 2.5 Flash Image(Nano Banana)
  3. 上傳你的照片(建議用正面自拍)。
  4. 輸入以下 prompt:
  5. A hyper-realistic 1/7 scale figurine of a character, designed as a finished commercial product, placed on an iMac computer desk with a white Apple keyboard. The figurine stands on a clean, round transparent acrylic base with no labels or text. Professional studio lighting highlights the sculpted details. On the iMac screen in the background, display the ongoing ZBrush modeling process of the same figurine, showing the contrast between “work in progress” and the finished product. Next to the figurine, place its packaging box with rounded corners and a transparent front window. The box is open at the top, revealing only the inner transparent plastic clamshell, and its height is slightly taller than the figure, realistically sized to contain it.
  6. 約莫 10 至 12 秒後,Nano Banana 將生成一張高質感的“公仔照”。
  7. 若不滿意,可繼續輸入新的指令,進行多回合優化。

Whisk

除了用 Gemini, Google AI Studio 之外,也有一個很親民的工具可以使用,Google Whisk 是 Google Labs 推出的一款 AI 圖像生成實驗工具,它的核心理念是「用影像提示取代文字提示」,讓創作過程更直觀、有趣又快速 。

使用方式總覽:

  • 使用者可以上傳或拖曳多張圖片,分別指定為 主體 (Subject)場景 (Scene)風格 (Style)
  • Whisk 會透過 Gemini 多模態 AI 生成每張圖片的描述(把圖像翻譯成文字),再透過 Imagen 3 來自動生成最終的合成圖像 。
  • 若使用者沒有圖片,也可以點擊類似骰子的按鈕,讓 Whisk 自動產生候選圖像作為創作起點。
  • 最終生成的作品同時附上自動生成的文字提示,使用者可收藏(favorite)、下載或進入「refine」(精修)模式繼續調整。

功能特色與優勢

特性說明
直觀的視覺提示流程不需撰寫冗長文字,只要用圖像即可引導 AI 創作,降低入門門檻。
高度創意自由度主體、場景、風格可任意混搭,產出意想不到的視覺效果。
可精調的 promptWhisk 生成的文字提示可供修改,讓你能微調構圖或細節。
迅速生成,適合腦力激盪非用於精細修圖,而是強調快速試想與探索多種視覺可能性。

背後技術架構

Whisk 的生成流程依靠兩大 Google 強力 AI 模型:

  1. Gemini:將使用者上傳的圖片轉換成文字描述(Image → Text)。
  2. Imagen 3:根據 Gemini 輸出的描述,生成新圖像(Text → Image)。

這種「I2T 加 T2I」方式,讓工具更懂得捕捉「精髓」而非複製細節,有助於創造富有變化的視覺作品


應用場景與用戶心得

適用於:

  • 創意構想:設計師、行銷、插畫師與教育者都能用它腦爆想法,例如試出角色在不同情境下的樣貌。
  • 教育工具:老師可以讓學生用上傳的照片快速生成故事插圖或原型設計。
  • 快速原型設計:用於生成貼紙、胸章、絨毛玩偶等產品概念,效率高又富創意。

最新資訊與發展動向

  • Whisk 自 2024 年底正式推出以來,已從初期美國限定,擴展至全球超過百個國家使用。
  • 隨著技術升級,Whisk 已經從最初的 Imagen 3 模型,陸續升級到更新、更強的 Imagen 4,圖像品質更高。
  • 雖然最近 Google 也廣為報導其視頻工具 Flow,但 Whisk 主要還是聚焦在圖像創作領域。

官網

https://aistudio.google.com/prompts/new_chat

https://gemini.google.com

參考資料