news 2026/4/27 21:46:28

Google ADK-Python:用Python高效控制Android设备的官方方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Google ADK-Python:用Python高效控制Android设备的官方方案

1. 项目概述:一个连接Android硬件与Python的桥梁

如果你是一名Android开发者,或者对硬件交互、自动化测试感兴趣,那你大概率遇到过这样的场景:你想用自己熟悉的Python脚本来控制一台Android设备,比如自动安装应用、模拟点击、读取传感器数据,或者进行复杂的端到端测试。过去,你可能需要依赖ADB(Android Debug Bridge)命令行工具,写一堆adb shell命令,然后在Python里用subprocess去调用,过程繁琐,错误处理也麻烦。而google/adk-python这个项目,正是Google官方为解决这类问题而提供的一个优雅方案。

简单来说,google/adk-python是一个Python库,它封装了Android ADB协议,并提供了一套高层级的、面向对象的API。它让你能够像操作一个本地对象一样,去控制远程的Android设备。你可以把它想象成一个“驱动程序”,只不过这个驱动不是给硬件用的,而是给Python程序用来“驱动”Android设备的。它的核心价值在于,将底层复杂的协议通信和命令解析封装起来,暴露给开发者一个简洁、直观的接口,极大地提升了开发效率和代码的可读性、可维护性。

这个项目适合哪些人呢?首先是移动应用测试工程师,尤其是那些正在构建或维护自动化测试框架的团队。其次,是热衷于硬件的极客和开发者,他们可以用Python快速搭建原型,实现Android设备与树莓派、Arduino等其他硬件的联动。最后,对于任何需要批量、程序化操作Android设备的运维或研发场景,这个库都是一个强有力的工具。接下来,我将带你深入拆解这个项目的设计思路、核心用法以及在实际操作中会遇到的那些“坑”。

2. 核心架构与设计哲学解析

2.1 为什么是Python?官方ADB与SDK的补充

在Android开发生态中,官方提供了强大的Android SDK和一系列命令行工具(如ADB)。那么,为什么Google还要推出一个Python版本的ADK呢?这背后的设计哲学,核心在于“生态融合”与“开发者体验”。

Android SDK和ADB工具链主要面向Java/Kotlin开发者,以及构建和部署流程。它们功能强大,但更偏向于底层和系统级操作。当我们需要在更上层的自动化脚本、数据分析流水线或AI模型中集成设备控制能力时,Java/Kotlin的环境可能显得笨重,而Shell脚本的复杂度和可维护性又较差。Python以其简洁的语法、丰富的科学计算与AI库(如NumPy, TensorFlow, OpenCV)以及强大的脚本能力,成为了连接这些领域与Android硬件世界的理想粘合剂。

adk-python并非要取代ADB,而是作为它的一个高层级封装和友好接口。它底层依然通过ADB守护进程(adbd)与设备通信,但将push,pull,shell,forward等命令,抽象成了Device对象的方法和属性。这种面向对象的设计,让代码意图更清晰。例如,你想知道设备序列号,直接访问device.serial属性;想安装一个APK,调用device.install(“path/to/app.apk”)方法即可,无需再拼接adb -s <serial> install这样的字符串。

2.2 核心对象模型:Device, Service与Stream

理解adk-python的架构,关键要抓住三个核心概念:DeviceServiceStream。这是整个库的骨架。

Device对象:这是所有操作的入口和核心。一个Device实例代表了一台物理或虚拟的Android设备。你通过adk.connect()或指定序列号来获取它。这个对象囊括了设备的基本信息(型号、Android版本、屏幕分辨率等)和绝大多数操作方法,如文件传输、应用管理、屏幕截图等。你可以把它看作一个设备的“遥控器”。

Service对象:这是对ADB协议中各种服务的抽象。ADB协议本身是基于客户端-服务器模型的,并且支持多种服务,如shell:,sync:,abb:等。在adk-python中,Service类负责建立和管理到设备上特定服务的连接。例如,当你调用device.shell()执行命令时,内部会创建一个ShellService;当进行文件同步时,会使用SyncService。作为普通使用者,你通常不会直接操作Service,但理解它有助于排查一些连接性问题。

Stream对象:这是数据流动的通道。无论是Shell命令的输出,还是文件传输的字节流,都是通过Stream对象来读取和写入的。库内部处理了数据包的封装、分片和解析,对外提供了类似文件对象的读写接口。这是实现稳定、高效数据传输的基础。

这种分层设计的好处是职责清晰,易于扩展。如果你想增加对新ADB协议命令的支持,理论上只需要在相应的Service子类中实现即可,而不会影响到上层的Device接口。

2.3 异步支持与性能考量

现代Python应用越来越注重并发性能。adk-python从设计之初就考虑到了这一点,它原生支持异步I/O(asyncio)。几乎所有可能涉及等待的操作,如执行一个长时间运行的Shell命令、传输大文件,都提供了异步版本的方法(通常以async_为前缀或可在async上下文中使用)。

这意味着你可以轻松地将设备操作集成到基于asyncio的事件循环中,同时管理多台设备的并行操作,而不会因为某台设备的阻塞操作导致整个程序停顿。对于大规模设备农场(Device Farm)的自动化测试场景,这种异步能力能显著提升任务执行效率和资源利用率。

注意:虽然提供了异步接口,但同步接口仍然是完全可用的。对于简单的脚本或线性任务,使用同步方式编写会更直观。你需要根据自己项目的并发需求来选择。混合使用同步和异步调用时需要小心,避免在未正确管理事件循环的情况下调用异步方法。

3. 环境搭建与基础操作实战

3.1 安装与初步配置:避坑指南

安装adk-python本身非常简单,标准的pip命令即可:

pip install adk-python

但是,安装成功只是第一步,真正容易出问题的是环境配置,尤其是ADB本身的环境。

首要前提:确保ADB在系统路径中adk-python是一个Python封装库,它底层需要调用系统的adb可执行文件来启动服务器并与设备建立初始连接。因此,你必须先安装Android SDK Platform-Tools,并将其中的adb命令所在目录添加到系统的PATH环境变量中。

一个常见的坑是,你可能通过某些IDE(如Android Studio)安装了ADB,但它没有添加到全局路径。在命令行中直接输入adb version,如果提示找不到命令,就说明配置有问题。在Windows上,你需要找到adb.exe的路径(通常在%LOCALAPPDATA%\Android\Sdk\platform-tools\);在macOS/Linux上,通常在~/Android/Sdk/platform-tools/。将其路径添加到PATH中。

连接设备:USB调试与网络连接。和普通ADB一样,你需要先在Android设备的“开发者选项”中开启“USB调试”。通过USB线连接电脑后,设备上可能会弹出“允许USB调试吗?”的授权对话框,务必点击“允许”。对于网络连接(Wi-Fi调试),需要先用USB线执行adb tcpip 5555,然后才能通过adk.connect(host=“设备IP”)进行无线连接。无线连接不稳定是另一个常见问题,在脚本中做好重连逻辑是必要的。

初始化代码示例

import adk # 连接当前通过USB连接的唯一设备(如果有多台会报错) device = adk.connect() # 通过序列号连接指定设备 device = adk.connect(serial=“emulator-5554”) # 通过网络IP连接 device = adk.connect(host=“192.168.1.100”) print(f“设备型号:{device.model}”) print(f“Android版本:{device.version}”)

3.2 设备信息获取与基础交互

成功连接设备后,你就可以像查询对象属性一样获取各种信息。这些信息对于编写自适应脚本非常重要,比如根据屏幕分辨率调整点击坐标,根据API版本决定使用哪些命令。

# 获取基础信息 serial = device.serial model = device.model # 例如,“Pixel 6” sdk_version = device.sdk_version # API级别,如 33 release = device.version # 版本字符串,如 “13” resolution = device.resolution # 返回 (width, height) 元组 # Shell命令执行 # 同步执行,等待命令结束并返回输出 result = device.shell(“pm list packages”) print(result) # 异步执行,适合长时间命令 import asyncio async def run_long_command(): async with device.async_shell(“dumpsys battery”) as shell: async for line in shell: print(line.decode(‘utf-8’, errors=‘ignore’).strip()) # 文件操作 # 推送文件到设备 device.push(“local_file.txt”, “/sdcard/remote_file.txt”) # 从设备拉取文件 device.pull(“/sdcard/DCIM/screenshot.png”, “./local_screenshot.png”) # 屏幕截图(返回PIL.Image对象) image = device.screenshot() image.save(“screen.png”)

这里有一个非常重要的实操心得device.shell()返回的通常是字节串(bytes)而非字符串。直接打印可能看到b‘...‘的前缀,或者包含无法解码的特殊字符。最佳实践是使用.decode(‘utf-8‘, errors=‘ignore‘)进行解码,并忽略无法解码的字符。对于已知是文本的输出,这样做更安全。如果是二进制输出,则应直接处理bytes对象。

4. 高级功能与应用场景深度剖析

4.1 应用管理自动化:安装、卸载与监控

自动化测试中,应用的重装、清理数据是常事。adk-python提供了比原生adb install/uninstall更友好的接口。

# 安装APK # install方法提供了更多选项 try: device.install(“app-debug.apk”, grant_permissions=True) # 安装并授予所有运行时权限 print(“安装成功”) except adk.AdbError as e: print(f“安装失败:{e}”) # 卸载应用 device.uninstall(“com.example.myapp”) # 启动应用 device.shell(“am start -n com.example.myapp/.MainActivity”) # 强制停止应用 device.shell(“am force-stop com.example.myapp”) # 获取当前前台应用 foreground_app = device.shell(“dumpsys window windows | grep -E ‘mCurrentFocus|mFocusedApp‘”).decode() # 需要进一步解析输出,这里只是示例

注意事项grant_permissions=True参数在Android 6.0(API 23)及以上设备上非常有用,它可以自动批准应用所需的所有危险权限,避免了在自动化测试过程中弹出权限请求对话框导致脚本阻塞。但这仅对在AndroidManifest.xml中声明的权限有效,对于运行时动态申请的权限仍需其他处理。

4.2 输入模拟与UI自动化:超越ADB Shell

虽然device.shell(“input tap x y”)可以模拟点击,但adk-pythoninput模块提供了更结构化、更符合编程习惯的方式。

from adk import input # 模拟点击(相对于屏幕左上角) input.tap(device, 500, 1000) # 模拟滑动(从(x1, y1)滑动到(x2, y2),持续200毫秒) input.swipe(device, 100, 500, 400, 500, duration=200) # 模拟文本输入 input.text(device, “Hello, Android!”) # 模拟按键事件,如HOME键、返回键 input.keyevent(device, “HOME”) input.keyevent(device, “BACK”)

对于复杂的UI自动化,单纯坐标点击是脆弱且难以维护的。更佳实践是结合adk-python的截图能力和图像识别库(如opencv-python)或基于Accessibility Service的UI分析工具(如uiautomator2)。你可以用device.screenshot()获取当前屏幕,用OpenCV匹配特定按钮的模板图像,计算出中心坐标后再用input.tap()点击。这样脚本的健壮性会高很多。

4.3 文件系统同步与日志抓取

文件传输是ADB的强项,adk-python的同步服务(SyncService)对此做了很好的封装。

# 推送整个目录(递归) device.push(“./local_dir/”, “/sdcard/remote_dir/”) # 拉取整个目录 device.pull(“/sdcard/logs/”, “./local_logs/”) # 实时抓取Logcat日志 # 这是一个非常强大的功能,可以过滤特定标签和级别的日志 async def capture_logs(): async with device.async_logcat(clear=True) as logcat: # clear=True 先清空旧日志 async for line in logcat: # line 是 bytes log_line = line.decode(‘utf-8‘, errors=‘ignore‘).strip() if “MyAppTag” in log_line and “E/” in log_line: # 过滤错误日志 print(f“捕获到错误:{log_line}”) # 可以在这里触发报警或保存到文件

文件传输的坑:在传输大量小文件时,性能可能不如预期。这是因为每个文件传输都需要建立和关闭同步会话。如果遇到这种情况,可以考虑先在本地将文件打包成.tar格式,传输到设备后再用device.shell(“tar -xf ...”)解压,有时效率更高。另外,确保目标设备(尤其是模拟器)有足够的存储空间,否则push操作会静默失败或报错。

4.4 端口转发与网络调试

对于需要与设备上本地服务(如一个在localhost:8080运行的Web服务器)通信的调试场景,端口转发是必备功能。

# 将设备上的8080端口转发到本机的8080端口 forwarder = device.forward(“tcp:8080”, “tcp:8080”) # 现在,访问本机的 http://localhost:8080 就等于访问设备的 localhost:8080 # ... 执行你的测试,例如用requests库访问本机端口 ... import requests response = requests.get(“http://localhost:8080/api/test”) # 完成后,移除转发规则 forwarder.close() # 反向端口转发:将本机的端口暴露给设备 reverse_forwarder = device.reverse(“tcp:9000”, “tcp:9000”) # 设备上的应用可以连接到 localhost:9000 来访问你电脑上运行的服务

这个功能在调试移动端H5页面、与设备上运行的Node.js或Python服务通信时极其有用。记得在使用完毕后及时关闭forwarderreverse_forwarder,释放端口资源。

5. 构建健壮的自动化脚本:模式与最佳实践

5.1 错误处理与重试机制

网络连接不稳定、设备临时无响应、ADB服务器异常是自动化脚本中的常态。一个健壮的脚本必须有完善的错误处理和重试逻辑。

import time from adk import AdbError def robust_operation(device, operation, max_retries=3): “”“执行一个操作,失败后重试”“” for attempt in range(max_retries): try: return operation(device) except AdbError as e: print(f“操作失败(尝试 {attempt + 1}/{max_retries}): {e}”) if attempt == max_retries - 1: raise # 重试次数用尽,抛出异常 time.sleep(2 ** attempt) # 指数退避等待 except ConnectionResetError: print(“连接断开,尝试重新连接...”) device.reconnect() # 假设有reconnect方法,可能需要重新初始化 time.sleep(1) # 使用示例 def install_app(dev): dev.install(“myapp.apk”) robust_operation(device, install_app)

AdbErroradk-python中大多数操作失败时抛出的异常基类。对于连接类错误,有时可能需要更底层的重连,即重新创建Device对象。你可以将设备序列号或主机信息保存下来,在捕获到特定连接异常时,重新执行adk.connect()

5.2 多设备并行管理

当你需要同时控制多台设备进行兼容性测试或压力测试时,异步编程和线程池是你的好帮手。

import asyncio import adk from concurrent.futures import ThreadPoolExecutor # 假设我们有一个设备序列号列表 device_serials = [“emulator-5554”, “ABCDEF012345”, “192.168.1.100:5555”] async def test_on_device(serial): “”“在单台设备上执行测试任务”“” try: # 注意:adk.connect是同步函数,在异步环境中使用run_in_executor loop = asyncio.get_running_loop() device = await loop.run_in_executor(None, adk.connect, serial) print(f“开始在 {serial} 上测试”) # 执行你的测试步骤,例如安装、运行、截图 await loop.run_in_executor(None, device.install, “test.apk”) screenshot = await loop.run_in_executor(None, device.screenshot) screenshot.save(f“screenshot_{serial}.png”) print(f“{serial} 测试完成”) return True except Exception as e: print(f“设备 {serial} 测试失败:{e}”) return False async def main(): tasks = [test_on_device(serial) for serial in device_serials] results = await asyncio.gather(*tasks, return_exceptions=True) print(f“所有任务完成,结果:{results}”) # 运行 asyncio.run(main())

这里的关键点是,adk-python的核心操作大多是同步的(尽管有异步的logcat等)。在异步主循环中调用它们时,需要使用asyncio.run_in_executor将其放到一个线程池中执行,避免阻塞事件循环。对于真正的I/O密集型操作(如网络传输),这样做是合理且高效的。

5.3 与测试框架集成:Pytest示例

adk-python集成到成熟的测试框架如Pytest中,可以更好地组织测试用例、管理设备状态和生成报告。

# conftest.py import pytest import adk @pytest.fixture(scope=“session”) def connected_device(): “”“提供一个已连接的设备fixture”“” device = adk.connect() yield device # 测试结束后清理,例如卸载测试应用 device.uninstall(“com.example.testapp”, keep_data=False) # test_app.py def test_app_installation(connected_device): device = connected_device device.install(“app-debug.apk”) # 验证应用是否在已安装列表 output = device.shell(“pm list packages | grep com.example.testapp”).decode() assert “com.example.testapp” in output def test_app_launch(connected_device): device = connected_device device.shell(“am start -n com.example.testapp/.MainActivity”) # 通过dumpsys activity检查应用是否在前台 import time time.sleep(2) # 等待应用启动 output = device.shell(“dumpsys activity activities | grep mResumedActivity”).decode() assert “com.example.testapp” in output

使用Pytest的fixture,你可以确保所有测试用例都使用同一个已连接且状态已知的设备,并在测试会话开始和结束时执行统一的准备和清理工作,使测试代码更清晰、更可维护。

6. 常见问题排查与性能优化技巧

6.1 连接失败与超时问题

这是新手最常遇到的问题。下面是一个排查清单:

问题现象可能原因解决方案
adk.AdbError: No devices found1. USB未连接或松动。
2. 设备未开启USB调试。
3. 电脑ADB驱动未安装(Windows)。
4. 设备未授权电脑调试。
1. 检查USB线,尝试不同接口。
2. 进入开发者选项确认。
3. 安装Google USB Driver或设备厂商驱动。
4. 查看设备屏幕是否有授权弹窗,点击“允许”。
Connection refusedConnection reset1. ADB服务器未运行或崩溃。
2. 设备端ADB守护进程(adbd)异常。
3. 网络连接不稳定(Wi-Fi调试)。
1. 命令行执行adb kill-server && adb start-server
2. 重启设备。
3. 检查Wi-Fi网络,或切换回USB连接。
操作超时 (TimeoutError)1. 设备繁忙或无响应。
2. 执行的Shell命令本身长时间不返回。
3. 网络延迟高。
1. 检查设备状态,重启设备。
2. 为长时间命令使用异步接口 (async_shell),并设置合理的超时时间。
3. 优化命令,或增加默认超时时间(如果库支持配置)。

一个实用的技巧是,在脚本开头加入一个“设备健康检查”函数,确保设备在线且响应正常:

def device_health_check(device, timeout=10): try: # 执行一个快速命令,如获取序列号 _ = device.serial # 或者执行一个简单的shell命令 device.shell(“echo ok”, timeout=timeout) return True except Exception: return False

6.2 Shell命令输出乱码与解析

Android设备的Shell输出编码可能不一致,特别是当输出包含非ASCII字符(如中文)或特殊控制字符时。

# 最佳实践:使用通用的解码和清理方式 output_bytes = device.shell(“ls /sdcard/“) # 方法1:尝试UTF-8,忽略错误 output_text = output_bytes.decode(‘utf-8‘, errors=‘ignore‘) # 方法2:更激进地移除控制字符 import re cleaned_text = re.sub(r‘[\x00-\x1f\x7f-\x9f]‘, ‘’, output_text) # 对于已知命令,可以逐行处理 lines = output_text.strip().split(‘\n‘) for line in lines: if line: # 跳过空行 process_line(line)

对于dumpsyslogcat等命令的复杂输出,建议使用专门的解析库(如adb-shell库中的一些解析工具,或自己编写正则表达式)来提取关键信息,而不是手动分割字符串。

6.3 性能瓶颈分析与优化

当脚本处理大量设备或执行大量操作时,性能可能成为问题。

  1. 批量操作替代循环:尽量避免在Python层用for循环执行大量简单的Shell命令。例如,要安装10个APK,可以考虑先将它们推送到设备的一个目录,然后在设备上写一个Shell脚本循环安装,最后只执行一次device.shell(“sh /sdcard/install_all.sh”)。这减少了ADB协议上的往返次数。
  2. 善用异步:对于I/O密集型操作,如同时从多台设备拉取日志,务必使用异步接口。asyncio.gather可以让你同时等待多个异步操作完成,而不是顺序执行。
  3. 连接复用:创建Device对象是有开销的。如果你的脚本需要反复对同一设备进行操作,应该在整个脚本生命周期内保持同一个Device实例,而不是每次操作都重新连接。
  4. 截图优化device.screenshot()会生成一个完整的PNG图像,如果只是为了进行图像识别(比如找某个按钮),这个分辨率可能过高,处理起来慢。你可以先截图,然后用PIL库立即进行缩放,或者探索是否可以通过ADB命令获取压缩率更高的截图(但adk-python可能未直接提供此接口)。

6.4 模拟器与真机的差异处理

模拟器(如Android Emulator)和物理真机在行为上可能存在细微差别,脚本需要具备一定的适应性。

  • 启动速度:模拟器启动慢,脚本开头需要更长的等待时间(time.sleep(30)或轮询检查adb devices)。
  • 网络环境:模拟器通常与宿主机共享网络,而真机可能处于复杂的Wi-Fi或移动网络下。涉及网络请求的测试需要注意。
  • 功能支持:一些硬件相关功能(如GPS、蓝牙、某些传感器)在模拟器上可能是模拟的或不可用。你的脚本在调用相关API前,最好先检查设备属性(device.shell(“getprop ro.hardware”)可以区分)。
  • 文件系统路径:虽然/sdcard/通常都存在,但一些内部路径可能不同。使用标准的环境变量如$EXTERNAL_STORAGE会更安全。

一个简单的设备类型判断和适配逻辑:

def is_emulator(device): output = device.shell(“getprop ro.kernel.qemu”).decode().strip() return output == “1” if is_emulator(device): print(“当前是模拟器,应用模拟器特定设置...”) # 例如,设置模拟器的GPS位置 device.shell(“geo fix 116.397128 39.916527”) else: print(“当前是真机”)

7. 扩展思路:与其他工具链的融合

adk-python本身是一个强大的基础工具,但它的价值在与其他工具链融合时会得到倍增。

与UI自动化框架结合:如前所述,可以结合opencv-python进行图像识别自动化。更进一步,可以集成uiautomator2这个Python库,它通过Accessibility Service提供了更精确的UI元素定位和操作能力。你可以用adk-python负责设备连接、应用安装和基础Shell操作,用uiautomator2负责精细的UI交互。

集成到CI/CD流水线:在Jenkins、GitLab CI或GitHub Actions中,你可以将adk-python脚本作为测试阶段的一部分。CI服务器连接着测试设备(可以是云真机服务提供的设备),在每次代码提交后自动执行安装、冒烟测试、截图等操作,并将结果和日志归档。

设备农场(Device Farm)管理:如果你内部维护着一个由数十台不同型号Android设备组成的测试机柜,你可以用adk-python编写一个调度系统。这个系统接收测试任务,自动寻找空闲的、符合测试要求(如特定系统版本、分辨率)的设备,将测试APK和脚本推送到设备上执行,并收集所有设备的日志和截图,最终生成一份聚合报告。

与Appium的互补:Appium是一个跨平台的移动应用自动化框架,功能全面但相对重量级。adk-python则更轻量、更直接。对于一些不需要跨平台、且对执行速度和控制粒度要求更高的场景(例如,在设备启动后执行一系列初始化配置),用adk-python编写辅助脚本会是更好的选择。

我个人在多个项目中深度使用adk-python的感受是,它完美地填补了ADB命令行工具与高层级测试框架之间的空白。它提供了足够的抽象来提升开发效率,又保持了足够的底层访问能力以满足灵活性的需求。刚开始使用时,你可能会觉得直接写ADB命令更“顺手”,但一旦你习惯了它的面向对象风格和错误处理方式,再回去写那些脆弱的Shell字符串拼接就会感到非常不适应了。最关键的一点是,它是Google官方维护的项目,这意味着它在协议兼容性和未来Android版本的支持上,会比第三方库更有保障。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 21:45:21

掌握3DS游戏格式转换:高效实用的完整操作手册

掌握3DS游戏格式转换&#xff1a;高效实用的完整操作手册 【免费下载链接】3dsconv Python script to convert Nintendo 3DS CCI (".cci", ".3ds") files to the CIA format 项目地址: https://gitcode.com/gh_mirrors/3d/3dsconv 你是否曾经下载了…

作者头像 李华
网站建设 2026/4/27 21:45:20

C++笔记——STL map

在 C STL 中&#xff0c;map 是有序键值对&#xff08;key-value&#xff09;关联容器&#xff0c;它的核心特点是键唯一、自动排序、高效查找&#xff0c;是开发中处理映射关系&#xff08;如字典、配置、索引&#xff09;最常用的容器之一。这篇笔记会从核心特性、头文件、常…

作者头像 李华
网站建设 2026/4/27 21:40:25

如何用Untrunc免费快速修复损坏的MP4视频?终极完整指南

如何用Untrunc免费快速修复损坏的MP4视频&#xff1f;终极完整指南 【免费下载链接】untrunc Restore a truncated mp4/mov. Improved version of ponchio/untrunc 项目地址: https://gitcode.com/gh_mirrors/un/untrunc 你是否曾因珍贵的MP4视频文件损坏而束手无策&…

作者头像 李华
网站建设 2026/4/27 21:38:44

别再手动算距离了!Halcon平面拟合后,一键计算点到平面距离的两种方法

Halcon平面拟合实战&#xff1a;高效计算点到平面距离的两种工程方案 在工业视觉检测和三维点云处理中&#xff0c;平面拟合后的距离计算是个高频需求场景。想象一下这样的工作场景&#xff1a;你已经用Halcon完成了精密零件的平面拟合&#xff0c;接下来需要快速评估上千个测量…

作者头像 李华
网站建设 2026/4/27 21:37:53

【2026年最新600套毕设项目分享】社区二手物品交易小程序(30187)

有需要的同学&#xff0c;源代码和配套文档领取&#xff0c;加文章最下方的名片哦 一、项目演示 项目演示视频 项目演示视频2 项目演示视频3 二、资料介绍 完整源代码&#xff08;前后端源代码SQL脚本&#xff09;配套文档&#xff08;LWPPT开题报告/任务书&#xff09;远…

作者头像 李华
网站建设 2026/4/27 21:37:52

【2026年最新600套毕设项目分享】中国剪纸微信小程序(30188)

有需要的同学&#xff0c;源代码和配套文档领取&#xff0c;加文章最下方的名片哦 一、项目演示 项目演示视频 项目演示视频2 项目演示视频3 二、资料介绍 完整源代码&#xff08;前后端源代码SQL脚本&#xff09;配套文档&#xff08;LWPPT开题报告/任务书&#xff09;远…

作者头像 李华