实用程序:无需付费软件!自制音视频转字幕工具,复制代码直接运行

简介: 一款基于Whisper模型的音视频转字幕工具,支持多格式文件,提供可视化界面与实时进度反馈,可自动识别语音并生成简体SRT字幕,操作简单,开源免费,显著提升字幕制作效率。

前言

在多媒体内容爆炸的时代,为音视频添加字幕成为提升内容可访问性、传播效率的重要手段。无论是自媒体创作者、教育工作者还是普通用户,都可能面临手动制作字幕耗时费力的问题。基于此,我开发了一款"音视频转字幕工具",借助OpenAI的Whisper语音识别模型,实现从音视频文件到标准SRT字幕的自动化转换。

这款工具结合了moviepy的音视频处理能力、whisper的语音识别能力和tkinter的可视化界面,让用户无需专业知识即可快速生成字幕。下面将详细介绍工具的实现原理与使用方法。

已在github开源:https://github.com/ChenAI-TGF/Audio_And_Video_Transcription

一、工具介绍

这款音视频转字幕工具具备以下核心功能:

  1. 多格式支持:兼容MP4、AVI、MOV等视频格式及MP3、WAV等音频格式
  2. 灵活参数配置:可选择Whisper模型(tiny/base/small/medium/large)、调整线程数和温度值
  3. 实时进度反馈:双进度条分别显示音频提取和字幕识别进度
  4. 时间估算:实时显示处理耗时和预计剩余时间
  5. 繁简转换:自动将识别结果转换为简体中文
  6. 标准字幕输出:生成符合SRT格式的字幕文件,可直接用于视频编辑

工具的优势在于可视化操作降低了技术门槛,同时保留了参数调整的灵活性,兼顾了普通用户和进阶用户的需求。

二、代码核心部分详解

1. 音频提取模块

音视频文件首先需要提取音频轨道,这一步由video_to_audio方法实现:

def video_to_audio(self, video_path: str, audio_path: str) -> bool:
    try:
        with VideoFileClip(video_path) as video:
            total_duration = video.duration
            audio = video.audio

            # 记录音频转换开始时间
            self.audio_start_time = datetime.now()

            # 写入音频(logger=None关闭冗余输出)
            audio.write_audiofile(audio_path, logger=None)
            # 强制进度到100%
            self.update_audio_progress(100.0)
        return True
    except Exception as e:
        messagebox.showerror("错误", f"音视频转音频失败:{str(e)}")
        return False

核心逻辑:使用moviepyVideoFileClip读取视频文件,提取音频轨道后写入WAV格式文件。对于本身就是音频的文件(如MP3),会直接跳过提取步骤

2. Whisper模型加载与语音识别

语音转文字是工具的核心功能,基于OpenAI的Whisper模型实现:

def load_whisper_model(self) -> Optional[whisper.Whisper]:
    try:
        model_name = self.model_var.get()
        self.update_transcribe_progress(10)
        # 加载指定模型,使用CPU运行(可改为"cuda"启用GPU加速)
        model = whisper.load_model(model_name, device="cpu") 
        self.update_transcribe_progress(20)
        return model
    except Exception as e:
        messagebox.showerror("错误", f"模型加载失败:{str(e)}")
        return None

def transcribe_audio(self, audio_path: str) -> Optional[dict]:
    global is_running
    self.model = self.load_whisper_model()
    if not self.model or not is_running:
        return None

    try:
        # 记录字幕识别开始时间
        self.transcribe_start_time = datetime.now()

        # 分段识别模拟进度
        self.update_transcribe_progress(30)
        result = self.model.transcribe(
            audio_path,
            language="zh",  # 指定中文识别
            temperature=self.temp_var.get(),  # 控制输出随机性
        )
        self.update_transcribe_progress(80)

        if not is_running:
            return None

        self.update_transcribe_progress(100)
        return result
    except Exception as e:
        messagebox.showerror("错误", f"字幕识别失败:{str(e)}")
        return None

核心逻辑

  • 先加载用户选择的Whisper模型(模型越小速度越快,精度越低)
  • 通过transcribe方法处理音频,指定language="zh"优化中文识别效果
  • temperature参数控制输出随机性(0表示确定性输出,适合字幕生成)

3. SRT字幕格式化

识别结果需要转换为标准SRT格式,包含序号、时间轴和文本:

def format_srt(self, result: dict) -> str:
    srt_content = ""
    for i, segment in enumerate(result["segments"], 1):
        start = self.format_time(segment["start"])
        end = self.format_time(segment["end"])
        # 繁简转换(将可能的繁体转为简体)
        text = self.cc.convert(segment["text"].strip())
        srt_content += f"{i}\n{start} --> {end}\n{text}\n\n"
    return srt_content

@staticmethod
def format_time(seconds: float) -> str:
    """将秒数格式化为SRT时间格式(hh:mm:ss,fff)"""
    hours = math.floor(seconds / 3600)
    minutes = math.floor((seconds % 3600) / 60)
    secs = math.floor(seconds % 60)
    millis = math.floor((seconds % 1) * 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"

核心逻辑

  • 解析Whisper返回的分段结果(包含开始时间、结束时间和文本)
  • 将时间戳转换为SRT要求的hh:mm:ss,fff格式
  • 使用OpenCC进行繁简转换,确保输出统一为简体中文

4. 多线程与进度管理

为避免UI卡顿,核心处理逻辑在独立线程中运行:

def start_convert(self):
    """启动转换线程"""
    thread = threading.Thread(target=self.convert_thread, daemon=True)
    thread.start()

def convert_thread(self):
    """转换线程(避免UI卡顿)"""
    global is_running
    is_running = True
    self.start_btn.config(state=tk.DISABLED)
    self.stop_btn.config(state=tk.NORMAL)

    # 重置进度和时间
    self.update_audio_progress(0.0)
    self.update_transcribe_progress(0.0)
    self.result_text.delete(1.0, tk.END)
    self.total_start_time = datetime.now()

    # 核心处理流程
    input_path = self.file_path_var.get()
    # 音频提取 -> 语音识别 -> 字幕格式化 -> 保存文件
    # ...(省略具体步骤)

核心逻辑

  • 将耗时的音频处理和识别任务放入子线程
  • 通过全局变量is_running实现主线程与子线程的通信
  • 实时更新进度条和时间显示,提升用户体验

    三、完整代码

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
from moviepy import VideoFileClip
import whisper
import threading
import time
from datetime import datetime, timedelta
from typing import Optional
import math
from opencc import OpenCC

# 全局变量用于控制进度和线程
audio_convert_progress = 0.0
transcribe_progress = 0.0
is_running = False

class AudioVideoToSubtitle:
    def __init__(self, root):
        self.root = root
        self.root.title("音视频转字幕工具")
        self.root.geometry("900x650")  # 扩大窗口以容纳时间显示

        # 初始化繁简转换器
        self.cc = OpenCC('t2s')

        # 时间跟踪变量
        self.total_start_time = None  # 总处理开始时间
        self.audio_start_time = None  # 音频转换开始时间
        self.transcribe_start_time = None  # 字幕识别开始时间
        self.time_update_id = None  # 时间更新定时器ID

        # 初始化模型
        self.model = None
        self.model_path = None

        self.init_ui()

    def init_ui(self):
        # 1. 文件选择区域
        file_frame = ttk.LabelFrame(self.root, text="文件设置")
        file_frame.pack(fill=tk.X, padx=10, pady=5)

        self.file_path_var = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.file_path_var, width=70).pack(side=tk.LEFT, padx=5, pady=5)
        ttk.Button(file_frame, text="选择文件", command=self.select_file).pack(side=tk.LEFT, padx=5, pady=5)

        # 2. 速度调节参数区域
        param_frame = ttk.LabelFrame(self.root, text="速度调节参数")
        param_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(param_frame, text="模型选择:").pack(side=tk.LEFT, padx=5, pady=5)
        self.model_var = tk.StringVar(value="base")
        model_options = ["tiny", "base", "small", "medium", "large"]
        ttk.Combobox(param_frame, textvariable=self.model_var, values=model_options, width=10).pack(side=tk.LEFT, padx=5, pady=5)

        ttk.Label(param_frame, text="线程数:").pack(side=tk.LEFT, padx=5, pady=5)
        self.thread_var = tk.IntVar(value=4)
        ttk.Spinbox(param_frame, from_=1, to=16, textvariable=self.thread_var, width=5).pack(side=tk.LEFT, padx=5, pady=5)

        ttk.Label(param_frame, text="温度值:").pack(side=tk.LEFT, padx=5, pady=5)
        self.temp_var = tk.DoubleVar(value=0.0)
        ttk.Spinbox(param_frame, from_=0.0, to=1.0, increment=0.1, textvariable=self.temp_var, width=5).pack(side=tk.LEFT, padx=5, pady=5)

        # 3. 进度条和时间显示区域
        progress_frame = ttk.LabelFrame(self.root, text="处理进度与时间")
        progress_frame.pack(fill=tk.X, padx=10, pady=5)

        # 音视频转音频进度条
        ttk.Label(progress_frame, text="音视频转音频:").pack(side=tk.LEFT, padx=5)
        self.audio_progress = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')
        self.audio_progress.pack(side=tk.LEFT, padx=5, pady=5)
        self.audio_progress_label = ttk.Label(progress_frame, text="0%")
        self.audio_progress_label.pack(side=tk.LEFT, padx=5)

        # 字幕识别进度条
        ttk.Label(progress_frame, text="字幕识别:").pack(side=tk.LEFT, padx=5)
        self.transcribe_progress = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')
        self.transcribe_progress.pack(side=tk.LEFT, padx=5, pady=5)
        self.transcribe_progress_label = ttk.Label(progress_frame, text="0%")
        self.transcribe_progress_label.pack(side=tk.LEFT, padx=5)

        # 时间显示区域
        time_frame = ttk.LabelFrame(self.root, text="时间信息")
        time_frame.pack(fill=tk.X, padx=10, pady=5)

        self.elapsed_time_var = tk.StringVar(value="已处理时间: 00:00:00")
        ttk.Label(time_frame, textvariable=self.elapsed_time_var).pack(side=tk.LEFT, padx=20, pady=5)

        self.estimated_time_var = tk.StringVar(value="预计剩余时间: --:--:--")
        ttk.Label(time_frame, textvariable=self.estimated_time_var).pack(side=tk.LEFT, padx=20, pady=5)

        # 4. 控制按钮区域
        btn_frame = ttk.Frame(self.root)
        btn_frame.pack(pady=10)

        self.start_btn = ttk.Button(btn_frame, text="开始转换", command=self.start_convert)
        self.start_btn.pack(side=tk.LEFT, padx=10)
        self.stop_btn = ttk.Button(btn_frame, text="停止转换", command=self.stop_convert, state=tk.DISABLED)
        self.stop_btn.pack(side=tk.LEFT, padx=10)

        # 5. 结果显示区域
        result_frame = ttk.LabelFrame(self.root, text="识别结果预览")
        result_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        self.result_text = tk.Text(result_frame, height=15)
        scrollbar = ttk.Scrollbar(result_frame, command=self.result_text.yview)
        self.result_text.configure(yscrollcommand=scrollbar.set)
        self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, padx=5, pady=5)

    def select_file(self):
        """选择音视频文件"""
        file_types = [
            ("音视频文件", "*.mp4 *.avi *.mov *.mkv *.flv *.mp3 *.wav *.m4a"),
            ("所有文件", "*.*")
        ]
        file_path = filedialog.askopenfilename(filetypes=file_types)
        if file_path:
            self.file_path_var.set(file_path)

    def update_audio_progress(self, value: float):
        """更新音频转换进度条"""
        global audio_convert_progress
        audio_convert_progress = min(value, 100.0)
        self.audio_progress["value"] = audio_convert_progress
        self.audio_progress_label.config(text=f"{int(audio_convert_progress)}%")
        self.root.update_idletasks()

    def update_transcribe_progress(self, value: float):
        """更新字幕识别进度条"""
        global transcribe_progress
        transcribe_progress = min(value, 100.0)
        self.transcribe_progress["value"] = transcribe_progress
        self.transcribe_progress_label.config(text=f"{int(transcribe_progress)}%")
        self.root.update_idletasks()

    def format_time_display(self, seconds: float) -> str:
        """将秒数格式化为时分秒显示"""
        hours, remainder = divmod(int(seconds), 3600)
        minutes, seconds = divmod(remainder, 60)
        return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

    def update_time_display(self):
        """更新时间显示信息"""
        if not is_running or not self.total_start_time:
            return

        # 计算已处理时间
        elapsed_seconds = (datetime.now() - self.total_start_time).total_seconds()
        self.elapsed_time_var.set(f"已处理时间: {self.format_time_display(elapsed_seconds)}")

        # 计算预计剩余时间
        try:
            if audio_convert_progress < 100:
                # 音频转换阶段
                if self.audio_start_time and audio_convert_progress > 0:
                    audio_elapsed = (datetime.now() - self.audio_start_time).total_seconds()
                    total_audio_estimated = audio_elapsed / (audio_convert_progress / 100)
                    audio_remaining = total_audio_estimated - audio_elapsed

                    # 假设转录时间与音频时间相当(简单估算)
                    total_estimated = total_audio_estimated * 2
                    remaining = total_estimated - elapsed_seconds
                    self.estimated_time_var.set(f"预计剩余时间: {self.format_time_display(remaining)}")
            else:
                # 字幕识别阶段
                if self.transcribe_start_time and transcribe_progress > 0 and transcribe_progress < 100:
                    transcribe_elapsed = (datetime.now() - self.transcribe_start_time).total_seconds()
                    total_transcribe_estimated = transcribe_elapsed / (transcribe_progress / 100)
                    transcribe_remaining = total_transcribe_estimated - transcribe_elapsed
                    self.estimated_time_var.set(f"预计剩余时间: {self.format_time_display(transcribe_remaining)}")
                elif transcribe_progress >= 100:
                    self.estimated_time_var.set(f"预计剩余时间: 00:00:00")
        except (ZeroDivisionError, Exception):
            self.estimated_time_var.set(f"预计剩余时间: 计算中...")

        # 继续定时更新
        self.time_update_id = self.root.after(1000, self.update_time_display)

    def video_to_audio(self, video_path: str, audio_path: str) -> bool:
        """音视频转音频,带进度更新"""
        try:
            with VideoFileClip(video_path) as video:
                total_duration = video.duration
                audio = video.audio

                # 记录音频转换开始时间
                self.audio_start_time = datetime.now()

                # 写入音频
                audio.write_audiofile(audio_path, logger=None)
                # 强制进度到100%
                self.update_audio_progress(100.0)
            return True
        except Exception as e:
            messagebox.showerror("错误", f"音视频转音频失败:{str(e)}")
            return False

    def load_whisper_model(self) -> Optional[whisper.Whisper]:
        """加载whisper模型"""
        try:
            model_name = self.model_var.get()
            self.update_transcribe_progress(10)
            model = whisper.load_model(model_name, device="cpu") 
            self.update_transcribe_progress(20)
            return model
        except Exception as e:
            messagebox.showerror("错误", f"模型加载失败:{str(e)}")
            return None

    def transcribe_audio(self, audio_path: str) -> Optional[dict]:
        """音频转字幕,带进度更新"""
        global is_running
        self.model = self.load_whisper_model()
        if not self.model or not is_running:
            return None

        try:
            # 记录字幕识别开始时间
            self.transcribe_start_time = datetime.now()

            # 分段识别模拟进度
            self.update_transcribe_progress(30)
            result = self.model.transcribe(
                audio_path,
                language="zh",
                temperature=self.temp_var.get(),
            )
            self.update_transcribe_progress(80)

            if not is_running:
                return None

            self.update_transcribe_progress(100)
            return result
        except Exception as e:
            messagebox.showerror("错误", f"字幕识别失败:{str(e)}")
            return None

    def format_srt(self, result: dict) -> str:
        """将识别结果格式化为SRT字幕格式"""
        srt_content = ""
        for i, segment in enumerate(result["segments"], 1):
            start = self.format_time(segment["start"])
            end = self.format_time(segment["end"])
            text = self.cc.convert(segment["text"].strip())
            srt_content += f"{i}\n{start} --> {end}\n{text}\n\n"
        return srt_content

    @staticmethod
    def format_time(seconds: float) -> str:
        """将秒数格式化为SRT时间格式(hh:mm:ss,fff)"""
        hours = math.floor(seconds / 3600)
        minutes = math.floor((seconds % 3600) / 60)
        secs = math.floor(seconds % 60)
        millis = math.floor((seconds % 1) * 1000)
        return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"

    def save_subtitle(self, srt_content: str, input_path: str):
        """保存字幕文件"""
        save_path = os.path.splitext(input_path)[0] + ".srt"
        try:
            with open(save_path, "w", encoding="utf-8") as f:
                f.write(srt_content)
            messagebox.showinfo("成功", f"字幕已保存至:\n{save_path}")
            return save_path
        except Exception as e:
            messagebox.showerror("错误", f"字幕保存失败:{str(e)}")
            return None

    def convert_thread(self):
        """转换线程(避免UI卡顿)"""
        global is_running
        is_running = True
        self.start_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)

        # 重置进度和时间
        self.update_audio_progress(0.0)
        self.update_transcribe_progress(0.0)
        self.result_text.delete(1.0, tk.END)
        self.total_start_time = datetime.now()
        self.audio_start_time = None
        self.transcribe_start_time = None

        # 启动时间更新
        self.root.after(0, self.update_time_display)

        input_path = self.file_path_var.get()
        if not os.path.exists(input_path):
            messagebox.showwarning("警告", "请选择有效的音视频文件!")
            self.reset_ui()
            return

        # 临时音频文件路径
        temp_audio = "temp_audio.wav"
        try:
            # 步骤1:音视频转音频
            if not is_running:
                return
            if input_path.lower().endswith(("mp3", "wav", "m4a")):
                # 已是音频文件,跳过转换
                self.update_audio_progress(100.0)
                audio_path = input_path
                self.audio_start_time = datetime.now()  # 标记音频处理完成时间
            else:
                if not self.video_to_audio(input_path, temp_audio):
                    return
                audio_path = temp_audio

            # 步骤2:音频转字幕
            if not is_running:
                return
            result = self.transcribe_audio(audio_path)
            if not result or not is_running:
                return

            # 步骤3:格式化并显示结果
            srt_content = self.format_srt(result)
            self.result_text.insert(1.0, srt_content)

            # 步骤4:保存字幕
            self.save_subtitle(srt_content, input_path)

        finally:
            # 清理临时文件
            if os.path.exists(temp_audio) and not input_path.lower().endswith(("mp3", "wav", "m4a")):
                os.remove(temp_audio)
            self.reset_ui()

    def start_convert(self):
        """启动转换线程"""
        thread = threading.Thread(target=self.convert_thread, daemon=True)
        thread.start()

    def stop_convert(self):
        """停止转换"""
        global is_running
        is_running = False
        self.stop_btn.config(state=tk.DISABLED)
        self.result_text.insert(tk.END, "\n\n转换已停止!")

    def reset_ui(self):
        """重置UI状态"""
        global is_running
        is_running = False
        self.start_btn.config(state=tk.NORMAL)
        self.stop_btn.config(state=tk.DISABLED)

        # 停止时间更新
        if self.time_update_id:
            self.root.after_cancel(self.time_update_id)
            self.time_update_id = None

        # 重置时间显示
        self.elapsed_time_var.set("已处理时间: 00:00:00")
        self.estimated_time_var.set("预计剩余时间: --:--:--")

if __name__ == "__main__":
    # 提示安装依赖
    try:
        import torch
        import opencc
    except ImportError as e:
        missing = str(e).split("'")[1]
        messagebox.showwarning("提示", f"请先安装依赖库:\npip install torch moviepy openai-whisper ffmpeg-python opencc-python-reimplemented")
        exit()

    root = tk.Tk()
    app = AudioVideoToSubtitle(root)
    root.mainloop()

四、效果演示

  1. 工具启动:运行程序后显示主界面,包含文件选择、参数配置、进度显示和结果预览区域。

1.png

  1. 文件选择:点击"选择文件"按钮,选择需要转换的音视频文件(如MP4格式视频)。

2.png

  1. 参数配置
    • 模型选择:根据需求选择(tiny最快,large最精准)
    • 线程数:根据CPU核心数调整(建议4-8)
    • 温度值:默认0.0(适合字幕生成)

3.png

  1. 开始转换
    • 点击"开始转换",音频提取进度条开始推进
    • 提取完成后,字幕识别进度条启动
    • 实时显示"已处理时间"和"预计剩余时间"

4.png

  1. 结果查看
    • 识别完成后,结果预览区显示SRT格式字幕
    • 自动保存与原文件同名的SRT文件(如"视频.mp4"生成"视频.srt")
    • 弹窗提示保存路径

5.png

  1. 中途停止:如需中断,点击"停止转换"按钮,工具会清理临时文件并重置状态。

五、第三方库安装

1、需要的库

该音视频转字幕工具的代码依赖以下第三方库,以下是各库的作用及安装方法:

  1. torch(PyTorch)
  • 作用:Whisper模型运行的基础框架,用于加载和运行语音识别模型(Whisper基于PyTorch实现)。
  • 安装命令
    推荐根据系统和是否需要GPU加速,从PyTorch官网获取对应命令,基础CPU版本可直接安装:
    pip install torch
    
  1. moviepy
  • 作用:音视频处理库,用于从视频中提取音频轨道(核心功能之一)。
  • 安装命令
    pip install moviepy
    
  1. openai-whisper
  • 作用:OpenAI官方的语音识别库,提供Whisper模型(实现语音转文字的核心功能)。
  • 安装命令
    pip install openai-whisper
    
  1. ffmpeg-python
  • 作用moviepy处理音视频时依赖的底层工具封装,用于实际执行音视频编解码操作。
  • 注意:除了安装Python库,还需要在系统中安装ffmpeg程序(否则moviepy可能无法正常工作):
    • Python库安装
      pip install ffmpeg-python
      
    • 系统级ffmpeg安装
      • Windows:从ffmpeg官网下载安装包,解压后将bin目录添加到系统环境变量。
      • Ubuntu/Debian:sudo apt-get install ffmpeg
      • macOS:brew install ffmpeg(需先安装Homebrew)
  1. opencc-python-reimplemented
  • 作用:繁简转换库,用于将识别结果中的繁体中文自动转换为简体中文(代码中通过OpenCC('t2s')实现)。
  • 安装命令
    pip install opencc-python-reimplemented
    

2、一条命令安装所有依赖

可将上述命令整合为一条安装命令(推荐使用国内镜像源如-i https://pypi.tuna.tsinghua.edu.cn/simple加速):

pip install torch moviepy openai-whisper ffmpeg-python opencc-python-reimplemented -i https://pypi.tuna.tsinghua.edu.cn/simple

3、注意事项

  • 首次运行时,Whisper会自动下载选择的模型(如base模型约1GB),请确保网络畅通。
  • 若电脑有NVIDIA显卡且安装了CUDA,可将代码中device="cpu"改为device="cuda",显著提升识别速度(需安装对应CUDA版本的PyTorch)。

    总结

这款音视频转字幕工具通过整合moviepywhisper的强大功能,实现了文字识别字幕生成的自动化流程。核心优势在于:

  1. 易用性:可视化界面降低了技术门槛,无需命令行操作
  2. 灵活性:可通过模型选择平衡速度与精度
  3. 实用性:生成标准SRT格式,直接适配主流视频编辑软件

无论是自媒体创作者快速制作字幕,还是学习者为教学视频添加字幕,这款工具都能显著提升效率,降低字幕制作的技术门槛。

目录
相关文章
|
1月前
|
编解码 并行计算 物联网
【实战教程】Flux.1-dev 360° 全景驾驶舱:从零到一的“零坑”部署指南
本教程教你如何在24G显存单卡环境下部署超大文生图模型Flux.1-dev。通过ModelScope极速下载、Hugging Face身份鉴权与显存顺序卸载技术,解决下载慢、授权难、显存溢出等问题,实现稳定推理。
267 1
|
关系型数据库 MySQL 数据库
tushare宏观数据使用pandas入库,增加唯一索引
1,对pandas入数据库 pandas代码中自带了to_sql的方法可以直接使用。 但是数据字段是text的,需要修改成 varchar的,否则不能增加索引。 在增加了主键之后可以控制数据不能再增量修改了。 增加:dtype类型可以解决 dtype={col_name: NVARCHAR(length=255) for col_name in dat
3805 0
|
16天前
|
人工智能 定位技术 SoC
2026年美赛A题——翻译及建模完整思路
本题要求构建锂离子电池荷电状态(SOC)的**显式连续时间数学模型**,通过微分方程刻画SOC随时间变化规律,融合屏幕、CPU、网络等多因素功耗影响;用于预测不同场景下剩余使用时间(TTE),开展不确定性量化、敏感性分析,并转化为用户节电建议与系统优化策略。(239字)
329 3
|
16天前
|
人工智能 安全
2026年美赛B题——翻译及建模完整思路
本题要求构建数学模型,对比太空电梯(三座银河港)与传统火箭两种运输方式,优化10万人月球殖民地建设(需1亿吨物资)及运营(年水资源补给)的运输方案,在成本、工期、可靠性与环境影响多目标下寻求最优解,并提出可落地的实施建议。(239字)
273 2
|
25天前
|
IDE 自动驾驶 Linux
深度解析 CAN 总线:从底层物理层到 SocketCAN 编程实战
CAN总线是工业通信的关键技术,以其高可靠性和实时性广泛应用于自动驾驶、轨道交通等领域。其核心技术包括差分信号传输(物理层)和非破坏性逐位仲裁机制(数据链路层),确保在极端环境下稳定工作。CAN协议支持标准帧(11位ID)和扩展帧(29位ID),并通过严密的错误检测(5种机制)和节点健康管理(TEC/REC计数器)实现自我修复。进阶的CAN FD技术提升了数据传输能力(64字节负载,5Mbps速率)。Linux环境下可通过SocketCAN实现CAN通信模拟。
476 8
|
2月前
|
存储 数据采集 算法
具身智能:零基础入门睿尔曼机械臂(六)——手眼标定代码库详解,从原理到实践
本文详解睿尔曼手眼标定代码库,涵盖眼在手上与眼在手外两种模式的实现原理及核心代码解析。内容包括数据采集、位姿处理、相机标定与手眼矩阵求解全流程,结合OpenCV的Tsai算法实现高精度坐标转换,助力机器人视觉精准抓取应用开发。
315 2
|
2月前
|
运维 持续交付 API
具身智能:零基础入门睿尔曼机械臂(四)—— 夹爪无响应?官方例程踩坑与排错实战
本文复盘睿尔曼机械臂夹爪“指令成功但无动作”的故障,揭示官方例程遗漏末端电压配置与通信协议初始化两大隐患。通过“软件—硬件—通信”三步排查,结合万用表测量与示教器配置,最终解决供电缺失与指令失效问题,为末端执行器部署提供可落地的实战方案。
213 6
|
2月前
|
Linux 开发工具 Python
具身智能:零基础入门睿尔曼机械臂(三)——夹爪抓取与释放控制全解析
本文详解睿尔曼第三代机械臂电动夹爪的Python SDK控制方法,聚焦`set_gripper_pick_on`与`set_gripper_release`核心函数,拆解速度、力度、阻塞等参数含义,结合“运动+抓取+释放”完整流程代码,手把手实现夹爪抓放实操,助力零基础用户快速掌握从代码到动作的全流程控制。
264 13
|
2月前
|
数据采集 Web App开发 安全
爬虫专栏:破解网站检测selenium反爬——“当前环境正在被调试“”
本文记录了一次Selenium爬虫被Gitee安全验证拦截的排查经历。爬虫运行一周后突然失效,频繁触发“安全验证”弹窗,尝试隐藏webdriver特征、更换IP、模拟人工操作等均无效。最终发现:手动访问Gitee完成验证后,环境风险标记解除,爬虫自动恢复正常。表明反爬机制针对的是“访问环境”而非工具本身,人工验证可快速解锁,为同类问题提供简洁高效的解决思路。
298 4
|
6月前
|
算法 安全 搜索推荐
算法备案办官方流程
企业办理算法备案需登录备案系统,填写主体及算法信息并提交相关材料。流程包括注册备案、算法信息填报、产品信息提交、审核与公示等环节。企业需提前准备营业执照、身份证明等文件,确保资质真实有效。审核分为主体审核、一审和二审,通过后进入国家网信办公示,公示无异议即获备案号。整个流程约需1个月左右,具体以官方要求为准。