在行空板上部署ChatGLM:本地大模型与边缘AI的实践指南

发布时间:2026/7/28 5:04:33
在行空板上部署ChatGLM:本地大模型与边缘AI的实践指南 1. 项目缘起当开源硬件遇上本地大模型最近在捣鼓行空板这玩意儿真是个宝藏。它本质上是一块集成了高性能处理器、Wi-Fi/蓝牙、触摸屏和各种传感器接口的Python编程单板计算机特别适合用来做物联网和AI边缘计算的原型开发。玩了一阵子基础项目后我就在想能不能用它跑点更“酷”的东西比如现在火得一塌糊涂的大语言模型。直接把ChatGPT这类云端API接上去虽然简单但总觉得少了点“硬核”的乐趣而且网络延迟、隐私问题、API调用成本都是现实考量。于是我的目光转向了可以在本地部署的、经过压缩和优化的开源大模型。ChatGLM系列特别是其量化后的版本以其相对优秀的性能和适中的资源占用进入了我的视野。这个项目的核心想法也就诞生了在一块行空板上部署一个轻量级的ChatGLM模型并利用其触摸屏和Python生态打造一个可以离线运行、支持多角色设定的交互式聊天终端。这不仅仅是一个“玩具”。想象一下它可以是一个放在桌头的智能助手一个为孩子设计的离线故事伙伴或者一个集成到智能家居中、通过语音和触摸进行自然交互的控制中心。最关键的是整个过程从模型部署、接口封装到交互界面开发完全在行空板这一块板子上完成实现了从云端到边缘的“着陆”这对于理解大模型在资源受限环境下的应用具有非常实际的参考意义。2. 核心组件选型与环境搭建要实现这个想法我们需要在行空板上搭建一个完整的软件栈。这个栈可以分为三层硬件层行空板本身、AI推理层大模型运行环境、应用交互层用户界面和逻辑。每一层的选型都直接决定了项目的可行性和最终体验。2.1 硬件基石行空板配置与能力评估我使用的是行空板UNIHIKER其核心是一颗四核ARM Cortex-A55处理器主频1.5GHz内存1GB存储8GB eMMC。这个配置对于运行完整的、未经优化的百亿参数大模型来说是远远不够的。但是我们的目标不是运行原版ChatGLM-6B而是其经过量化后的版本。量化是一种模型压缩技术通过降低模型中权重和激活值的数值精度例如从32位浮点数降到8位整数甚至4位整数可以大幅减少模型的内存占用和计算量同时性能损失在可接受范围内。行空板运行基于Debian的定制Linux系统预装了Python 3.9和丰富的库这为我们提供了绝佳的起点。我们需要评估的关键点是1GB内存能否装下量化后的模型及其运行时环境经过查询和测试ChatGLM2-6B的INT4量化版本模型文件大小约为3-4GB但运行时通过分片加载和内存映射技术实际常驻内存可以控制在1GB以内这为在行空板上运行提供了理论可能。存储方面8GB eMMC在安装系统、Python环境和模型文件后略显紧张但通过清理和优化是足够的。2.2 模型选择为什么是ChatGLM及其量化版本在众多开源中文大模型中我选择ChatGLM2-6B或ChatGLM3-6B作为基础主要基于以下几点考虑优秀的双语能力ChatGLM在中文理解和生成上表现突出同时具备不错的英文能力适合多场景。完整的开源生态其开源协议友好提供了完整的模型权重、推理代码和详细的部署文档。社区活跃遇到问题容易找到解决方案。丰富的量化版本官方和社区提供了多种量化等级的模型如INT8, INT4并有配套的推理优化方案这对边缘设备至关重要。对话格式友好ChatGLM原生支持多轮对话并定义了清晰的角色[gMASK]、|user|、|assistant|等便于我们实现多角色交互逻辑。对于行空板INT4量化版本是性价比最高的选择。我最终选择了由社区成员使用AutoGPTQ或llama.cpp等工具量化后的ChatGLM2-6B-INT4模型。AutoGPTQ量化后的模型可以直接使用transformers库加载与Hugging Face生态结合紧密而llama.cpp量化后的模型通常是GGUF格式则通过其C推理后端效率可能更高内存管理更精细。考虑到行空板是ARM架构直接使用transformers加载PyTorch模型可能面临一些ARM兼容的底层库问题而llama.cpp对ARM的支持相对成熟稳定因此我优先尝试了GGUF格式的模型。2.3 软件栈搭建从系统依赖到Python环境行空板开机后首先需要通过SSH或者其自带的Web终端Jupyter Lab进行系统级操作。第一步系统更新与依赖安装在终端中我们需要安装一些必要的系统库特别是那些与加速计算和模型加载相关的。sudo apt-get update sudo apt-get upgrade -y # 安装基础编译工具和依赖 sudo apt-get install -y build-essential cmake git wget # 安装Python开发包 sudo apt-get install -y python3-dev python3-pip python3-venv第二步创建独立的Python虚拟环境为了避免与行空板自带的系统Python环境产生冲突强烈建议创建一个虚拟环境。python3 -m venv chatglm_env source chatglm_env/bin/activate激活虚拟环境后终端的提示符前会出现(chatglm_env)字样。第三步安装核心Python包这里的选择取决于我们使用的推理后端。如果使用transformersAutoGPTQ方案需要安装pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu pip install transformers accelerate sentencepiece auto-gptq注意PyTorch需要安装ARM兼容的版本上述命令安装的是CPU版本。对于行空板我们没有独立的GPU所以CPU版本是唯一选择。如果决定使用llama.cpp方案则步骤有所不同。我们需要先编译llama.cppgit clone https://github.com/ggerganov/llama.cpp cd llama.cpp make -j4编译成功后会生成一个main可执行文件。然后安装其Python绑定pip install llama-cpp-pythonllama-cpp-python这个包提供了在Python中调用llama.cpp的接口它底层是C效率很高。第四步下载量化模型从Hugging Face Model Hub或可靠的社区仓库下载ChatGLM2-6B的INT4量化模型文件GGUF格式或GPTQ格式。例如一个GGUF格式的模型文件可能叫chatglm2-6b-q4_0.gguf。将其通过SCP命令或U盘拷贝到行空板的某个目录下比如/home/pi/models/。注意首次加载模型时llama.cpp或transformers可能会对模型进行转换或缓存这个过程可能需要几分钟并且会占用额外的临时存储空间。确保行空板有足够的剩余空间至少2-3GB。3. 模型部署与推理引擎封装环境准备好后最核心的一步就是让模型在行空板上“跑起来”。这一部分我们将深入两种部署方案的细节并解释我最终的选择。3.1 方案A使用Transformers与AutoGPTQ这个方案最“正统”与Hugging Face生态无缝衔接。其核心思想是使用AutoGPTQ库自动识别并加载GPTQ格式的量化模型。创建一个Python脚本load_model_gptq.pyfrom transformers import AutoTokenizer, AutoModelForCausalLM, TextStreamer import torch model_name_or_path /home/pi/models/chatglm2-6b-int4-gptq # 加载分词器 tokenizer AutoTokenizer.from_pretrained(model_name_or_path, trust_remote_codeTrue) # 加载量化模型 device_mapcpu 指定使用CPU model AutoModelForCausalLM.from_pretrained( model_name_or_path, torch_dtypetorch.float16, # 即使量化部分计算仍需浮点 device_mapcpu, trust_remote_codeTrue ) model.eval() # 设置为评估模式 def generate_response(text): inputs tokenizer(text, return_tensorspt) # 将输入数据也放到CPU上 inputs {k: v.cpu() for k, v in inputs.items()} with torch.no_grad(): outputs model.generate(**inputs, max_length512, temperature0.7) response tokenizer.decode(outputs[0], skip_special_tokensTrue) return response # 测试 if __name__ __main__: prompt 你好请介绍一下你自己。 print(generate_response(prompt))遇到的问题与挑战内存压力即使加载INT4量化模型在1GB内存的行空板上transformers加载过程仍极易触发OOM内存溢出。这是因为transformers在加载时倾向于将模型权重尽可能放入连续内存。加载速度首次加载非常缓慢可能需要5-10分钟。推理速度纯CPU推理生成一段100字左右的回复可能需要30秒到1分钟交互体验不佳。3.2 方案B使用Llama.cpp及其Python绑定鉴于方案A的挑战我转向了llama.cpp方案。llama.cpp是一个用C编写的高效推理引擎专为在消费级硬件上运行LLM设计其对内存的精细控制和ARM平台的优化做得更好。首先确保你已经按照上一节编译好了llama.cpp。然后使用其Python绑定创建脚本load_model_gguf.pyfrom llama_cpp import Llama # 指定模型路径 model_path /home/pi/models/chatglm2-6b-q4_0.gguf # 创建Llama对象 n_ctx是上下文长度 n_threads是线程数设为4核 llm Llama( model_pathmodel_path, n_ctx2048, # 上下文长度影响记忆能力 n_threads4, # 使用所有CPU核心 n_gpu_layers0, # 行空板无GPU设为0 verboseFalse # 关闭详细日志 ) def generate_response_llama(prompt, max_tokens256): # 构建符合ChatGLM格式的提示词。注意不同模型格式不同需要适配。 # ChatGLM的对话格式通常为 “[Round 1]\n\n问{用户输入}\n\n答” formatted_prompt f[Round 1]\n\n问{prompt}\n\n答 output llm( formatted_prompt, max_tokensmax_tokens, stop[[Round, \n\n问], # 停止词防止生成无限循环的对话轮次 echoFalse, # 不返回输入的提示词 temperature0.7, top_p0.9 ) return output[choices][0][text].strip() if __name__ __main__: test_prompt 你好请介绍一下你自己。 response generate_response_llama(test_prompt) print(f用户: {test_prompt}) print(fAI: {response})为什么最终选择方案B内存占用显著降低llama.cpp在加载GGUF模型时采用内存映射mmap技术模型文件并非全部读入RAM而是按需加载页面极大缓解了内存压力。实测中加载同一个INT4模型llama.cpp的常驻内存比transformers方案少200-300MB。推理速度更快由于其纯C实现和针对ARM架构的优化如使用ARM NEON指令集llama.cpp的token生成速度tokens/s比PyTorch CPU后端快50%以上。启动速度快加载模型几乎在10-20秒内完成体验提升巨大。社区支持针对行空板这类ARM设备llama.cpp的社区资源和成功案例更多。实操心得使用llama.cpp时n_ctx参数不要设置得过大如4096这会线性增加内存开销。2048对于大多数对话场景已经足够。另外stop参数对于控制生成格式至关重要需要根据模型具体的对话模板进行精心设置否则AI可能会自己不断模拟多轮对话。4. 多角色交互逻辑的设计与实现一个简单的问答机太无趣了。我们的目标是“多角色交互式”。这意味着我们需要为AI赋予不同的“人格”或“角色”并在交互中让用户可以选择或切换。例如角色可以是“幽默的助手”、“严谨的教授”、“亲切的朋友”或者“某个历史人物”。4.1 角色系统设计角色本质上是一个系统提示词System Prompt模板。我们为每个角色预定义一个描述其性格、说话风格和知识背景的文本。# roles.py ROLES_CONFIG { default: { name: 通用助手, system_prompt: 你是一个乐于助人、知识渊博的AI助手。请用中文回答用户的问题回答应清晰、准确、友好。 }, professor: { name: 博学教授, system_prompt: 你是一位严谨、博学的老教授说话喜欢引经据典逻辑严密偶尔会带一点学究气。回答问题时请先梳理逻辑框架再给出详细解释。 }, friend: { name: 贴心好友, system_prompt: 你是一个善解人意、活泼开朗的好朋友。说话方式亲切、自然充满共情力。可以用一些网络用语和表情符号在心里想象但不要过度。重点是倾听和给予支持性的回应。 }, storyteller: { name: 故事大王, system_prompt: 你是一个想象力丰富、讲述能力极强的故事家。无论用户提出什么话题你都能将其编织成一个生动有趣、情节曲折的小故事。故事要有开头、发展、高潮和结尾。 } }4.2 对话历史管理为了实现连贯的多轮对话我们必须维护一个对话历史列表。每次新的用户输入都不是孤立的而是要和之前的对话历史一起构成完整的上下文送给模型。# conversation.py class ConversationManager: def __init__(self, system_prompt): self.history [] if system_prompt: # 将系统提示作为第一条历史记录具体格式需适配模型 self.history.append({role: system, content: system_prompt}) def add_user_message(self, message): self.history.append({role: user, content: message}) def add_assistant_message(self, message): self.history.append({role: assistant, content: message}) def get_prompt_for_model(self): 将历史记录转换为模型能理解的单一提示字符串。 # 这是最关键也是最容易出错的部分格式必须完全匹配模型训练时的格式。 # 以ChatGLM为例其多轮对话格式可能类似 # [Round 1] # 问{用户消息1} # 答{助手回复1} # [Round 2] # 问{用户消息2} # 答 prompt_text round_num 1 # 假设self.history是交替的user/assistant消息忽略system角色已融入 for i in range(0, len(self.history), 2): if i1 len(self.history): user_msg self.history[i][content] assistant_msg self.history[i1][content] prompt_text f[Round {round_num}]\n\n问{user_msg}\n\n答{assistant_msg}\n\n round_num 1 # 添加当前最新的用户问题已在add_user_message中添加 # 最后不跟“答”由模型生成 # 注意这里需要根据实际history结构调整。更稳健的做法是单独管理“未回答的用户消息”。 return prompt_text.rstrip() # 去除末尾多余换行 def clear_history(self): self.history []4.3 整合角色与对话管理现在我们将模型、角色系统和对话管理器组合起来。# chat_engine.py from llama_cpp import Llama from conversation import ConversationManager from roles import ROLES_CONFIG class ChatEngine: def __init__(self, model_path): self.llm Llama(model_pathmodel_path, n_ctx2048, n_threads4, n_gpu_layers0) self.current_role default self.conversation None self.switch_role(default) # 初始化默认角色对话 def switch_role(self, role_key): 切换到指定角色 if role_key not in ROLES_CONFIG: print(f角色 {role_key} 不存在使用默认角色。) role_key default role_info ROLES_CONFIG[role_key] self.current_role role_key # 创建新的对话管理器并注入系统提示 # 注意对于ChatGLM系统提示可能需要以特殊方式融入而非作为单独一轮。 # 一种常见做法是将系统提示作为第一条用户消息的一部分或者放在历史记录的开头。 # 这里我们采用一种简单方式在每轮对话的实际用户输入前隐式地加上角色设定。 self.conversation ConversationManager() # 我们不在history中显式添加system而是在构建最终prompt时处理。 self._system_prompt role_info[system_prompt] print(f已切换角色为: {role_info[name]}) def _build_full_prompt(self, user_input): 构建包含角色设定和对话历史的完整提示 # 方法1将角色设定作为历史的一部分模拟一轮对话 base_prompt f{self._system_prompt}\n\n # 添加上下文历史多轮 history_prompt self.conversation.get_prompt_for_model() if history_prompt: base_prompt history_prompt \n\n # 加上当前用户输入 current_round_prompt f[Round {len(self.conversation.history)//2 1}]\n\n问{user_input}\n\n答 full_prompt base_prompt current_round_prompt return full_prompt def chat(self, user_input): 处理用户输入返回AI回复 # 将用户输入添加到对话历史 self.conversation.add_user_message(user_input) # 构建完整提示 full_prompt self._build_full_prompt(user_input) # 调用模型生成 output self.llm( full_prompt, max_tokens512, stop[[Round, \n\n问], # 停止词防止生成新轮次 echoFalse, temperature0.8, # 可稍高一点让角色扮演更有趣 top_p0.95 ) response output[choices][0][text].strip() # 将AI回复添加到对话历史 self.conversation.add_assistant_message(response) return response def clear_chat(self): 清空当前对话历史 self.conversation.clear_history() print(对话历史已清空。)关键细节与避坑角色系统提示词的融合方式是成败关键。直接拼接在历史前面模型有时会“忘记”或弱化这个设定。更高级的做法是使用“指令微调”模型的对话格式将system作为一个独立的角色信息传递给模型。对于ChatGLM可能需要研究其chat接口的调用方式如果使用原生transformers它内部封装了格式处理。在我们的llama.cpp方案中需要手动模拟这种格式。多测试不同角色下的回复调整提示词模板直到角色特征稳定出现。5. 行空板图形界面开发与交互优化有了强大的后端引擎我们需要一个友好的前端界面。行空板配备了2.8英寸的触摸屏使用Python的unihiker库基于tkinter封装可以轻松开发图形界面。5.1 界面布局设计我们的界面需要包含以下几个区域角色选择区一排按钮用于切换不同角色。对话显示区一个可滚动的文本框用于显示完整的对话历史。输入区一个文本输入框和一个发送按钮。控制区清空对话、退出等按钮。# gui_main.py import tkinter as tk from tkinter import scrolledtext, font from chat_engine import ChatEngine from roles import ROLES_CONFIG import threading class ChatGUI: def __init__(self, root, model_path): self.root root self.root.title(行空板 ChatGLM 多角色聊天机) self.root.geometry(480x320) # 适配行空板屏幕 # 初始化聊天引擎 self.engine ChatEngine(model_path) # 设置字体 self.text_font font.Font(familyHelvetica, size10) self.button_font font.Font(familyHelvetica, size9) self.setup_ui() def setup_ui(self): # 顶部角色选择区域 role_frame tk.Frame(self.root) role_frame.pack(sidetk.TOP, filltk.X, padx5, pady5) tk.Label(role_frame, text角色:, fontself.button_font).pack(sidetk.LEFT) for role_key, role_info in ROLES_CONFIG.items(): btn tk.Button( role_frame, textrole_info[name], fontself.button_font, commandlambda rkrole_key: self.switch_role(rk) ) btn.pack(sidetk.LEFT, padx2) # 中间对话显示区域 self.chat_display scrolledtext.ScrolledText( self.root, wraptk.WORD, fontself.text_font, statedisabled, height12 ) self.chat_display.pack(sidetk.TOP, filltk.BOTH, expandTrue, padx5, pady5) # 底部输入和控制区域 bottom_frame tk.Frame(self.root) bottom_frame.pack(sidetk.BOTTOM, filltk.X, padx5, pady5) self.user_input tk.Entry(bottom_frame, fontself.text_font) self.user_input.pack(sidetk.LEFT, filltk.X, expandTrue, padx(0, 5)) self.user_input.bind(Return, lambda event: self.send_message()) # 绑定回车键 send_btn tk.Button(bottom_frame, text发送, fontself.button_font, commandself.send_message) send_btn.pack(sidetk.LEFT, padx(0, 5)) clear_btn tk.Button(bottom_frame, text清空, fontself.button_font, commandself.clear_chat) clear_btn.pack(sidetk.LEFT, padx(0, 5)) quit_btn tk.Button(bottom_frame, text退出, fontself.button_font, commandself.root.quit) quit_btn.pack(sidetk.LEFT) # 初始显示欢迎信息 self.append_to_display(系统, f欢迎使用多角色聊天机当前角色{ROLES_CONFIG[self.engine.current_role][name]}。请开始对话吧) def append_to_display(self, speaker, message): 将消息添加到对话显示框 self.chat_display.config(statenormal) self.chat_display.insert(tk.END, f{speaker}: {message}\n\n) self.chat_display.see(tk.END) # 自动滚动到底部 self.chat_display.config(statedisabled) def send_message(self): 处理发送消息 user_text self.user_input.get().strip() if not user_text: return # 显示用户消息 self.append_to_display(你, user_text) self.user_input.delete(0, tk.END) # 清空输入框 # 在新线程中调用模型生成避免界面卡死 threading.Thread(targetself.generate_response, args(user_text,), daemonTrue).start() def generate_response(self, user_text): 调用引擎生成回复在子线程中运行 # 在界面上显示“思考中...”提示 self.root.after(0, self.append_to_display, AI, 思考中...) try: response self.engine.chat(user_text) # 在主线程中更新UI self.root.after(0, self.update_display_with_response, response) except Exception as e: self.root.after(0, self.append_to_display, 系统, f生成回复时出错: {e}) def update_display_with_response(self, response): 用AI的真实回复替换‘思考中...’ # 获取当前最后一行即“思考中...” self.chat_display.config(statenormal) # 这里逻辑稍微复杂简单实现先删除最后一行再插入新回复 # 更优雅的做法是记录“思考中...”的位置索引。这里为简化我们采用一个变通方法 # 不显示“思考中...”而是直接显示结果。或者在生成前禁用输入生成后恢复。 # 我们修改一下不显示“思考中...”而是在按钮或状态栏显示“生成中”。 # 为了简单我们去掉“思考中...”的显示直接显示结果。 self.chat_display.insert(tk.END, fAI: {response}\n\n) self.chat_display.see(tk.END) self.chat_display.config(statedisabled) def switch_role(self, role_key): 切换角色 old_role_name ROLES_CONFIG[self.engine.current_role][name] self.engine.switch_role(role_key) new_role_name ROLES_CONFIG[role_key][name] self.append_to_display(系统, f角色已从「{old_role_name}」切换为「{new_role_name}」。对话历史已清空。) def clear_chat(self): 清空对话 self.engine.clear_chat() self.chat_display.config(statenormal) self.chat_display.delete(1.0, tk.END) self.append_to_display(系统, 对话历史已清空。) if __name__ __main__: root tk.Tk() # 指定你的模型路径 MODEL_PATH /home/pi/models/chatglm2-6b-q4_0.gguf app ChatGUI(root, MODEL_PATH) root.mainloop()5.2 性能优化与用户体验提升在资源受限的行空板上直接运行上述代码可能会遇到界面卡顿因为模型推理是CPU密集型任务会阻塞主线程。我们已使用threading将推理放到后台线程。但还有更多优化点流式输出Streaming上述代码是等模型完全生成完所有token后再显示。更好的体验是像ChatGPT那样逐字输出。llama.cpp的Python绑定支持通过回调函数实现流式输出。这需要修改chat_engine.py中的生成逻辑并设计UI实时更新机制。对话历史长度管理随着对话轮次增加上下文会越来越长导致生成速度变慢甚至超出模型的最大上下文长度n_ctx。需要实现一个滑动窗口或摘要机制只保留最近N轮对话或者将久远的历史总结成一段话。输入预处理与后处理可以增加对用户输入的简单检查如过滤敏感词、过长输入截断以及对AI输出的格式化如调整换行、处理可能的乱码。离线语音集成进阶行空板有麦克风和扬声器接口可以集成VOSK、Whisper.cpp等离线语音识别/合成库实现纯语音交互让设备更像一个独立的智能硬件。6. 项目集成、部署与实测反馈将各个模块整合后我们最终的项目目录结构大致如下/chatglm_on_unihiker/ ├── models/ │ └── chatglm2-6b-q4_0.gguf # 量化模型文件 ├── roles.py # 角色配置 ├── conversation.py # 对话历史管理 ├── chat_engine.py # 核心聊天引擎 ├── gui_main.py # 主界面程序 └── requirements.txt # Python依赖列表requirements.txt内容llama-cpp-python0.2.23在行空板上进入项目目录激活虚拟环境安装依赖然后直接运行GUI主程序cd /home/pi/chatglm_on_unihiker source chatglm_env/bin/activate pip install -r requirements.txt # 如果之前没装的话 python gui_main.py实测体验与反馈启动时间首次运行加载模型约15-25秒后续启动模型已加载几乎瞬间。内存占用通过htop命令观察常驻内存约850MB交换分区swap使用约200MB运行稳定。响应速度生成一段50-100字的回复耗时约10-20秒。对于本地CPU推理来说这个速度是可以接受的交互体验。角色效果切换角色后AI的回复风格确实能发生明显变化。“教授”角色会使用更复杂的句式和术语“朋友”角色则更口语化、带有更多情感词汇。这证明了系统提示词的有效性。发热与稳定性持续运行半小时后行空板CPU温度在60-70度左右属于正常范围。未出现死机或崩溃。遇到的典型问题与解决生成内容重复或循环这通常是由于stop参数设置不当或者模型在生成时陷入了某种重复模式。调整temperature降低和top_p降低并仔细检查stop词是否准确匹配了可能引发循环的文本模式。回复不符合角色设定说明系统提示词不够强或格式不对。尝试用更强烈的指令性语言例如“你必须始终以一位博学教授的身份和口吻回答问题这是最重要的规则。”并将提示词放在更靠近用户输入的位置。内存不足导致程序被终止检查是否有其他程序占用内存。可以尝试为行空板增加ZRAM交换空间或者使用更激进的量化模型如尝试INT3甚至二值化模型但效果会下降。这个项目成功地将一个数GB的大语言模型“塞进”了一块仅有1GB内存的单板计算机并实现了带图形界面的多角色交互。它不仅仅是一个演示更是一个完整的边缘AI应用原型。你可以基于此扩展更多功能比如连接传感器让AI解读环境数据、控制执行器实现语音控制、或者将对话记录保存到数据库进行分析。行空板提供的丰富硬件接口和Python的易用性为这类AIoT应用的创新打开了广阔的空间。