feat(deepdata): 引入修改密码、文件预览组件与清洗算子增强,deepflow 改名为 deep-flow
本次同步 09-12 至 09-28 的代码变更,主要内容: 新增功能 - 修改密码:后端新增 ChangePasswordRequest DTO 与UserController/UserService 对应接口,前端新增 ChangePasswordDialog 并在 Header 接入入口 - 文件预览组件重构:file-preview 目录拆分出 VideoPreview(支持 blobUrl 直传)、 ImagePreview、AudioPreview,index.tsx 统一调度各类型预览器 - 新增 my_ops 算子模块:markitdown_parser、mineru_pdf_parser、text_quality_evaluator - 新增 tools/minio-file-server MinIO 文件服务 功能增强 - 数据清洗:cleaning_task_service/scheduler/routes 重构, data_juicer_executor 与 wrapper 增强,OperatorTable/LogsTable/TaskDetail 同步调整 - 数据标注:annotation sync.py、project.py 接口与schema 更新 - 数据采集、算子市场、首页与菜单路由相关页面调整 - DataJuicer 算子接入(useOperatorOperations、dj_ops_schema) 结构调整 - deepflow/ 更名为 deep-flow/(419 个文件重命名) - 新增 helm 部署编排、GitHub Actions 之外的自建镜像构建脚本 未纳入本次提交 - scripts-backup/、runtime/python-executor-backup/、components_backup/(备份目录) - *.backup*、__pycache__ 产物 - zh_core_web_sm-3.8.0-py3-none-any.whl(46MB 构建产物) - DataX/(内嵌独立仓库,远程亦未收录其源码)
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from deepdata.core.base_op import OPERATORS
|
||||
|
||||
# 注意:module_path 必须与解压后的目录层级对应。
|
||||
# 按照规范,压缩包名 mineru_pdf_parser.zip 解压后应位于 ops.user.mineru_pdf_parser
|
||||
OPERATORS.register_module(
|
||||
module_name='MinerUPdfParser',
|
||||
module_path="ops.user.mineru_pdf_parser.process"
|
||||
)
|
||||
@@ -0,0 +1,132 @@
|
||||
name: "MinerU PDF Parser"
|
||||
description: "调用远端 MinerU FastAPI 服务解析 PDF,提取 Markdown 文本与图片。支持 Base64 内联、本地保存或上传至外部图床服务。"
|
||||
language: "python"
|
||||
raw_id: "MinerUPdfParser"
|
||||
version: "1.0.0"
|
||||
vendor: "DeepData"
|
||||
modal: "text"
|
||||
inputs: "text"
|
||||
outputs: "text"
|
||||
types:
|
||||
- 'collect'
|
||||
|
||||
release:
|
||||
- "首次发布:支持远程 MinerU API 调用"
|
||||
- "新增图片外部上传图床功能"
|
||||
- "优化图片处理优先级与容错机制"
|
||||
|
||||
runtime:
|
||||
memory: 524288000
|
||||
cpu: 0.1
|
||||
gpu: 0
|
||||
npu: 0
|
||||
storage: 50MB
|
||||
|
||||
metrics:
|
||||
- name: "吞吐量"
|
||||
metric: "Depends on API latency"
|
||||
- name: "准确率"
|
||||
metric: "Depends on MinerU version"
|
||||
|
||||
settings:
|
||||
mineruApi:
|
||||
name: "MinerU API 地址"
|
||||
description: "MinerU 服务基础 URL (如 http://mineru.ailab.ccoop.cc:18585)"
|
||||
type: "input"
|
||||
defaultVal: "http://mineru.ailab.ccoop.cc:18585"
|
||||
required: true
|
||||
server_url:
|
||||
name: "兼容服务地址"
|
||||
description: "VLM/Hybrid 后端所需的 OpenAI 兼容地址 (可选)"
|
||||
type: "input"
|
||||
defaultVal: ""
|
||||
required: false
|
||||
backend:
|
||||
name: "解析后端"
|
||||
description: "选择解析模式"
|
||||
type: "select"
|
||||
defaultVal: "pipeline"
|
||||
required: true
|
||||
options:
|
||||
- label: "本地推理 (Pipeline)"
|
||||
value: "pipeline"
|
||||
- label: "远程 VLM (HTTP Client)"
|
||||
value: "vlm-http-client"
|
||||
- label: "远程混合 (Hybrid HTTP Client)"
|
||||
value: "hybrid-http-client"
|
||||
lang_list:
|
||||
name: "语言列表"
|
||||
description: "输入语言代码,多个语言用逗号分隔 (如 ch,en)"
|
||||
type: "input"
|
||||
defaultVal: "ch"
|
||||
required: true
|
||||
parse_method:
|
||||
name: "解析方法"
|
||||
description: "PDF 解析方式"
|
||||
type: "select"
|
||||
defaultVal: "auto"
|
||||
required: true
|
||||
options:
|
||||
- label: "自动"
|
||||
value: "auto"
|
||||
- label: "OCR"
|
||||
value: "ocr"
|
||||
- label: "文本"
|
||||
value: "txt"
|
||||
formula_enable:
|
||||
name: "启用公式识别"
|
||||
description: "是否解析数学公式"
|
||||
type: "switch"
|
||||
defaultVal: "true"
|
||||
checkedLabel: "是"
|
||||
unCheckedLabel: "否"
|
||||
table_enable:
|
||||
name: "启用表格解析"
|
||||
description: "是否解析文档中的表格"
|
||||
type: "switch"
|
||||
defaultVal: "true"
|
||||
checkedLabel: "是"
|
||||
unCheckedLabel: "否"
|
||||
return_images:
|
||||
name: "返回图片数据"
|
||||
description: "是否将提取的图片数据包含在响应中(关闭可节省带宽)"
|
||||
type: "switch"
|
||||
defaultVal: "true"
|
||||
checkedLabel: "是"
|
||||
unCheckedLabel: "否"
|
||||
timeout:
|
||||
name: "请求超时 (秒)"
|
||||
description: "HTTP 请求最大等待时间"
|
||||
type: "slider"
|
||||
defaultVal: 900
|
||||
min: 60
|
||||
max: 3600
|
||||
step: 60
|
||||
embed_images_base64:
|
||||
name: "嵌入 Base64 图片"
|
||||
description: "是否将提取的图片以 Base64 格式内联到 Markdown 文本中"
|
||||
type: "switch"
|
||||
defaultVal: "true"
|
||||
checkedLabel: "是"
|
||||
unCheckedLabel: "否"
|
||||
|
||||
# ================= 新增:外部图床上传配置 =================
|
||||
upload_images:
|
||||
name: "上传图片到外部服务"
|
||||
description: "开启后,将自动把提取的图片上传至下方指定的 API,并替换为可访问的网络链接"
|
||||
type: "switch"
|
||||
defaultVal: "true"
|
||||
checkedLabel: "开启上传"
|
||||
unCheckedLabel: "使用 Base64/本地"
|
||||
upload_api_url:
|
||||
name: "图片上传 API 地址"
|
||||
description: "接收图片的 POST 接口 (支持 multipart/form-data),例如: http://example.com/upload/form"
|
||||
type: "input"
|
||||
defaultVal: "http://mfs.ailab.ccoop.cc:18585/upload/form"
|
||||
required: false
|
||||
preview_base_url:
|
||||
name: "图片预览基础地址"
|
||||
description: "用于拼接上传返回的相对路径,生成最终 URL。例如: http://example.com"
|
||||
type: "input"
|
||||
defaultVal: "http://mfs.ailab.ccoop.cc:18585/"
|
||||
required: false
|
||||
@@ -0,0 +1,371 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import re
|
||||
import requests
|
||||
import mimetypes
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from typing import Dict, Any
|
||||
from loguru import logger
|
||||
|
||||
try:
|
||||
from deepdata.core.base_op import Mapper
|
||||
except ImportError:
|
||||
print("⚠️ 本地调试模式:未找到 'deepdata' 库,正在启用模拟 (Mock) Mapper...")
|
||||
class Mapper:
|
||||
def __init__(self, *args, **kwargs): pass
|
||||
|
||||
# 匹配绝对地址图片链接
|
||||
ABSOLUTE_IMG_REGEX = re.compile(r'!\[([^\]]*)\]\((https?://[^\s)]+)\)')
|
||||
# 匹配相对地址图片链接
|
||||
RELATIVE_IMG_REGEX = re.compile(r'!\[([^\]]*)\]\((images/[^)]+)\)')
|
||||
|
||||
class MinerUPdfParser(Mapper):
|
||||
"""基于远端 MinerU FastAPI 的 PDF 结构化解析算子"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.mineru_api = kwargs.get("mineruApi", "http://mineru.ailab.ccoop.cc:18585").rstrip("/")
|
||||
self.server_url = kwargs.get("server_url", "")
|
||||
self.backend = kwargs.get("backend", "pipeline")
|
||||
|
||||
lang_str = kwargs.get("lang_list", "ch")
|
||||
self.lang_list = [lang.strip() for lang in lang_str.split(",") if lang.strip()]
|
||||
|
||||
self.parse_method = kwargs.get("parse_method", "auto")
|
||||
self.formula_enable = kwargs.get("formula_enable", "true") == "true"
|
||||
self.table_enable = kwargs.get("table_enable", "true") == "true"
|
||||
self.return_images = kwargs.get("return_images", "true") == "true"
|
||||
|
||||
# 🔑 Base64 嵌入开关
|
||||
self.embed_images_base64 = kwargs.get("embed_images_base64", "true") == "true"
|
||||
|
||||
# 🆕 下载远程图片开关
|
||||
self.download_remote_images = kwargs.get("download_remote_images", "true") == "true"
|
||||
|
||||
# 🆕 上传到外部图床配置
|
||||
self.upload_images = kwargs.get("upload_images", "false") == "true"
|
||||
self.upload_api_url = kwargs.get("upload_api_url", "").strip()
|
||||
# 注意:这里保留 rstrip("/"),但在拼接函数中会做更严格的清洗
|
||||
self.preview_base_url = kwargs.get("preview_base_url", "").rstrip("/")
|
||||
|
||||
try:
|
||||
self.timeout = int(kwargs.get("timeout", 900))
|
||||
except (ValueError, TypeError):
|
||||
self.timeout = 900
|
||||
|
||||
self.images_subdir = "images"
|
||||
|
||||
def _is_same_origin(self, url1: str, url2: str) -> bool:
|
||||
"""判断两个 URL 是否同源(协议 + 域名 + 端口)"""
|
||||
if not url1 or not url2:
|
||||
return False
|
||||
try:
|
||||
parsed1 = urlparse(url1)
|
||||
parsed2 = urlparse(url2)
|
||||
return (parsed1.scheme == parsed2.scheme and
|
||||
parsed1.hostname == parsed2.hostname and
|
||||
parsed1.port == parsed2.port)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
async def _call_api_async(self, file_path: str) -> dict:
|
||||
url = f"{self.mineru_api}/file_parse"
|
||||
file_obj = open(file_path, "rb")
|
||||
|
||||
try:
|
||||
form = aiohttp.FormData()
|
||||
form.add_field("files", file_obj, filename=Path(file_path).name, content_type="application/pdf")
|
||||
|
||||
def add_field(key: str, value: Any):
|
||||
if value is None: return
|
||||
if isinstance(value, list):
|
||||
form.add_field(key, ",".join(map(str, value)))
|
||||
elif isinstance(value, bool):
|
||||
form.add_field(key, str(value).lower())
|
||||
else:
|
||||
form.add_field(key, str(value))
|
||||
|
||||
add_field("backend", self.backend)
|
||||
add_field("lang_list", self.lang_list)
|
||||
add_field("parse_method", self.parse_method)
|
||||
add_field("formula_enable", self.formula_enable)
|
||||
add_field("table_enable", self.table_enable)
|
||||
add_field("server_url", self.server_url)
|
||||
add_field("return_md", True)
|
||||
add_field("return_images", self.return_images)
|
||||
add_field("response_format_zip", False)
|
||||
add_field("start_page_id", 0)
|
||||
add_field("end_page_id", 99999)
|
||||
add_field("output_dir", "./output")
|
||||
|
||||
timeout = aiohttp.ClientTimeout(total=self.timeout)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(url, data=form) as resp:
|
||||
resp_text = await resp.text()
|
||||
if resp.status != 200:
|
||||
raise Exception(f"MinerU API HTTP {resp.status}: {resp_text}")
|
||||
return json.loads(resp_text)
|
||||
finally:
|
||||
file_obj.close()
|
||||
|
||||
def _upload_image_to_api(self, img_bytes: bytes, filename: str) -> str:
|
||||
"""上传图片到外部 API 并返回完整预览 URL"""
|
||||
if not self.upload_images or not self.upload_api_url:
|
||||
return ""
|
||||
|
||||
ct = mimetypes.guess_type(filename)[0] or "image/jpeg"
|
||||
files = {"file": (filename, BytesIO(img_bytes), ct)}
|
||||
|
||||
try:
|
||||
resp = requests.post(self.upload_api_url, files=files, timeout=30)
|
||||
resp.raise_for_status()
|
||||
|
||||
try:
|
||||
data = resp.json()
|
||||
# 尝试获取 path, url, data 字段,兼容不同返回格式
|
||||
raw_path = data.get("path") or data.get("url") or data.get("data")
|
||||
path = str(raw_path) if raw_path else ""
|
||||
except:
|
||||
path = resp.text.strip()
|
||||
|
||||
# 🛡️ 核心修复:强力清洗路径,防止双斜杠 //
|
||||
# 1. 去除首尾的引号、空格
|
||||
clean_path = path.strip('\"\' ')
|
||||
# 2. 去除开头的斜杠 (因为我们会手动拼接一个分隔符)
|
||||
clean_path = clean_path.lstrip('/')
|
||||
|
||||
# 3. 去除 Base URL 末尾的斜杠和空格
|
||||
clean_base = self.preview_base_url.strip(' /')
|
||||
|
||||
if not clean_base or not clean_path:
|
||||
raise ValueError("Base URL 或 路径 解析为空")
|
||||
|
||||
# 4. 标准拼接:base/path
|
||||
return f"{clean_base}/{clean_path}"
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ 图片上传失败 [{filename}]: {e}")
|
||||
return ""
|
||||
|
||||
def _decode_image_data(self, img_data: Any) -> bytes:
|
||||
"""将 Base64 字符串或原始数据转为 bytes"""
|
||||
if isinstance(img_data, bytes):
|
||||
return img_data
|
||||
if isinstance(img_data, str):
|
||||
try:
|
||||
if img_data.startswith("data:image"):
|
||||
_, b64_data = img_data.split(",", 1)
|
||||
else:
|
||||
b64_data = img_data
|
||||
return base64.b64decode(b64_data)
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
def _download_image(self, url: str) -> bytes:
|
||||
"""下载图片"""
|
||||
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=30)
|
||||
response.raise_for_status()
|
||||
return response.content
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ 下载图片失败 [{url}]: {e}")
|
||||
return None
|
||||
|
||||
def _process_all_images(self, md_content: str, images_dict: Dict[str, Any], export_path: str) -> str:
|
||||
"""统一处理所有类型图片(绝对地址 + 相对地址)"""
|
||||
|
||||
# ==================== 1. 处理相对地址图片 ====================
|
||||
def replace_relative(match):
|
||||
alt = match.group(1)
|
||||
img_filename = match.group(2) # 如 "images/xxx.jpg"
|
||||
img_data = images_dict.get(Path(img_filename).name) # 取文件名部分
|
||||
|
||||
if not img_data:
|
||||
return match.group(0)
|
||||
|
||||
img_bytes = self._decode_image_data(img_data)
|
||||
if not img_bytes:
|
||||
return match.group(0)
|
||||
|
||||
# 🟢 优先上传
|
||||
if self.upload_images and self.upload_api_url:
|
||||
upload_url = self._upload_image_to_api(img_bytes, Path(img_filename).name)
|
||||
if upload_url:
|
||||
return f""
|
||||
|
||||
# 🟡 Base64 内联
|
||||
if self.embed_images_base64:
|
||||
if isinstance(img_data, str) and not img_data.startswith("data:"):
|
||||
ext = Path(img_filename).suffix.lstrip('.') or 'jpg'
|
||||
img_data = f"data:image/{ext};base64,{img_data}"
|
||||
return f""
|
||||
|
||||
# 🔴 本地存储
|
||||
final_export_path = os.path.abspath(export_path)
|
||||
images_dir = os.path.join(final_export_path, self.images_subdir)
|
||||
os.makedirs(images_dir, exist_ok=True)
|
||||
|
||||
save_path = os.path.join(images_dir, Path(img_filename).name)
|
||||
with open(save_path, "wb") as f:
|
||||
f.write(img_bytes)
|
||||
|
||||
return f".name)})"
|
||||
|
||||
md_content = RELATIVE_IMG_REGEX.sub(replace_relative, md_content)
|
||||
|
||||
# ==================== 2. 处理绝对地址图片 ====================
|
||||
def replace_absolute(match):
|
||||
alt = match.group(1)
|
||||
img_url = match.group(2)
|
||||
|
||||
# 判断是否与预览地址同源
|
||||
if self.upload_images and self.preview_base_url and self._is_same_origin(img_url, self.preview_base_url):
|
||||
return match.group(0) # 同源则保留原链接
|
||||
|
||||
# 不同源则下载后上传
|
||||
img_bytes = self._download_image(img_url)
|
||||
if not img_bytes:
|
||||
return match.group(0) # 下载失败保留原链接
|
||||
|
||||
filename = Path(urlparse(img_url).path).name or "remote_image.jpg"
|
||||
|
||||
# 🟢 优先上传
|
||||
if self.upload_images and self.upload_api_url:
|
||||
upload_url = self._upload_image_to_api(img_bytes, filename)
|
||||
if upload_url:
|
||||
return f""
|
||||
|
||||
# 🟡 Base64 内联
|
||||
if self.embed_images_base64:
|
||||
mime_type = mimetypes.guess_type(img_url)[0] or "image/png"
|
||||
b64_str = base64.b64encode(img_bytes).decode("utf-8")
|
||||
return f""
|
||||
|
||||
# 🔴 本地存储
|
||||
import hashlib
|
||||
mime_type = mimetypes.guess_type(img_url)[0] or "image/png"
|
||||
ext = mimetypes.guess_extension(mime_type) or '.jpg'
|
||||
new_filename = hashlib.md5(img_url.encode()).hexdigest()[:12] + ext
|
||||
|
||||
final_export_path = os.path.abspath(export_path)
|
||||
images_dir = os.path.join(final_export_path, self.images_subdir)
|
||||
os.makedirs(images_dir, exist_ok=True)
|
||||
|
||||
save_path = os.path.join(images_dir, new_filename)
|
||||
with open(save_path, "wb") as f:
|
||||
f.write(img_bytes)
|
||||
|
||||
return f"})"
|
||||
|
||||
if self.download_remote_images:
|
||||
md_content = ABSOLUTE_IMG_REGEX.sub(replace_absolute, md_content)
|
||||
|
||||
return md_content
|
||||
|
||||
def execute(self, sample: Dict[str, Any]) -> Dict[str, Any]:
|
||||
filename = sample.get("fileName", "")
|
||||
filepath = sample.get("filePath", "")
|
||||
export_path = sample.get("export_path", "./output")
|
||||
|
||||
# 支持多种图片格式和 PDF
|
||||
if not filename.lower().endswith((".png", ".jpeg", ".jpg", ".webp", ".gif", ".pdf")):
|
||||
return sample
|
||||
|
||||
start_time = time.time()
|
||||
try:
|
||||
logger.info(f"🚀 开始解析: {filename}")
|
||||
response = asyncio.run(self._call_api_async(filepath))
|
||||
|
||||
if "results" not in response:
|
||||
raise ValueError("Invalid response structure: missing 'results'")
|
||||
|
||||
results = response["results"]
|
||||
file_stem = Path(filepath).stem
|
||||
task_data = results.get(file_stem)
|
||||
if not task_data and results:
|
||||
task_data = next(iter(results.values()))
|
||||
|
||||
md_content = task_data.get("md_content", "")
|
||||
images_dict = task_data.get("images", {})
|
||||
|
||||
# 统一处理所有图片(相对地址 + 绝对地址)
|
||||
md_content = self._process_all_images(md_content, images_dict, export_path)
|
||||
logger.info("✅ 所有图片处理完成")
|
||||
|
||||
sample["text"] = md_content
|
||||
sample["target_type"] = "md"
|
||||
logger.info(f"✅ 解析成功: {filename} | 耗时 {time.time() - start_time:.2f}s | MD 长度 {len(md_content)}")
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"❌ 解析失败 {filename}: {e}")
|
||||
raise
|
||||
|
||||
return sample
|
||||
|
||||
# ==========================================================
|
||||
# 🧪 本地调试与测试入口
|
||||
# ==========================================================
|
||||
if __name__ == "__main__":
|
||||
print("="*60)
|
||||
print("🧪 MinerUPdfParser 本地测试")
|
||||
print("="*60)
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_pdf = os.path.join(current_dir, "test.pdf")
|
||||
|
||||
if not os.path.exists(test_pdf):
|
||||
print(f"❌ 找不到测试文件: {test_pdf}")
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = os.path.join(current_dir, "test_output")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
mock_sample = {
|
||||
"fileName": "test.pdf",
|
||||
"filePath": test_pdf,
|
||||
"export_path": output_dir,
|
||||
"text": "",
|
||||
"target_type": ""
|
||||
}
|
||||
|
||||
mock_kwargs = {
|
||||
"mineruApi": "http://mineru.ailab.ccoop.cc:18585",
|
||||
"embed_images_base64": "true",
|
||||
"download_remote_images": "true",
|
||||
"upload_images": "true",
|
||||
"upload_api_url": "http://127.0.0.1:8000/upload/form",
|
||||
"preview_base_url": "http://127.0.0.1:8000/file" # 即使这里没带斜杠,代码也会处理带斜杠的情况
|
||||
}
|
||||
|
||||
try:
|
||||
parser = MinerUPdfParser(**mock_kwargs)
|
||||
result = parser.execute(mock_sample)
|
||||
|
||||
# 保存生成的 Markdown
|
||||
md_content = result.get("text", "")
|
||||
md_file = os.path.join(output_dir, "result.md")
|
||||
with open(md_file, "w", encoding="utf-8") as f:
|
||||
f.write(md_content)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✅ 测试通过!")
|
||||
print(f"📂 输出目录: {result.get('export_path')}")
|
||||
print(f"📄 结果文件: {md_file}")
|
||||
print("="*60)
|
||||
except Exception as e:
|
||||
print(f"\n❌ 测试失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,4 @@
|
||||
aiohttp>=3.9.0
|
||||
loguru>=0.7.0
|
||||
hashlib
|
||||
pydantic-settings>=2.0.0
|
||||
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,466 @@
|
||||
# 云深处科技
|
||||
|
||||

|
||||
|
||||
# 山猫M2oPro
|
||||
|
||||
# 声 明
|
||||
|
||||
• 本手册是杭州云深处科技股份有限公司所拥有的信息资产,在未经杭州云深处科技股份有限公司许可的情况下,严禁转载或修改本手册的部分或全部内容。
|
||||
• 本手册对产品“山猫 M20 Pro”的基本组件、运输储存、具体操作、异常处理及技术参数等内容进行了相应的说明。请务必在认真阅读并充分理解本手册的基础上使用产品。
|
||||
• 有关安全使用的基本事项,在“阅读提示”中有详细描述,阅读本手册前请务必熟读,以确保正确使用。本手册中的图及照片为代表性示例,与所购买产品或有细节差异。
|
||||
本手册会根据产品改进、规格变更等原因进行适当的修改。本手册所记载的内容,不排除有误记或遗漏的可能性。如本手册破损、丢失或对本手册内容有疑问,请及时与我公司联系。
|
||||
• 客户擅自拆卸、改造产品引发的故障不在本公司保修范围之内,详见“售后服务与保修条款”。
|
||||
|
||||
# 阅读提示
|
||||
|
||||
# 符号说明
|
||||
|
||||
使用前(安装、运输、保养、检修),请务必熟读并掌握本手册,熟知设备知识和安全事项后再开始使用,本手册的安全事项分为“注意”、“强制”、“禁止”三大项,即使是“注意”所记载的内容,也会因情况不同而产生严重后果,因此任何一条注意事项都极为重要,请务必严格遵守。
|
||||
|
||||
注意使用提示或操作建议,若使用或操作不当,可能造成设备不能正常使用或损坏。
|
||||
|
||||
强制 必须遵守的事项。
|
||||
|
||||

|
||||
|
||||
禁止 禁止的事项,误操作时有危险,可能发生轻伤事故或对设备造成损害。
|
||||
|
||||
# 获取帮助
|
||||
|
||||
为了获得更多帮助资源,以协助您熟练地使用山猫 M20 Pro,您还可以访问杭州云深处科技的企业网站:http://www.deeprobotics.cn 。
|
||||
|
||||
# 重要安全提示
|
||||
|
||||
在启动山猫 M20 Pro 之前,请确保机器人周围有 2 米以上的空间,避免与人或物体发生碰撞。
|
||||
|
||||

|
||||
|
||||
• 机器人通过楼梯或斜坡时,请勿站在机器人下方的楼梯、平台或斜坡上,以避免机器人跌落时可能会造成的人员损伤。
|
||||
|
||||
• 当机器人在使用过程中出现腿乱摆、剧烈晃动等非正常现象时,请按下手柄上的【急停】键以启动软急停功能,使正在运动的机器人进入保护状态。机器人将自动趴下,待排查问题后,再解除紧急停止即可正常操作机器人。
|
||||
|
||||
# 目录
|
||||
|
||||
声 明 2
|
||||
|
||||
# 阅读提示 2
|
||||
|
||||
获取帮助 2
|
||||
|
||||
# 重要安全提示 3
|
||||
|
||||
# 1 产品介绍 5
|
||||
|
||||
1.1 产品概述 5
|
||||
|
||||
1.2 产品清单 5
|
||||
|
||||
1.3 部件名称 6
|
||||
|
||||
1.4 主要参数 7
|
||||
|
||||
# 1.5 灯光说明 8
|
||||
|
||||
1.5.1 电源开关 8
|
||||
1.5.2 电池 9
|
||||
1.5.3 指示灯带 9
|
||||
|
||||
1.6模式与步态 10
|
||||
|
||||
# 2 使用与操作 11
|
||||
|
||||
# 2.1 准备工作 11
|
||||
|
||||
2.1.1 环境确认 11
|
||||
2.1.2 搬运提示 11
|
||||
2.1.3 电量及机体检查 12
|
||||
|
||||
# 2.2 充电 13
|
||||
|
||||
# 2.3 启动 14
|
||||
|
||||
2.3.1 准备 14
|
||||
2.3.2 开机 14
|
||||
2.3.3 连接 14
|
||||
|
||||
# 2.4 控制界面 15
|
||||
|
||||
2.5 设置界面 18
|
||||
|
||||
# 2.6 应急操作 18
|
||||
|
||||
2.6.1 软急停 18
|
||||
2.6.2 硬急停 18
|
||||
2.6.3 过热保护 19
|
||||
2.6.4 低电量保护 19
|
||||
2.6.5 其他情形 19
|
||||
2.7 关机 19
|
||||
2.8 负载加装 20
|
||||
|
||||
# 3 注意事项 21
|
||||
|
||||
3.1使用环境注意事项 21
|
||||
3.2 电池注意事项 21
|
||||
3.3 其他注意事项 22
|
||||
|
||||
# 4 常见问题与解决 23
|
||||
|
||||
# 5 运输储存 24
|
||||
|
||||
5.1 运输 24
|
||||
5.2 储存 24
|
||||
5.3 搬运 24
|
||||
|
||||
# 6 保修条款 25
|
||||
|
||||
6.1 保修政策 25
|
||||
6.2 保修范围 25
|
||||
6.3 送修说明 26
|
||||
|
||||
# 7 废弃处理 27
|
||||
|
||||
# 1 产品介绍
|
||||
|
||||
# 1.1 产品概述
|
||||
|
||||
山猫 M20 Pro 是一款四轮足式机器人,每条腿上有 4 个电机,共有 16 个自由度,具有多种运动方式。头部和尾部分别配备 1个激光雷达和 1 个广角相机,内部搭载 3台工业级高性能主机,用于运动控制和环境感知计算以及建图导航等。
|
||||
|
||||

|
||||
|
||||
# 1.2 产品清单
|
||||
|
||||

|
||||
|
||||
# 1.3 部件名称
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
※机器人采用前后对称设计,遥控接收天线所在侧为机器人前侧;硬急停按钮所在侧为机器人后侧。
|
||||
|
||||
# 1.4 主要参数
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>本体规格</td></tr><tr><td rowspan=1 colspan=1>站立尺寸 (长×宽×高)</td><td rowspan=1 colspan=1>820mmX430mmX570mm</td></tr><tr><td rowspan=1 colspan=1>趴地尺寸 (长 ×宽× 高)</td><td rowspan=1 colspan=1>820mmX600mmX185mm</td></tr><tr><td rowspan=1 colspan=1>足部轮胎尺寸</td><td rowspan=1 colspan=1>7英寸</td></tr><tr><td rowspan=1 colspan=1>整机重量</td><td rowspan=1 colspan=1>35kg</td></tr></table>
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>电气参数</td></tr><tr><td rowspan=1 colspan=1>电池容量</td><td rowspan=1 colspan=1>4.5Ah (单块电池,25C环境下测得的理想数据)</td></tr><tr><td rowspan=1 colspan=1>电池额定电压</td><td rowspan=1 colspan=1>72V</td></tr><tr><td rowspan=1 colspan=1>智能充电管家输入电压</td><td rowspan=1 colspan=1>100V~240V</td></tr><tr><td rowspan=1 colspan=1>智能充电管家输出</td><td rowspan=1 colspan=1>84V/3A (单路)</td></tr><tr><td rowspan=1 colspan=1>电池充电时长</td><td rowspan=1 colspan=1>1.5h~2h(单块电池,充电管家支持双电池同时充电)</td></tr><tr><td rowspan=1 colspan=1>外接电源接口</td><td rowspan=1 colspan=1>72V;24V;5V(USB)</td></tr><tr><td rowspan=1 colspan=1>外接通讯接口</td><td rowspan=1 colspan=1>Ethernet; WiFi;USB</td></tr><tr><td rowspan=1 colspan=1>自主充电功能</td><td rowspan=1 colspan=1>支持 (选配)</td></tr></table>
|
||||
|
||||
<table><tr><td colspan="2" rowspan="1">运动参数</td></tr><tr><td colspan="1" rowspan="1">最高速度</td><td colspan="1" rowspan="1">5m/s(极限测试数据,出于安全考虑不对外开放,实际产品限速至3m/s)</td></tr><tr><td colspan="1" rowspan="1">最大工作速度</td><td colspan="1" rowspan="1">2m/s</td></tr><tr><td colspan="1" rowspan="1">可攀爬斜坡最大坡度</td><td colspan="1" rowspan="1">±45°(视地面材质情况或有差异)</td></tr><tr><td colspan="1" rowspan="1">可攀爬连续楼梯最大高度</td><td colspan="1" rowspan="1">25cm</td></tr><tr><td colspan="1" rowspan="1">可攀爬连续楼梯最大坡度</td><td colspan="1" rowspan="1">±45°</td></tr><tr><td colspan="1" rowspan="1">可攀爬单级台阶最大高度</td><td colspan="1" rowspan="1">80cm</td></tr><tr><td colspan="1" rowspan="1">空载运动续航时间</td><td colspan="1" rowspan="1">3h (双电池供电)</td></tr><tr><td colspan="1" rowspan="1">有效负载运动续航时间</td><td colspan="1" rowspan="1">2.5h (双电池供电)</td></tr><tr><td colspan="1" rowspan="1">有效负载</td><td colspan="1" rowspan="1">15kg</td></tr><tr><td colspan="1" rowspan="1">最大负载</td><td colspan="1" rowspan="1">50kg(极限测试数据)</td></tr></table>
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>传感器参数</td></tr><tr><td rowspan=1 colspan=1>96线激光雷达</td><td rowspan=1 colspan=1>2个</td></tr><tr><td rowspan=1 colspan=1>广角相机</td><td rowspan=1 colspan=1>2个</td></tr><tr><td rowspan=1 colspan=1>照明灯</td><td rowspan=1 colspan=1>2个</td></tr></table>
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>环境参数</td></tr><tr><td rowspan=1 colspan=1>防护等级</td><td rowspan=1 colspan=1>IP66 (安装两块电池)</td></tr><tr><td rowspan=1 colspan=1>工作环境温度</td><td rowspan=1 colspan=1>-20℃ ~55°℃</td></tr></table>
|
||||
|
||||
※上述数据在理想环境下与双电池供电模式下测得,实际结果或有偏差。
|
||||
|
||||
# 1.5 灯光说明
|
||||
|
||||
# 1.5.1 电源开关
|
||||
|
||||

|
||||
|
||||
# 1.5.2 电池
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=3>O熄灭 常亮 Q闪烁 流水灯</td></tr><tr><td rowspan=1 colspan=2>电池指示灯灯况</td><td rowspan=1 colspan=1>电量说明</td></tr><tr><td rowspan=1 colspan=1>●●·</td><td rowspan=1 colspan=1>四颗LED 灯常亮</td><td rowspan=1 colspan=1>电量为75%~100%</td></tr><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>前三颗LED 灯常亮</td><td rowspan=1 colspan=1>电量为50%~75%</td></tr><tr><td rowspan=1 colspan=1>●●00</td><td rowspan=1 colspan=1>前两颗 LED 灯常亮</td><td rowspan=1 colspan=1>电量为25%~50%</td></tr><tr><td rowspan=1 colspan=1>●OOO</td><td rowspan=1 colspan=1>第一颗LED 灯常亮</td><td rowspan=1 colspan=1>电量为10%~25%</td></tr><tr><td rowspan=1 colspan=1>Q○○○</td><td rowspan=1 colspan=1>第一颗 LED 灯慢速闪烁</td><td rowspan=1 colspan=1>电量为3%~10%</td></tr><tr><td rowspan=1 colspan=1>Q</td><td rowspan=1 colspan=1>第一颗闪烁30秒后关机</td><td rowspan=1 colspan=1>电量低于3%</td></tr><tr><td rowspan=1 colspan=1>O</td><td rowspan=1 colspan=1>流水灯依次亮起</td><td rowspan=1 colspan=1>充电中,亮灯数量表示当前电量</td></tr></table>
|
||||
|
||||
# 1.5.3 指示灯带
|
||||
|
||||
机器人背部的前后方的各有一条指示灯带,指示灯的灯语如下所示:
|
||||
|
||||
单个指示灯点亮
|
||||
|
||||
<table><tr><td rowspan=1 colspan=1>灯光</td><td rowspan=1 colspan=1>状态与说明</td></tr><tr><td rowspan=1 colspan=1>白色常亮</td><td rowspan=1 colspan=1>机器人当前处于常规模式</td></tr><tr><td rowspan=1 colspan=1>蓝色常亮</td><td rowspan=1 colspan=1>机器人当前正在使用导航,处于自主站立或自主运动状态</td></tr></table>
|
||||
|
||||
※单个指示灯点亮时,另一个指示灯处于熄灭状态,点亮的指示灯所在的侧向为机器人前进方向。
|
||||
|
||||
两个指示灯点亮
|
||||
|
||||
<table><tr><td rowspan=1 colspan=1>灯光</td><td rowspan=1 colspan=1>状态与说明</td></tr><tr><td rowspan=1 colspan=1>白色流水灯</td><td rowspan=1 colspan=1>等待连接/连接中,机器人在等待APP连接</td></tr><tr><td rowspan=1 colspan=1>白色常亮</td><td rowspan=1 colspan=1>连接已完成,机器人处于趴下状态</td></tr><tr><td rowspan=1 colspan=1>绿色流水灯</td><td rowspan=1 colspan=1>机器人当前正在充电桩上充电,每侧电池仓内电池的电量由同侧的指示灯流水显示,亮灯数量反映该侧电池电量水平;若某侧电池仓内未安装电池或安装的电池未开启,则同侧的指示灯熄灭,且未处于未开启状态的电池不会充电。</td></tr><tr><td rowspan=2 colspan=1>黄色常亮</td><td rowspan=1 colspan=1>机器人当前处于硬急停保护状态</td></tr><tr><td rowspan=1 colspan=1>持续性异常预警,可在手柄端查看具体异常预警信息</td></tr><tr><td rowspan=1 colspan=1>黄色闪烁</td><td rowspan=1 colspan=1>辅助模式下检测到机器人有与周围障碍物碰撞的风险</td></tr><tr><td rowspan=1 colspan=1>红色常亮</td><td rowspan=1 colspan=1>持续性异常保护,可在手柄端查看具体异常保护信息</td></tr></table>
|
||||
|
||||
# 1.6 模式与步态
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>使用模式</td></tr><tr><td rowspan=1 colspan=1>常规模式</td><td rowspan=1 colspan=1>该模式下完全由用户通过手柄控制机器人的运动</td></tr><tr><td rowspan=1 colspan=1>辅助模式</td><td rowspan=1 colspan=1>该模式下环境感知功能将协助用户通过手柄操作机器人安全移动</td></tr><tr><td rowspan=1 colspan=1>导航模式</td><td rowspan=1 colspan=1>该模式下无需用户参与,机器人自主沿规划路径移动</td></tr></table>
|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>步态</td></tr><tr><td rowspan=1 colspan=1>基础</td><td rowspan=1 colspan=1>该步态限制最高速度为2m/s,适用于常规结构化地形</td></tr><tr><td rowspan=1 colspan=1>楼梯</td><td rowspan=1 colspan=1>该步态适用于攀爬楼梯,不适用于平地行走</td></tr><tr><td rowspan=1 colspan=1>高台</td><td rowspan=1 colspan=1>该步态支持攀爬80cm以下的高台,不支持攀爬楼梯</td></tr></table>
|
||||
|
||||
# 2 使用与操作
|
||||
|
||||
# 2.1 准备工作
|
||||
|
||||
# 2.1.1 环境确认
|
||||
|
||||
<table><tr><td>0</td><td>·请确保在场的操作人员和非操作人员已仔细阅读本文档,了解基本操 作规范和安全注意事项。 在启动山猫M20Pro之前,确保机器人周围有2米以上的空间,避免与 人或物体发生碰撞。 ·请在-20℃~55C的环境中使用山猫M20 Pro。</td></tr></table>
|
||||
|
||||
# 2.1.2 搬运提示
|
||||
|
||||
山猫 ${ \mathsf { M } } 2 0 { \mathsf { P r o } }$ 的质量约为 35 千克,建议由两人配合搬运:一人站在机器人的前侧,握住机器人两边的小腿下侧;另一人站在机器人的后侧,握住机器人两边的小腿下侧完成搬运工作。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
• 搬运山猫 ${ \mathsf { M } } 2 0 { \mathsf { P r o } }$ 时,请轻拿轻放,并保持电源和电池处于关闭状态。• 刚结束运行的机器人腿部金属件温度较高,请等待其降至合适的温度后再搬运。• 搬运时注意避开机身上的易夹手点位和关节运动范围,以防止夹、划伤。
|
||||
|
||||
机器人的易高温与易夹手位置如下图所示:
|
||||
|
||||

|
||||
|
||||
※红色字母标识出的是易高温位置,橙色数字标识出的为易夹手点位。
|
||||
|
||||
※ $\textcircled { \mathsf { A } } / \textcircled { \mathsf { B } } / \textcircled { \mathsf { C } }$ 分别为侧摆 / 髋 / 膝关节所在位置, $\textcircled{0}$ 为与足部关节连接的轮毂面,运行结束后,关节模组及附近的金属件易高温,请勿触碰!
|
||||
|
||||
※ $\textcircled{1}$ 为头/尾部侧壳与关节之间的夹缝; $\textcircled{2}$ 为小腿与身体侧壳之间的夹缝; $\textcircled{3}$ 为大腿与小腿之间的夹缝。
|
||||
※图中机身右侧的注意点位未完全标出,机身采用左右对称设计,右侧的位置信息以左侧为参考。
|
||||
※机器人的易夹手点位包括但不限于上述位置,搬运机器人时请参考 2.1.2。
|
||||
|
||||
# 2.1.3 电量及机体检查
|
||||
|
||||
<table><tr><td>O</td><td>短按电池按钮查看电量,建议在同时安装两块电池的情况下开机使用。 确保遥控手柄电量充足。 确保机器人外观无明显损坏。 确保急停开关处于可触发状态。</td></tr></table>
|
||||
|
||||
<table><tr><td>O</td><td>若机器人出现零部件老化或损坏的情况,请勿启动机器人并及时联系售后。</td></tr></table>
|
||||
|
||||
# 2.2 充电
|
||||
|
||||
山猫M20Pro使用双电池供电,支持机器人在开机的情况下,更换其中任意一块电池。用户可以将电池插入智能充电管家,进行充电工作。
|
||||
|
||||
1. 首先使机器人处于正常趴下状态,再将前 / 后腿适当抬起以确保前 / 后侧电池能够顺利取出,握住电池的提带,施加适当的力向外拉,电池卡扣会受力解锁,随后可将电池从电池槽中取出。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
• 建议在 $0 ^ { \circ } C \sim 4 0 ^ { \circ } C$ 的环境中充电。
|
||||
• 充电过程中请时刻留意智能充电管家的状态,谨防意外发生,充电完成后及时断开充电电源。
|
||||
|
||||
2. 将电池插入智能充电管家的充电座中(电池开关侧朝内),再将智能充电管家接入市电$( 1 0 0 \mathsf { V } \small { \sim } 2 4 0 \mathsf { V } )$ ),随后开启智能充电管家开关进行充电。
|
||||
|
||||
3.充电时,电池上的指示灯均呈流水灯状态,亮灯数量对应已充电量。
|
||||
|
||||
4. 充电完成后,电池上的四颗 LED 指示灯熄灭。
|
||||
|
||||

|
||||
|
||||
# 2.3 启动
|
||||
|
||||
# 2.3.1 准备
|
||||
|
||||
1. 首先将机器人从运输箱中搬出并放置在平稳地面上,具体操作请参考 2.1.2“搬运提示”。
|
||||
|
||||
2. 安装前侧电池时,需将前腿适当抬起以确保电池能够顺利插入。插入电池时,需将电池正向放入电池槽(指示灯和电池开关在提带的上方),一只手向外拉电池提带,使电池卡扣收入电池,以方便插入;另一只手将电池底部略微抬起,用适当的力将电池推入电池槽。随后松开电池提带,当电池提带左右两侧的卡扣均卡入电池槽时,表示电池成功安装。后侧电池的安装方法同理。
|
||||
|
||||
3. 按要求摆放好山猫 M20 Pro:大腿紧贴小腿,小腿和足部轮胎自然触地(如下图所示)。
|
||||
|
||||

|
||||
|
||||
# 2.3.2 开机
|
||||
|
||||
开机前需确认硬急停开关是否被触发,若硬急停开关被按下,需先对机器人进行故障排查。在确保机器可正常运行的情况下,按照按钮上的箭头指示方向旋转开关以释放硬急停。在硬急停开关处于未按下状态的情况下,短按电池开关再长按至电池 LED 灯流水亮起并保持常亮,表示电池处于供电状态,电池指示灯显示当前电量。按下机身右侧面的电源开关,机器人开机,此时电源开关常亮绿光。
|
||||
|
||||
# 2.3.3 连接
|
||||
|
||||
长按电源键对手柄进行开机操作(使用前请确保手柄电量充足),出厂时机器人与手柄已进行一对一配对绑定,打开“云深处科技”APP后,手柄将自动连接与其绑定的机器人。
|
||||
|
||||

|
||||
|
||||
# 2.4 控制界面
|
||||
|
||||
打开APP后,会自动进入控制界面,如下图所示:
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>按钮功能定义</td></tr><tr><td rowspan=1 colspan=1>①手柄电源键</td><td rowspan=1 colspan=1>长按使手柄开机/关机,短按唤醒/熄屏</td></tr><tr><td rowspan=1 colspan=1>②软急停</td><td rowspan=1 colspan=1>触发软急停,强制机器人趴下,一般在机器人失控或紧急情况时使用</td></tr><tr><td rowspan=1 colspan=1>③使用模式</td><td rowspan=1 colspan=1>点击可切换使用模式:常规模式/导航模式/辅助模式</td></tr><tr><td rowspan=1 colspan=1>④设置</td><td rowspan=1 colspan=1>设置:关于设备/传感器管理/状态监控/辅助功能</td></tr><tr><td rowspan=1 colspan=1>⑤异常列表</td><td rowspan=1 colspan=1>仅在有异常信息时显示,点击可查看并处理异常内容</td></tr><tr><td rowspan=1 colspan=1>⑥移动数据</td><td rowspan=1 colspan=1>仅在手柄有移动网络连接时显示,显示移动数据信号强度信息</td></tr><tr><td rowspan=1 colspan=1>⑦机器人信号</td><td rowspan=1 colspan=1>仅在手柄与机器人连接时显示,显示连接信号强度与延迟信息</td></tr><tr><td rowspan=1 colspan=1>③WiFi连接</td><td rowspan=1 colspan=1>仅在手柄连接上机器人的WiFi时显示,显示WiFi信号强度与延迟信息</td></tr><tr><td rowspan=1 colspan=1>⑨急停</td><td rowspan=1 colspan=1>即软急停</td></tr><tr><td rowspan=1 colspan=1>展开/收起</td><td rowspan=1 colspan=1>点击可展开/收起机器人仪表盘</td></tr><tr><td rowspan=1 colspan=1>①照明灯指示灯</td><td rowspan=1 colspan=1>指示灯亮起/熄灭表示照明灯开启/关闭,仪表盘展开时,左/右侧指示灯亮起则表示前/后侧照明灯开启</td></tr><tr><td rowspan=1 colspan=1>②剩余里程</td><td rowspan=1 colspan=1>预计剩余的运动续航里程</td></tr><tr><td rowspan=1 colspan=1>③图传指示灯</td><td rowspan=1 colspan=1>指示灯亮起/熄灭表示图传开启/关闭,仪表盘展开时,左/右侧指示灯亮起则表示前/后侧图传开启</td></tr></table>
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>按钮功能定义</td></tr><tr><td rowspan=1 colspan=1>④电池信息</td><td rowspan=1 colspan=1>左右侧数据分别表示前/后侧电池剩余电量百分比</td></tr><tr><td rowspan=1 colspan=1>⑤趴下/站立</td><td rowspan=1 colspan=1>使机器人站立或趴下,并高亮显示当前状态</td></tr><tr><td rowspan=1 colspan=1>左摇杆</td><td rowspan=1 colspan=1>控制机器人平移方向</td></tr><tr><td rowspan=1 colspan=1>①右摇杆</td><td rowspan=1 colspan=1>左右控制机器人旋转</td></tr><tr><td rowspan=1 colspan=1>③速度调节</td><td rowspan=1 colspan=1>点击左侧的齿轮可开启速度调节,滑动右侧的滑块可调节最大速度值</td></tr><tr><td rowspan=1 colspan=1>③运动步态</td><td rowspan=1 colspan=1>点击步态名称以切换相应的步态,蓝色填充显示的步态为当前步态</td></tr><tr><td rowspan=1 colspan=1>②照明灯</td><td rowspan=1 colspan=1>点击控制照明灯开关:照明关闭/前照明灯/后照明灯/前后照明灯</td></tr><tr><td rowspan=1 colspan=1>②广角相机</td><td rowspan=1 colspan=1>点击切换广角相机视频流数量:单广角/双广角</td></tr><tr><td rowspan=1 colspan=1>②切换相机</td><td rowspan=1 colspan=1>点击切换主界面显示的广角相机视频流:前视角/后视角</td></tr><tr><td rowspan=1 colspan=1>③录屏</td><td rowspan=1 colspan=1>点击可开始/结束录制当前视角的视频</td></tr><tr><td rowspan=1 colspan=1>②拍照</td><td rowspan=1 colspan=1>点击可保存视频流画面</td></tr><tr><td rowspan=1 colspan=1>充电</td><td rowspan=1 colspan=1>点击后,机器人自动切换为导航模式,执行自主充电任务</td></tr><tr><td rowspan=1 colspan=1>②运动正方向指示</td><td rowspan=1 colspan=1>蓝色高亮显示的箭头方向为向前运动的方向,即运动正方向</td></tr><tr><td rowspan=1 colspan=1>②换向</td><td rowspan=1 colspan=1>点击后,机器人会切换向前运动的方向,向前运动的方向请参照26</td></tr><tr><td rowspan=1 colspan=1>28第二相机视角</td><td rowspan=1 colspan=1>启用双广角相机图传时显示,点击②/使该视角与主界面视角互换</td></tr></table>
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>按钮功能定义</td></tr><tr><td rowspan=1 colspan=1>②关节温度</td><td rowspan=1 colspan=1>点击后开关会蓝色高亮显示,并显示关节温度面板</td></tr><tr><td rowspan=1 colspan=1>30关节参数面板</td><td rowspan=1 colspan=1>显示关节的运动参数信息,点击上侧的腿部名称可切换查看相应的腿部关节参数,选中的腿部的名称由蓝色填充显示</td></tr><tr><td rowspan=1 colspan=1>③关节温度面板</td><td rowspan=1 colspan=1>显示关节的温度信息,点击电机/驱动器可切换查看相应的关节模块温度,电机/驱动器的过温/过热分别用橙色/红色显示</td></tr><tr><td rowspan=1 colspan=1>32关节参数</td><td rowspan=1 colspan=1>点击后开关会蓝色高亮显示,并显示关节运动参数面板</td></tr><tr><td rowspan=1 colspan=1>3 L1</td><td rowspan=1 colspan=1>使机器人站立或趴下,同按钮⑤</td></tr><tr><td rowspan=1 colspan=1>L2</td><td rowspan=1 colspan=1>切换主界面相机视角,同按钮22</td></tr><tr><td rowspan=1 colspan=1>③R1</td><td rowspan=1 colspan=1>自定义按键,可在"设置-辅助功能-快捷键自定义"中设置按键功能</td></tr><tr><td rowspan=1 colspan=1>3R2</td><td rowspan=1 colspan=1>自定义按键,可在"设置-辅助功能-快捷键自定义"中设置按键功能</td></tr></table>
|
||||
|
||||
# 2.5 设置界面
|
||||
|
||||
打开APP后,在控制界面点击按钮【 $\textcircled{4}$ 设置】,将会进入设置界面,如下图所示:
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=2>按钮功能定义</td></tr><tr><td rowspan=1 colspan=1>①关于设备</td><td rowspan=1 colspan=1>可查看:序列号/设备型号/系统版本/APP版本,进行OTA升级</td></tr><tr><td rowspan=1 colspan=1>②传感器管理</td><td rowspan=1 colspan=1>可开启或关闭:双激光雷达/双目相机</td></tr><tr><td rowspan=1 colspan=1>③状态监控</td><td rowspan=1 colspan=1>可查看整机运动状态/整机姿态/CPU状态以及电池状态</td></tr><tr><td rowspan=1 colspan=1>④辅助功能</td><td rowspan=1 colspan=1>可切设置机器人WiFi信息、设置物理按键功能,切换APP语言</td></tr></table>
|
||||
|
||||
# 2.6 应急操作
|
||||
|
||||
# 2.6.1 软急停
|
||||
|
||||
如果机器人使用过程中出现腿乱摆、剧烈晃动等非正常现象时,请启动手柄端软急停功能,使机器人趴下(点击控制手柄上的按钮 $\textcircled{2}$ 或 $\textcircled{12}$ )。待排查问题后,再次按下起立键即可正常操作机器人。
|
||||
|
||||
# 2.6.2 硬急停
|
||||
|
||||
硬急停,即硬件紧急宕机,按下机器人背部后侧硬急停开以触发硬急停,可避免机器人出现失控的情况。待排查完问题后,若需要解除硬急停,请按照按钮上的箭头方向旋转硬急停按钮以解除硬急停,若硬急停开关弹起,则表示硬急停已解除。硬急停解除后 , 机器人会自动重启,手柄会短暂断连,待机器人重启成功后,手柄会自动与机器人连接,连接完成后机器人可继续使用。
|
||||
|
||||
<table><tr><td>,</td><td>一旦触发硬急停,机器人就会立刻失去所有动能,直接从站立状态中下坠, 存在损伤地面、机器人损坏等风险,正常运动过程中严禁使用!</td></tr></table>
|
||||
|
||||
# 2.6.3 过热保护
|
||||
|
||||
系统自带温度感知。点击屏幕上的按钮 $\textcircled{2 9}$ 可查看关节模组的温度信息,橙色数字表示关节部件温度较高,红色数字表示关节部件过热。一旦机器人高强度运行导致电机、驱动器过热,将自动进入过热保护状态,机器人将自动停止运动,原地趴下。等待机器人降温,退出过热保护状态后,可继续操作机器人。
|
||||
|
||||
# 2.6.4 低电量保护
|
||||
|
||||
当两块电池的电量均低于 $2 0 \%$ 时,机器人会进入低电量预警状态,此时应立即充电或更换电池;当两块电池的电量均低于 $5 \%$ 时,机器人会触发低电量保护,它将自动趴下,不再响应手柄的遥控指令,请更换电池后再继续使用。
|
||||
|
||||
# 2.6.5 其他情形
|
||||
|
||||
·如果遇到摔倒后机器人关节仍在摆动的紧急情况,请等到机器人各关节完全停止运动后30秒,再按下身体尾部硬急停按钮。
|
||||
• 如遇起火,请勿用水灭火,并就近使用以下任意一种类型的灭火器:泡沫、干粉或二氧化碳。如果软急停功能失效或者机器人出现冒烟、进水等意外情况,请第一时间按下硬急停按钮并切断机器人电源,待确认安全后再进行问题的排查,同时将情况反馈至我司,我司将协助进行问题排查和机器人维修、更换。使用过程中请务必注意安全!
|
||||
• 如果机器人摔倒,请勿在硬急停状态未触发时,拖拽、推拉或翻转机器人。
|
||||
|
||||
# 2.7 关机
|
||||
|
||||
<table><tr><td>0</td><td>执行以下操作前,务必保证机器人处于趴下状态。</td></tr></table>
|
||||
|
||||
按下电源开关,电源开关按钮弹起,同时电源灯光与背部前后侧的灯带熄灭,机器人关机。短按电池开关键再长按至LED灯闪烁一次,随后电池指示灯灯光熄灭,表示电池停止供电,关机完成。
|
||||
|
||||
# 2.8 负载加装
|
||||
|
||||
山猫 M20 Pro 支持负载加装和二次开发,用户可根据机器人背部孔位(单位为毫米)自行加装设备。
|
||||
|
||||
负载的加装范围限制为导轨所在范围,高度限制为30cm,即可用的加装范围为$2 7 0 \mathrm { m m } \times 1 6 5 \mathrm { m m } \times 3 0 0 \mathrm { m m }$ ,建议加装负载重量不要超过 $1 5 \mathsf { k g } _ { \circ }$
|
||||
|
||||
详细的加装与二次开发内容请参考开发手册。
|
||||
|
||||

|
||||
|
||||
<table><tr><td rowspan=1 colspan=1>接口</td><td rowspan=1 colspan=1>④网口3+24V</td></tr><tr><td rowspan=1 colspan=1>① 72V</td><td rowspan=1 colspan=1>⑤网口4+24V</td></tr><tr><td rowspan=1 colspan=1>②网口1</td><td rowspan=1 colspan=1>⑥网口5+24V</td></tr><tr><td rowspan=1 colspan=1>③网口2+24V</td><td rowspan=1 colspan=1>⑦ USB (Type-C)</td></tr></table>
|
||||
|
||||

|
||||
|
||||
当在机器人背部加装设备时,机器人的运动性能可能会受影响,请务必在加装前向售后人员咨询相关事宜。
|
||||
使用外接供电口对负载供电时,应注意,供电口可对外提供的总功率不超过 $3 6 0 W$ ,24V电源供电总功率不超过 $2 5 0 W _ { c }$ 。
|
||||
• 未使用的航插接口请用航插口盖盖好。
|
||||
|
||||
# 3 注意事项
|
||||
|
||||
# 3.1 使用环境注意事项
|
||||
|
||||
<table><tr><td>!</td><td>·请勿在高压电线、高压输电站、移动电话基站和电视广播信号塔等电 磁干扰严重的环境下操控机器人,如果需要在强电磁干扰环境中操控 机器人,请先咨询售后。 请勿在WiFi信号干扰严重的环境下操控机器人,务必关闭部分或全部 其他无线设备WiFi信号源,然后再使用遥控手柄操控机器人。 请勿在有雾、下雪、雷电、沙暴、暴风、龙卷风等恶劣天气运行机器人。 请保持在视线范围内控制机器人,使机器人时刻与人群、水面、明火 等保持至少2米以上的安全距离。 请勿在冰面、玻璃、瓷砖等光滑地面使用机器人,如需通过光滑地面,</td></tr><tr><td>0</td><td>本产品仅在同时安装两块电池时的防护等级为IP66。此时机器人可完全 防止灰尘进入,并且能防止强烈喷水侵入。但不支持浸水,因浸水使液体 渗入而导致损坏不在保修范围内。如遇水喷溅,请在使用后擦净设备表面 的水渍保存。如跌落水中或被液体浸没,请立即停止使用,并擦干表面水 渍,联系售后获取支持。</td></tr></table>
|
||||
|
||||
# 3.2 电池注意事项
|
||||
|
||||
<table><tr><td></td><td>·使用电池前,请仔细阅读电池注意事项和电池表面标识。 电池必须使用官方提供的专用充电器(如智能充电管家)进行充电。 禁止机械撞击、碾压、或抛掷电池。请勿在电池或充电器上放置重物。 切勿在潮湿环境中使用电池,切勿将电池浸入水中或弄湿。 电池内部接触到水后可能会发生分解反应,从而引发电池自燃,甚至 引发爆炸。因此严禁使电池接触任何液体,切勿将电池浸入水中或弄湿, 切勿在雨中或潮湿环境中使用电池。如电池意外坠入水中,请立即将 电池置于安全的开阔区域并远离电池直至电池完全晾干。晾干的电池 不得再次使用。 禁止擅自拆卸电池。一旦拆卸,不予保修。因拆卸电池引发的电池事故, 云深处科技一律不予负责。 如长期不使用时,请将电池从机器人中取出单独存放,并充电至 60%,以免电池过放造成电芯损坏,无法恢复使用。</td></tr></table>
|
||||
|
||||

|
||||
|
||||
• 切勿将电池正负极短路。
|
||||
• 长按电池开关键 20 秒至电池指示灯的灯光闪烁 5 次,可使电池进入仓运模式。
|
||||
• 仓运模式下电池可以长期保存,不响应开关的点击操作且电池指示灯常灭。将仓运模式下的电池充电后可退出仓运模式。电池放置超过3个月时,应重新充电至 $6 0 \%$ 以保持电池活性。在使用或储存期间,如发现电池有出现高温发热、漏液、散发异味、变形及其它异常现象时,请立即停止使用并远离电池,并联系售后做进一步处理。
|
||||
• 详细的注意事项请参考电池说明书。
|
||||
|
||||
# 3.3 其他注意事项
|
||||
|
||||

|
||||
|
||||
• 搬运机器人时,注意机器人上面的防夹手标签,严禁将手伸入贴有防夹手标签的位置!
|
||||
|
||||
• 机器人在运动时切勿将其提起,以免机器人进行不可预期动作,造成机器人或人员损伤!
|
||||
|
||||
<table><tr><td></td><td>·请勿在遥控手柄中下载其他无关APP! 严禁私自拆卸机器人,一经拆卸,保修无效!</td></tr></table>
|
||||
|
||||
# 4 常见问题与解决
|
||||
|
||||
# Q1:机器人自己停止运动是正常现象吗?
|
||||
|
||||
A:可能是某种异常导致机器人触发了自身的保护机制,可通过手柄 APP 查看具体的操作提示。若持续无法恢复正常或未显示异常处理提示,请联系售后。
|
||||
|
||||
# Q2:连接机器人后手柄操作没有反应怎么办?
|
||||
|
||||
A:先确认急停状态是否已关闭,若未关闭,请先排查故障,排查完成后再关闭急停状态;
|
||||
若急停状态已关闭且依然无法操控机器人,请重启机器人。若仍有异常,请联系售后。
|
||||
|
||||
Q3:机器人起立后身体或腿部歪斜,是否还能继续使用?
|
||||
|
||||
A:此时不要控制机器人移动,请立刻按下软急停使机器人趴下,关机重启,再次尝试操控机器人站立。若仍有异常,请联系售后。
|
||||
|
||||
Q4:机器人由于某条腿失控摔倒该如何处理?
|
||||
|
||||
A:先点击手柄上的 $[ \textcircled{2} / \textcircled { 1 0 }$ 急停】键使其趴下,然后重启机器人。若重启后仍未恢复正常,请联系售后。
|
||||
|
||||
# Q5:机器人摔倒后视频流卡住该如何处理?
|
||||
|
||||
A:重启机器人。若重启后视频流依然没有恢复正常,请联系售后。
|
||||
|
||||
Q6:收到货后,短按电池开关电池不显示电量是正常现象吗?
|
||||
|
||||
A:可能是电池进入了仓运模式,请参考“2.2充电”为电池充电后再尝试电池是否能正常使用,若仍然无法使用,请联系售后。
|
||||
|
||||
Q7:机器人可以适应哪些地形?
|
||||
|
||||
A:机器人可适用于平地、草地等起伏较为平缓的地面,也可攀爬楼梯、斜坡等复杂地形,但请避免在极端地形(如冰面等过于光滑的表面、尖锐或起伏较大的地形、高处边缘等)中操作和使用机器人。
|
||||
|
||||
Q8:遇到查阅本手册后依然无法解决的问题时该如何处理?
|
||||
|
||||
A:保存数据并及时联系售后。
|
||||
|
||||
# 5 运输储存
|
||||
|
||||
# 5.1 运输
|
||||
|
||||
运输山猫 ${ \mathsf { M } } 2 0 { \mathsf { P r o } }$ 时,请使用专门为山猫 ${ \mathsf { M } } 2 0 { \mathsf { P r o } }$ 机器人设计的运输箱。
|
||||
|
||||
<table><tr><td></td><td>带箱运输前请将电池从机器人中取出,运输过程中务必保证运输箱正面朝 上。</td></tr></table>
|
||||
|
||||
# 5.2 储存
|
||||
|
||||
• 山猫M20Pro机器人要求洁净、干燥的储存环境,存储温度为 $0 ^ { \circ } C \sim 4 0 ^ { \circ } C _ { \circ }$
|
||||
• 必须关闭主电源,若长期不使机器人,请将电池从机器人中取出。严禁水或其他液体淋到机器人上。严禁将其他各类物品放置在关节旋转范围内。建议使用专门为山猫M20Pro机器人设计的运输箱来储存,防止冲击和振动。
|
||||
山猫M20Pro机器人在箱中必须背部朝上放置。
|
||||
• 电池存储的请参考“3.2 电池注意事项”。
|
||||
|
||||
# 5.3 搬运
|
||||
|
||||
请参考2.1.2“搬运提示”进行机器人的搬运。
|
||||
|
||||
# 6 保修条款
|
||||
|
||||
# 6.1 保修政策
|
||||
|
||||
您购买的山猫 ${ \mathsf { M } } 2 0 { \mathsf { P r o } }$ 主要部件保修时间如下表所示:
|
||||
|
||||
<table><tr><td rowspan=1 colspan=1>部件名称</td><td rowspan=1 colspan=1>保修期</td></tr><tr><td rowspan=1 colspan=1>关节模组</td><td rowspan=1 colspan=1>6个月</td></tr><tr><td rowspan=1 colspan=1>电池、广角相机、激光雷达、控制系统、其他电子元器件</td><td rowspan=1 colspan=1>12个月</td></tr></table>
|
||||
|
||||
注:外壳、足部轮胎等易损件及运输箱等配件不在保修范围,如有需要,请咨询售后支持。
|
||||
|
||||
您购买本产品后,保修期自您签收货物当日算起,请您务必保管好保修凭证。满足保修期和保修内容的产品或部件将获得免费售后服务。如果您购买的产品已经超过保修期,您也可以通过另外购买服务的方式来获得我们的帮助。
|
||||
|
||||
# 6.2 保修范围
|
||||
|
||||
根据具体的情况,我们会为您购买的产品进行相应的维修或者部件的更换。但以下情况将不属于免费保修范围,但您仍可选择有偿售后服务,具体事宜请咨询售后支持:
|
||||
|
||||
• 发生人为的非产品本身质量问题导致的损坏。发生私自改造、拆装、开壳等行为。超出保修期限。因未按说明书要求正确安装、使用及操作(如跌落、挤压、浸液、暴力使用等)而造成的由于二次开发导致的损坏。在超过安全承重情况下使用导致的损坏。无官方说明指导情况下自行维修或更换部件导致的损坏。自行加装第三方产品导致的损坏。使用非原装配套电池包供电导致的损坏。由于不可抗力的因素如台风、地震、火灾、雷击、异常电压导致的故障或损坏。在信号干扰严重的环境下操控机器人导致的损坏。在玻璃、冰面等光滑地面不当操控机器人导致的损坏。在高处边缘使用机器人导致的坠落损坏。在较平整地形操作机器人摔倒以外的跌落损坏。在地形尖锐、起伏较大等环境操作导致的损坏。在零部件发生老化或损坏情况下强行运行导致的损坏。在复杂环境场合中使用未保持安全距离导致的损坏。机器人内部有明显粉尘、砂砾、水、金属粉末侵入痕迹。搬运操作不当造成的损坏。
|
||||
在超过机器人抗干扰极限情况下对其施加的暴力或非暴力手段导致的损坏。
|
||||
• 由于操作失误导致机器人表面有明显摔打碰撞痕迹、明显划伤痕迹等情况。
|
||||
|
||||
# 6.3 送修说明
|
||||
|
||||
• 在获取售后服务之前,请务必备份所有数据,并删除重要数据以防数据丢失或泄露,云深处科技不对产品中包含的任何数据的丢失或泄露负责。
|
||||
向云深处科技获取售后服务时默认您授权云深处科技为售后之目的而进行任何修改、删除数据或恢复出厂设置等操作。
|
||||
• 在送修之前,请先联系云深处科技售后支持,云深处科技将尝试远程诊断和解决您的问题。
|
||||
如上述方式无法解决您的问题,与售后支持核实后可寄回四轮足机器人进行维修。您在将产品寄往云深处科技时需先行承担邮寄费用,云深处科技收到您需要保修的问题产品后,将对产品进行检测以确定问题和责任。
|
||||
若属于产品本身质量缺陷,云深处科技将负责承担检测费、材料费、人工费及寄回的快递费。
|
||||
如果检测认定产品不符合免费维修条件,您可选择付费维修,相应的检测费、材料费、人工费及寄回的快递费将由您承担,若不维修可选择原机寄回,相应的快递费及保险费用将由您承担。基于环保与安全的考虑,请勿寄回严重损坏的电池,如已寄回,云深处科技将对此类电池进行报废处理,不作退还。
|
||||
如您提供的收件地址错误导致无法投递或收件人拒绝接受,因此产生的不利后果与损失由您承担。
|
||||
• 为确保您的权益,在签收云深处科技寄送的售后产品时,请您仔细检查产品是否完好,如存在异常,请立即当场拍摄视频或照片并联系云深处科技获取解决方案,如存在未解决的售后问题,也请立即联系云深处科技,否则视为本次售后服务无争议结束。
|
||||
|
||||
# 7 废弃处理
|
||||
|
||||
• 对于废弃的机器人整机及零部件的处理,需根据国家关于废弃电器电子产品回收的相应法律法规进行。
|
||||
|
||||
• 特别的,机器人内含锂电池的使用或丢弃,需要按照国家有关电池类物品的处理法律法规进行。
|
||||
|
||||
# 云深处科技
|
||||
|
||||
杭州云深处科技股份有限公司
|
||||
|
||||
地址:浙江省杭州市西湖区三墩镇欣然街36号紫金梦想广场3 幢
|
||||
电话:400-0559-095
|
||||
网址:www.deeprobotics.cn
|
||||
@@ -0,0 +1,18 @@
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
import socketserver
|
||||
|
||||
class CORSHandler(SimpleHTTPRequestHandler):
|
||||
def end_headers(self):
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET,OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', '*')
|
||||
super().end_headers()
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
if __name__ == '__main__':
|
||||
PORT = 9999
|
||||
with socketserver.TCPServer(("", PORT), CORSHandler) as httpd:
|
||||
print(f"Serving at http://127.0.0.1:{PORT}")
|
||||
httpd.serve_forever()
|
||||