【系统集成 16-20】第 17/25 课

🤖 第17课:云端服务对接

📌 云端服务对接概述

服务机器人不是孤岛——云端服务提供地图更新、人脸库同步、NLU增强、远程管理能力:

☁️ 云端服务架构

┌────────────┐     ┌──────────────────────┐
│  机器人端   │ ←→ │     云端平台         │
│  本地推理   │     │  地图服务 │ 人脸库   │
│  离线兜底   │     │  NLU增强 │ 任务调度 │
│  数据采集   │     │  OTA更新 │ 远程监控 │
└────────────┘     └──────────────────────┘
        ↕                    ↕
   边缘网关 ←→ 消息队列 ←→ 数据库

📌 云端API调用

import json, hashlib, time

class CloudServiceManager:
    """云端服务管理器"""
    def __init__(self):
        self.apis = {
            "map_service": {"url": "https://api.cloud.com/maps", "timeout": 5000, "auth": "oauth2"},
            "face_service": {"url": "https://api.cloud.com/face", "timeout": 3000, "auth": "apikey"},
            "nlu_service": {"url": "https://api.cloud.com/nlu", "timeout": 2000, "auth": "token"},
            "schedule_service": {"url": "https://api.cloud.com/schedule", "timeout": 3000, "auth": "oauth2"},
            "config_service": {"url": "https://api.cloud.com/config", "timeout": 1000, "auth": "apikey"},
        }
        self.cache = {}
        self.cache_ttl = 300  # 5分钟缓存
        self.request_log = []

    def call(self, service_name, method="GET", data=None, use_cache=True):
        """调用云端服务(模拟)"""
        api = self.apis.get(service_name)
        if not api:
            return {"success": False, "error": f"未知服务: {service_name}"}

        # 缓存检查
        cache_key = hashlib.md5(f"{service_name}:{method}:{data}".encode()).hexdigest()
        if use_cache and method == "GET" and cache_key in self.cache:
            entry = self.cache[cache_key]
            if time.time() - entry["time"] < self.cache_ttl:
                self.request_log.append(f"[缓存命中] {service_name}")
                return entry["data"]

        # 模拟API调用
        latency = api["timeout"] * 0.3  # 模拟30%超时时间的延迟
        result = self._simulate_response(service_name, method, data)

        # 缓存写入
        if use_cache and method == "GET":
            self.cache[cache_key] = {"data": result, "time": time.time()}

        self.request_log.append(f"[调用] {service_name} {method} ({latency:.0f}ms)")
        return result

    def _simulate_response(self, service, method, data):
        responses = {
            "map_service": {"floors": [1,3,5], "version": "v2.3", "updated": "2024-01-15"},
            "face_service": {"recognized": True, "name": "张总", "confidence": 0.92},
            "nlu_service": {"intent": "navigate", "slots": {"location": "会议室A"}, "confidence": 0.88},
            "schedule_service": {"tasks": [{"id":"T1","type":"deliver","priority":3}]},
            "config_service": {"nav_speed": 0.8, "voice_volume": 0.7, "avoid_distance": 0.5},
        }
        return {"success": True, "data": responses.get(service, {}), "status": 200}

    def get_stats(self):
        total = len(self.request_log)
        cached = sum(1 for l in self.request_log if "缓存" in l)
        return {"total_requests": total, "cache_hits": cached, "hit_rate": cached/total if total else 0}

cloud = CloudServiceManager()
print("云端服务对接模拟")
print("=" * 55)

# 调用各种服务
services = ["map_service", "face_service", "nlu_service", "schedule_service", "config_service"]
for svc in services:
    result = cloud.call(svc)
    print(f"\n📋 {svc}:")
    print(f"  结果: {json.dumps(result, ensure_ascii=False, indent=2)[:200]}")

# 缓存测试
print("\n📋 缓存测试:")
cloud.call("map_service")  # 第二次调用应命中缓存
stats = cloud.get_stats()
print(f"  总请求: {stats['total_requests']}, 缓存命中: {stats['cache_hits']}, 命中率: {stats['hit_rate']:.0%}")
print("\n✅ 云端服务验证通过")
✅ 验证通过 云端服务对接模拟 ======================================================= 📋 map_service: 结果: { "success": true, "data": { "floors": [ 1, 3, 5 ], "version": "v2.3", "updated": "2024-01-15" }, "status": 200 } 📋 face_service: 结果: { "success": true, "data": { "recognized": true, "name": "张总", "confidence": 0.92 }, "status": 200 } 📋 nlu_service: 结果: { "success": true, "data": { "intent": "navigate", "slots": { "location": "会议室A" }, "confidence": 0.88 }, "status": 200 } 📋 schedule_service: 结果: { "success": true, "data": { "tasks": [ { "id": "T1", "type": "deliver", "priority": 3 } ] }, "status": 200 } 📋 config_service: 结果: { "success": true, "data": { "nav_speed": 0.8, "voice_volume": 0.7, "avoid_distance": 0.5 }, "status": 200 } 📋 缓存测试: 总请求: 6, 缓存命中: 1, 命中率: 17% ✅ 云端服务验证通过

📌 OTA远程更新

import json

class OTAUpdateManager:
    """OTA远程更新管理"""
    def __init__(self):
        self.current_version = "2.1.0"
        self.components = {
            "navigation": {"version": "2.1.0", "size": "45MB"},
            "voice": {"version": "1.5.0", "size": "120MB"},
            "vision": {"version": "3.0.0", "size": "200MB"},
            "system": {"version": "1.0.0", "size": "50MB"},
        }
        self.update_state = "idle"  # idle/checking/downloading/installing/rollback

    def check_update(self):
        """检查更新"""
        self.update_state = "checking"
        available = []
        updates = {
            "navigation": "2.2.0",
            "voice": "1.6.0",
            "vision": "3.1.0",
        }
        for comp, new_ver in updates.items():
            cur_ver = self.components[comp]["version"]
            if new_ver > cur_ver:
                available.append({
                    "component": comp,
                    "current": cur_ver,
                    "new": new_ver,
                    "size": self.components[comp]["size"],
                    "changelog": f"{comp} 性能优化+bug修复",
                    "critical": comp == "system"
                })
        self.update_state = "idle"
        return available

    def install_update(self, component, new_version):
        """安装更新"""
        self.update_state = "downloading"
        log = [f"[下载] {component} {new_version}"]
        
        self.update_state = "installing"
        old_version = self.components[component]["version"]
        log.append(f"[安装] 备份旧版本 {old_version}")
        log.append(f"[安装] 安装新版本 {new_version}")
        
        # 模拟安装成功
        self.components[component]["version"] = new_version
        log.append(f"[验证] 版本检查通过: {new_version}")
        
        self.update_state = "idle"
        return {"success": True, "log": log}

    def rollback(self, component):
        """回滚更新"""
        log = [f"[回滚] {component} 恢复到上一版本"]
        return {"success": True, "log": log}

ota = OTAUpdateManager()
print("OTA远程更新管理")
print("=" * 55)

updates = ota.check_update()
print(f"\n可用更新: {len(updates)}个")
for u in updates:
    print(f"  📦 {u['component']}: {u['current']} → {u['new']} ({u['size']}) - {u['changelog']}")

# 安装导航更新
result = ota.install_update("navigation", "2.2.0")
for log in result["log"]:
    print(f"  {log}")

print(f"\n当前版本:")
for comp, info in ota.components.items():
    print(f"  {comp}: {info['version']}")
print("\n✅ OTA更新验证通过")
✅ 验证通过 OTA远程更新管理 ======================================================= 可用更新: 3个 📦 navigation: 2.1.0 → 2.2.0 (45MB) - navigation 性能优化+bug修复 📦 voice: 1.5.0 → 1.6.0 (120MB) - voice 性能优化+bug修复 📦 vision: 3.0.0 → 3.1.0 (200MB) - vision 性能优化+bug修复 [下载] navigation 2.2.0 [安装] 备份旧版本 2.1.0 [安装] 安装新版本 2.2.0 [验证] 版本检查通过: 2.2.0 当前版本: navigation: 2.2.0 voice: 1.5.0 vision: 3.0.0 system: 1.0.0 ✅ OTA更新验证通过

📌 数据同步管理

class DataSyncManager:
    """数据同步管理"""
    def __init__(self):
        self.local_data = {
            "maps": {"version": 3, "hash": "abc123", "size": 5000000},
            "face_db": {"version": 15, "hash": "def456", "size": 2000000},
            "config": {"version": 8, "hash": "ghi789", "size": 50000},
            "logs": {"version": 100, "hash": "jkl012", "size": 10000000},
        }
        self.cloud_data = {
            "maps": {"version": 5, "hash": "xyz789"},
            "face_db": {"version": 15, "hash": "def456"},
            "config": {"version": 10, "hash": "mno345"},
            "logs": {"version": 100, "hash": "jkl012"},
        }
        self.sync_rules = {
            "maps": {"direction": "cloud_to_local", "conflict": "cloud_wins"},
            "face_db": {"direction": "bidirectional", "conflict": "newer_wins"},
            "config": {"direction": "cloud_to_local", "conflict": "cloud_wins"},
            "logs": {"direction": "local_to_cloud", "conflict": "merge"},
        }

    def sync(self):
        """执行同步"""
        results = []
        for data_type, rule in self.sync_rules.items():
            local = self.local_data.get(data_type, {})
            cloud = self.cloud_data.get(data_type, {})
            
            needs_sync = local.get("version", 0) != cloud.get("version", 0)
            if not needs_sync:
                results.append({"type": data_type, "action": "skip", "reason": "已同步"})
                continue
            
            direction = rule["direction"]
            if direction == "cloud_to_local":
                self.local_data[data_type]["version"] = cloud["version"]
                self.local_data[data_type]["hash"] = cloud["hash"]
                results.append({"type": data_type, "action": "download", "version": cloud["version"]})
            elif direction == "local_to_cloud":
                self.cloud_data[data_type]["version"] = local["version"]
                results.append({"type": data_type, "action": "upload", "version": local["version"]})
            elif direction == "bidirectional":
                if cloud.get("version", 0) > local.get("version", 0):
                    self.local_data[data_type]["version"] = cloud["version"]
                    results.append({"type": data_type, "action": "download", "version": cloud["version"]})
                else:
                    self.cloud_data[data_type]["version"] = local["version"]
                    results.append({"type": data_type, "action": "upload", "version": local["version"]})
        
        return results

sync = DataSyncManager()
print("数据同步管理")
print("=" * 55)

results = sync.sync()
for r in results:
    print(f"  📋 {r['type']}: {r['action']} {r.get('version','')} ({r.get('reason','')})")

print(f"\n同步后本地版本:")
for dt, info in sync.local_data.items():
    print(f"  {dt}: v{info['version']}")
print("\n✅ 数据同步验证通过")
✅ 验证通过 数据同步管理 ======================================================= 📋 maps: download 5 () 📋 face_db: skip (已同步) 📋 config: download 10 () 📋 logs: skip (已同步) 同步后本地版本: maps: v5 face_db: v15 config: v10 logs: v100 ✅ 数据同步验证通过

📌 离线与在线策略

🔄 离线/在线切换

功能在线模式离线兜底
语音识别云ASR(高精度)Whisper本地
自然语言理解LLM(强泛化)规则引擎
人脸识别云比对(大库)本地特征库
地图云端最新本地缓存
任务调度云端优化本地队列

📌 练习

📝 练习 1

实现断网检测与优雅降级:检测网络断开后自动切换到离线模式,恢复后增量同步。

📝 练习 2

实现灰度发布:OTA更新时先对10%的机器人发布,观察1小时无问题后全量发布。

📝 练习 3

设计数据安全方案:端到端加密、数据脱敏、访问审计日志。

📌 成就

🏆 本课成就

◀ 上一课 📚 目录 下一课 ▶