Vue中Web Serial API串口通信实战指南‌

Vue 中 Web Serial API 串口通信实战指南


目录

  1. 前言
  2. Web Serial API 简介
  3. 项目环境准备

    • 3.1 浏览器兼容性
    • 3.2 Vue 项目初始化
  4. 基本原理与流程

    • 4.1 权限请求与端口选择
    • 4.2 打开/关闭串口
    • 4.3 读写数据流
  5. Vue 中集成 Web Serial API

    • 5.1 组件化思路与目录结构
    • 5.2 串口服务封装 (serialService.js)
    • 5.3 串口管理组件 (SerialManager.vue)
    • 5.4 串口数据交互组件 (SerialTerminal.vue)
  6. 实战示例:与 Arduino 设备通信

    • 6.1 硬件准备与波特率协议
    • 6.2 Vue 端完整示例代码
    • 6.3 数据流动图解
  7. 错误处理与调试技巧

    • 7.1 常见错误类型
    • 7.2 调试建议
  8. 性能与稳定性优化

    • 8.1 流缓冲与节流
    • 8.2 重连与断开重试
  9. 安全与权限注意事项
  10. 总结

前言

随着现代浏览器不断扩展对硬件接口的支持,Web Serial API(串口 API)让前端能够直接操控电脑上的串口设备(如:Arduino、传感器、机器人控制板等),无需编写任何原生应用或安装额外插件。本文将以 Vue 为基础,手把手教你如何在浏览器环境下,通过 Web Serial API 与串口设备进行双向通信。我们会从基础原理讲起,演示如何:

  • 请求串口权限并选择端口
  • 打开与关闭串口
  • 以指定波特率收发数据
  • 在 Vue 组件中将这些逻辑模块化、组件化
  • 结合常见硬件(例如 Arduino)做实战演示

配有详尽的代码示例ASCII 图解关键步骤说明,即便是串口通信新手,也能迅速上手。

声明:Web Serial API 目前在 Chrome、Edge 等基于 Chromium 的浏览器中有较好支持,其他浏览器兼容性有限。请确保使用支持该 API 的浏览器。

Web Serial API 简介

2.1 什么是 Web Serial API

Web Serial API 是一组在浏览器里与本地串口(Serial Port)设备通信的标准接口。通过它,网页可以:

  • 探测并列出用户电脑连接的串口设备
  • 请求用户许可后打开串口,指定波特率、数据位、停止位等参数
  • 通过可读/可写的流(Streams)与设备进行双向数据交换

这一特性尤其适用于物联网、硬件调试、科学实验、工业监控等场景,前端工程师可以直接在网页中完成与硬件的交互。

2.2 浏览器兼容性

截至本文撰写,Web Serial API 在以下环境已获得支持:

浏览器版本支持情况
Chrome89 及以上✅ 支持
Edge89 及以上✅ 支持
Opera75 及以上✅ 支持
Firefox无官方支持❌ 不支持
Safari无官方支持❌ 不支持

若浏览器不支持,需先进行兼容性检查并给出降级提示。


项目环境准备

3.1 浏览器兼容性检查

在 Vue 组件或服务代码中,调用 Web Serial API 之前,需先确认浏览器支持:

function isSerialSupported() {
  return 'serial' in navigator;
}

if (!isSerialSupported()) {
  alert('当前浏览器不支持 Web Serial API,请使用 Chrome 或 Edge 最新版本。');
}

3.2 Vue 项目初始化

以下示例以 Vue 3 + Vite 为基础。若使用 Vue 2 + Vue CLI,改写语法即可。

# 1. 新建 Vue 3 项目(Vite 模板)
npm create vite@latest vue-web-serial -- --template vue

cd vue-web-serial
npm install

# 2. 安装 UI 库(可选,此处不依赖额外 UI)
# npm install element-plus

# 3. 运行开发
npm run dev

完成后,项目目录示例:

vue-web-serial/
├─ public/
├─ src/
│  ├─ assets/
│  ├─ components/
│  │   ├─ SerialManager.vue
│  │   └─ SerialTerminal.vue
│  ├─ services/
│  │   └─ serialService.js
│  ├─ App.vue
│  └─ main.js
├─ index.html
└─ package.json

基本原理与流程

与串口设备通信有以下核心步骤:

  1. 请求权限并选择串口

    • 使用 navigator.serial.requestPort() 弹出设备选择对话框;
    • 获取用户批准后,得到一个 SerialPort 对象。
  2. 打开串口

    • 在端口对象上调用 port.open({ baudRate: 9600, dataBits, stopBits, parity })
    • 返回一个 Promise,当端口成功打开后,可获取可读/可写的流。
  3. 读写数据流

    • 写入:通过 WritableStream 获取 writer = port.writable.getWriter(),再调用 writer.write(Uint8Array) 发送;
    • 读取:从 port.readable.getReader() 中的 reader.read() 获取来自设备的数据流。
  4. 关闭串口

    • 将读写器 reader.cancel()writer.releaseLock(),最后调用 port.close() 释放资源。

4.1 权限请求与端口选择

async function requestPort() {
  if (!('serial' in navigator)) {
    throw new Error('浏览器不支持 Web Serial API');
  }
  // 弹出选择框,用户选择后返回 SerialPort
  const port = await navigator.serial.requestPort();
  return port;
}
注意:调用 requestPort() 必须在用户交互(如点击按钮)触发的回调里,否则会被浏览器拦截。

4.2 打开/关闭串口

async function openPort(port) {
  // 9600 波特率,8 数据位,1 停止位,无奇偶校验
  await port.open({ baudRate: 9600, dataBits: 8, stopBits: 1, parity: 'none' });
}

async function closePort(port) {
  if (port.readable) {
    await port.readable.cancel();
  }
  if (port.writable) {
    await port.writable.getWriter().close();
  }
  await port.close();
}

4.3 读写数据流

写数据

async function writeData(port, dataStr) {
  const encoder = new TextEncoder();
  const writer = port.writable.getWriter();
  await writer.write(encoder.encode(dataStr));
  writer.releaseLock();
}

读数据(使用循环持续读取):

async function readLoop(port, onDataCallback) {
  const decoder = new TextDecoder();
  const reader = port.readable.getReader();
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      const text = decoder.decode(value);
      onDataCallback(text);
    }
  } catch (err) {
    console.error('读取出错:', err);
  } finally {
    reader.releaseLock();
  }
}

Vue 中集成 Web Serial API

为了代码组织清晰,我们将串口读写逻辑封装成一个服务(serialService.js),并在 Vue 组件里调用。

5.1 组件化思路与目录结构

src/
├─ services/
│   └─ serialService.js   # 串口通信核心逻辑
├─ components/
│   ├─ SerialManager.vue  # 串口端口选择、打开/关闭 控制
│   └─ SerialTerminal.vue # 收发数据及显示终端输出
├─ App.vue
└─ main.js
  • serialService.js:封装 requestPortopenPortreadLoopwriteDataclosePort 等函数,导出一个单例对象。
  • SerialManager.vue:提供 UI 让用户请求权限并打开/关闭串口,同时将 SerialPort 对象及读/写状态通过 propsprovide/inject 传给子组件。
  • SerialTerminal.vue:接受已打开的 SerialPort,执行读循环并提供输入框发送数据,可实时显示接收到的文本。

5.2 串口服务封装 (serialService.js)

// src/services/serialService.js

/**
 * 封装 Web Serial API 核心逻辑
 */
class SerialService {
  constructor() {
    this.port = null;        // SerialPort 对象
    this.reader = null;      // ReadableStreamDefaultReader
    this.writer = null;      // WritableStreamDefaultWriter
    this.keepReading = false;
  }

  // 1. 请求用户选择串口
  async requestPort() {
    if (!('serial' in navigator)) {
      throw new Error('浏览器不支持 Web Serial API');
    }
    // 用户交互触发
    this.port = await navigator.serial.requestPort();
    return this.port;
  }

  // 2. 打开串口
  async openPort(options = { baudRate: 9600, dataBits: 8, stopBits: 1, parity: 'none' }) {
    if (!this.port) {
      throw new Error('请先 requestPort()');
    }
    await this.port.open(options);
  }

  // 3. 开始读循环
  async startReading(onData) {
    if (!this.port || !this.port.readable) {
      throw new Error('串口未打开或不可读');
    }
    this.keepReading = true;
    const decoder = new TextDecoder();
    this.reader = this.port.readable.getReader();
    try {
      while (this.keepReading) {
        const { value, done } = await this.reader.read();
        if (done) break;
        const text = decoder.decode(value);
        onData(text);
      }
    } catch (err) {
      console.error('读取失败:', err);
    } finally {
      this.reader.releaseLock();
    }
  }

  // 4. 停止读循环
  async stopReading() {
    this.keepReading = false;
    if (this.reader) {
      await this.reader.cancel();
      this.reader.releaseLock();
      this.reader = null;
    }
  }

  // 5. 写入数据
  async writeData(dataStr) {
    if (!this.port || !this.port.writable) {
      throw new Error('串口未打开或不可写');
    }
    const encoder = new TextEncoder();
    this.writer = this.port.writable.getWriter();
    await this.writer.write(encoder.encode(dataStr));
    this.writer.releaseLock();
  }

  // 6. 关闭串口
  async closePort() {
    await this.stopReading();
    if (this.writer) {
      await this.writer.close();
      this.writer.releaseLock();
      this.writer = null;
    }
    if (this.port) {
      await this.port.close();
      this.port = null;
    }
  }
}

// 导出单例
export default new SerialService();

5.3 串口管理组件 (SerialManager.vue)

负责:请求串口、打开/关闭、显示连接状态。

<template>
  <div class="serial-manager">
    <button @click="handleRequestPort" :disabled="port">
      {{ port ? '已选择端口' : '选择串口设备' }}
    </button>
    <span v-if="port">✔ 已选择设备</span>

    <div v-if="port" class="controls">
      <label>波特率:
        <select v-model="baudRate">
          <option v-for="b in [9600, 19200, 38400, 57600, 115200]" :key="b" :value="b">
            {{ b }}
          </option>
        </select>
      </label>
      <button @click="handleOpenPort" :disabled="isOpen">
        {{ isOpen ? '已打开' : '打开串口' }}
      </button>
      <button @click="handleClosePort" :disabled="!isOpen">
        关闭串口
      </button>
      <span v-if="isOpen" class="status">✔ 串口已打开</span>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import serialService from '@/services/serialService';

const port = ref(null);
const isOpen = ref(false);
const baudRate = ref(9600);

// 1. 请求选择端口
async function handleRequestPort() {
  try {
    const selected = await serialService.requestPort();
    port.value = selected;
  } catch (err) {
    alert('选择串口失败:' + err.message);
  }
}

// 2. 打开串口
async function handleOpenPort() {
  try {
    await serialService.openPort({ baudRate: baudRate.value });
    isOpen.value = true;
  } catch (err) {
    alert('打开串口失败:' + err.message);
  }
}

// 3. 关闭串口
async function handleClosePort() {
  try {
    await serialService.closePort();
    isOpen.value = false;
    port.value = null;
  } catch (err) {
    alert('关闭串口失败:' + err.message);
  }
}
</script>

<style scoped>
.serial-manager {
  margin: 16px 0;
}
.controls {
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.status {
  color: #4caf50;
  font-weight: bold;
}
button {
  padding: 6px 12px;
  background: #409eff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
button:disabled {
  background: #ccc;
  cursor: not-allowed;
}
select {
  margin-left: 4px;
  padding: 4px;
}
</style>
  • handleRequestPort:用户点击,弹出串口选择对话框,成功后将 port 引用存储到本地状态。
  • handleOpenPort:传入选定波特率,调用 serialService.openPort(),打开后将 isOpen 标记为 true
  • handleClosePort:关闭读写并释放资源,重置状态。

5.4 串口数据交互组件 (SerialTerminal.vue)

负责:在串口打开后,执行读循环,将收到的数据展示在“终端”窗口,并提供输入框发送数据。

<template>
  <div class="serial-terminal" v-if="isOpen">
    <div class="terminal-output" ref="outputRef">
      <div v-for="(line, idx) in lines" :key="idx">{{ line }}</div>
    </div>
    <div class="terminal-input">
      <input v-model="inputText" placeholder="输入发送内容" @keydown.enter="sendData" />
      <button @click="sendData">发送</button>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onBeforeUnmount, nextTick } from 'vue';
import serialService from '@/services/serialService';
import { inject } from 'vue';

// 从父组件注入 isOpen 标记
const isOpen = inject('isOpen');
const lines = ref([]);
const inputText = ref('');
const outputRef = ref(null);

// 1. 当 isOpen 变为 true 时,启动读循环
watch(isOpen, async (open) => {
  if (open) {
    lines.value = []; // 清空终端输出
    await serialService.startReading(onDataReceived);
  } else {
    await serialService.stopReading();
  }
});

// 当收到数据时,追加到 lines 数组并自动滚到底部
function onDataReceived(text) {
  lines.value.push(text);
  nextTick(() => {
    const el = outputRef.value;
    el.scrollTop = el.scrollHeight;
  });
}

// 2. 发送输入框内容到串口
async function sendData() {
  if (!inputText.value) return;
  try {
    await serialService.writeData(inputText.value + '\n');
    lines.value.push('▶ ' + inputText.value); // 回显
    inputText.value = '';
    nextTick(() => {
      const el = outputRef.value;
      el.scrollTop = el.scrollHeight;
    });
  } catch (err) {
    alert('发送失败:' + err.message);
  }
}

// 3. 组件卸载时确保关闭读循环
onBeforeUnmount(async () => {
  if (isOpen.value) {
    await serialService.stopReading();
  }
});
</script>

<style scoped>
.serial-terminal {
  border: 1px solid #ccc;
  border-radius: 4px;
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  height: 300px;
}
.terminal-output {
  flex: 1;
  background: #1e1e1e;
  color: #f1f1f1;
  font-family: monospace;
  padding: 8px;
  overflow-y: auto;
}
.terminal-input {
  display: flex;
  border-top: 1px solid #ccc;
}
.terminal-input input {
  flex: 1;
  border: none;
  padding: 8px;
  font-size: 14px;
}
.terminal-input input:focus {
  outline: none;
}
.terminal-input button {
  padding: 8px 12px;
  background: #409eff;
  border: none;
  color: white;
  cursor: pointer;
}
.terminal-input button:hover {
  background: #66b1ff;
}
</style>
  • 使用 watch(isOpen, …):当串口打开(isOpen=true),调用 serialService.startReading(onDataReceived) 启动读循环,并将收到的数据逐行显示;若 isOpen=false,停止读循环。
  • onDataReceived:将接收到的文本推入 lines,并在下一次 DOM 更新后自动滚动到底部。
  • sendData:在输入框按回车或点击“发送”时,将输入内容通过 serialService.writeData 写入串口,并在终端窗口回显。

实战示例:与 Arduino 设备通信

将上述组件组装起来,即可实现浏览器与 Arduino(或其他串口设备)双向通信。

6.1 硬件准备与波特率协议

假设硬件:

  • Arduino Uno
  • 简单程序:打开串口 9600 baud,收到字符后原样回显,并每隔 1 秒发送 “hello from Arduino\n”。

Arduino 示例代码(Arduino.ino):

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) {
    String input = Serial.readStringUntil('\n');
    Serial.print("Echo: ");
    Serial.println(input);
  }
  Serial.println("hello from Arduino");
  delay(1000);
}

6.2 Vue 端完整示例代码

6.2.1 App.vue

<template>
  <div id="app">
    <h1>Vue Web Serial API 串口通信示例</h1>
    <SerialManager />
    <SerialTerminal />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue';
import SerialManager from './components/SerialManager.vue';
import SerialTerminal from './components/SerialTerminal.vue';

// 在根组件提供 isOpen 状态,供子组件注入
const isOpen = ref(false);
provide('isOpen', isOpen);

// 监听子组件的操作,更新 isOpen(通过事件或直接反写)
// 这里用 provide/inject 简化示例,当 SerialManager 打开或关闭时,
// 可手动同步 isOpen(也可使用状态管理方案)
</script>

<style>
body {
  font-family: Arial, sans-serif;
  padding: 16px;
}
#app {
  max-width: 800px;
  margin: 0 auto;
}
</style>

6.2.2 说明

  • App.vue 通过 provide('isOpen', isOpen)isOpen 标记提供给子组件;
  • SerialManager.vue 中,一旦成功打开串口,应更新 isOpen.value = true;关闭串口时 isOpen.value = false
  • SerialTerminal.vue 通过 inject('isOpen') 获取同一个响应式标记,自动启动/停止读循环。

6.3 数据流动图解

┌────────────────────────────────────────────┐
│            用户点击“选择串口”按钮           │
└────────────────────────────────────────────┘
     ↓ SerialManager.handleRequestPort
┌────────────────────────────────────────────┐
│ navigator.serial.requestPort() → 用户选择串口 │
└────────────────────────────────────────────┘
     ↓ SerialManager.handleOpenPort
┌────────────────────────────────────────────┐
│ serialService.openPort({ baudRate:9600 }) │
│ → Arduino 与 Chrome 建立串口连接            │
└────────────────────────────────────────────┘
  isOpen.value = true (provide/inject 触发)
     ↓ SerialTerminal.watch(isOpen)
┌────────────────────────────────────────────┐
│ serialService.startReading(onDataReceived) │
│  ↓                                       │
│  Arduino 每秒发送 “hello from Arduino\n”  │
│  Chrome 通过 reader.read() 读取到字符串    │
│  调用 onDataReceived(text) → lines.push() │
└────────────────────────────────────────────┘
     ↓ SerialTerminal.onDataReceived
┌────────────────────────────────────────────┐
│ 终端输出区域渲染新行 “hello from Arduino”  │
└────────────────────────────────────────────┘
     ↓ 用户在 SerialTerminal 输入 “Test\n”
┌────────────────────────────────────────────┐
│ SerialTerminal.sendData → serialService.writeData("Test\n") │
│ → Arduino 接收到 “Test” 并回显 “Echo: Test”                │
└────────────────────────────────────────────┘
     ↓ Arduino → Chrome 通过读循环读取 “Echo: Test\n”
┌────────────────────────────────────────────┐
│ SerialTerminal.onDataReceived → lines.push("Echo: Test")  │
│ → 终端输出 “Echo: Test”                           │
└────────────────────────────────────────────┘

错误处理与调试技巧

7.1 常见错误类型

  1. NotFoundError

    • 表示没有找到任何可用串口,或用户在选择对话框中点击“取消”。
    • 需在 catch 中捕获并提示用户。
  2. SecurityError

    • 触发时机:在非 HTTPS 环境下调用 Web Serial API。
    • 解决:将页面部署到 HTTPS 环境,或使用 localhost 进行本地开发。
  3. NetworkError / InvalidStateError

    • 在串口已打开但硬件被拔掉、连接中断时可能出现。
    • 建议在捕获后执行重连或关闭清理。
  4. 读写冲突

    • 在尚未完成前一次 reader.read()writer.write() 时,重复调用会抛错。
    • 建议采用“先释放锁再重新获取锁”的方式,或检查 readerwriter 状态。

7.2 调试建议

  • 查看浏览器控制台

    • 在 Chrome DevTools → Application → Serial 中可查看当前已连接的串口设备;
    • 在 Console 面板观察错误信息和日志输出。
  • 使用串口调试助手

    • 在 PC 上安装独立串口调试软件(如 “YAT”、“PuTTY”)调试 Arduino 程序,确保 Arduino 程序正常运行并在标准串口发送/接收。
  • 波特率和协议匹配

    • 确认 Arduino 端使用 Serial.begin(9600) 与前端 openPort({ baudRate: 9600 }) 一致;
    • 若串口设备发送二进制数据而非文本,需使用 Uint8Array 而非 TextEncoder/TextDecoder
  • 日志与断点

    • serialService 的各关键步骤(openPortstartReadingwriteData)添加 console.log
    • 使用 DevTools 中断点调试,跟踪 reader.read() 返回的数据。

性能与稳定性优化

8.1 流缓冲与节流

  • 连续读写

    • 如果数据量较大(如设备在短时间内发送大量数据),需在 onDataReceived 里做节流,例如累积一段时间后再更新 UI:

      let buffer = '';
      let timer = null;
      function onDataReceived(text) {
        buffer += text;
        if (!timer) {
          timer = setTimeout(() => {
            lines.value.push(buffer);
            buffer = '';
            timer = null;
            scrollToBottom();
          }, 200); // 每 200ms 更新一次
        }
      }
  • 写入频率限制

    • 若前端频繁调用 writeData,串口设备可能来不及处理,建议控制发送间隔或检查设备“就绪”信号。

8.2 重连与断开重试

  • 串口在长期通信中可能因为设备拔插、电脑睡眠导致断开,可监听 port.readable 或捕获 read() 读出错后尝试重连:

    async function safeReadLoop(onData) {
      try {
        await serialService.startReading(onData);
      } catch (err) {
        console.warn('读循环中断,尝试重连…');
        await serialService.closePort();
        // 等待 2 秒后重连
        setTimeout(async () => {
          await serialService.openPort();
          safeReadLoop(onData);
        }, 2000);
      }
    }
  • 或在 reader.read() 捕获异常后,主动关闭并重新执行 openPort + startReading

安全与权限注意事项

  1. 仅限 HTTPS

    • Web Serial API 必须在 安全上下文(HTTPS 或 localhost)下使用,否则会报 SecurityError
  2. 显式用户交互调用

    • navigator.serial.requestPort() 必须出现在用户手动点击回调中,否则被浏览器阻止。如果希望“自动重连”或“静默打开”,须先做好用户授权。
  3. 设备权限仅在页面生命周期内有效

    • 用户选择端口后,若页面刷新或关闭,需要重新调用 requestPort()。无法跨页面或跨站点持久化。
  4. 主动关闭端口

    • 在页面 unloadbeforeunload 事件里,确保调用 serialService.closePort() 释放硬件资源,否则相同设备无法二次打开。
  5. 避免泄露设备数据

    • 串口数据可能包含敏感信息,应在前端或后端加密或脱敏,避免在开发者工具中暴露。

总结

本文围绕 Vue 中 Web Serial API 串口通信,从基础原理到完整示例进行了系统介绍,关键内容包括:

  • Web Serial API:了解许可请求、打开串口、读写流、关闭串口的核心流程;
  • Vue 集成架构:通过 serialService.js 将串口逻辑抽象为可复用服务;通过 SerialManager.vue 管理串口端口与状态;通过 SerialTerminal.vue 实现终端式数据收发与显示;
  • 实战示例:与 Arduino 设备进行双向通信,Arduino 定时发送 “hello” 并回显收到的数据,前端可发送指令并查看实时回显;
  • 错误处理与调试:列举了常见错误类型(权限、兼容性、读写冲突)和解决思路;
  • 性能优化:提供流缓冲节流、重连机制示例,保证在大量数据或断连场景下稳定运行;
  • 安全与权限:强调必须在 HTTPS 环境下使用,权限仅在同一页面会话中有效,务必在卸载时主动关闭串口。

通过本文示例与说明,相信你已经掌握了在现代浏览器中,利用 Vue 框架调用 Web Serial API 与物理串口设备通信的全流程。后续你可以将其扩展到更复杂的工业控制、物联网可视化、机器人调试界面等场景,轻松打造高效、便捷的硬件交互 Web 应用。

VUE
最后修改于:2025年05月31日 12:29

评论已关闭

推荐阅读

DDPG 模型解析,附Pytorch完整代码
2024年11月24日
DQN 模型解析,附Pytorch完整代码
2024年11月24日
AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日