2025寒假练 - 基于Crowpanel和LLM的个人智能小助手
该项目使用了Crowpanel和大语言模型,实现了个人智能小助手的设计,它的主要功能为:智能问答,工具调用。
标签
CrowPanel
2025寒假一起练
大语言模型
SyntaxError
更新2025-03-13
69

项目介绍

本项目基于Crowpanel开发板实现了一个基于大语言模型的个人智能小助手,借由LVGL搭建前端人机交互界面,以大语言模型如deepseek-r1、qwen2.5为智能基座模型对用户的输入进行回馈,通过function call等功能完成对应需求任务。其中实现主要功能如下:

  1. 用户可以通过LVGL搭建的界面进行人机交互,支持触摸操作,可以通过文本框调用软键盘输入文本,反馈给大语言模型。
  2. 大语言模型支持工具调用功能,可以完成开灯、调整灯光亮度等功能,实现具身智能。
  3. 显示界面可交互,控件都可使用,显示日历,选择大语言模型等功能都可以实现。
  4. 用户可自定义工具与功能,实现个性化定制。

硬件介绍

Elecrow ESP32 4.3英寸显示屏是一款功能强大的HMI触摸屏,具有480*272分辨率的LCD显示屏。使用ESP32-S3-WROOM-1-N4R2模组作为主控处理器,具有双核32位 LX7处理器,集成WiFi和蓝牙无线功能,主频高达240MHz,提供强大的性能和多功能的应用,适用于物联网应用设备等场景。

套件包含一块4.3英寸LCD显示屏和一块驱动板,显示屏采用电阻式触摸技术,并自带电阻式触摸笔,让屏幕的使用更加灵活。此外,板子预留了TF卡槽、多种外设接口、USB接口、喇叭接口、电池接口等,提供了更多的扩展可能。支持Arduino IDE、Espressif IDF、PlatformIO、MicroPython等多种开发环境,并兼容LVGL图形库,让开发者不仅可以定制自己的UI界面,还可以快速轻松地创建有趣的项目,大大缩短了开发周期。

方案框图与设计思路

image.png

如图是整个方案的设计。

  1. 首先在PC机或者其他上位机上部署大语言模型本地推理服务,其中本项目采用Ollama平台部署了4bit量化 Deepseek-R1 8B参数以及Qwen2.5 7B参数的大语言模型作为基座模型进行服务,Ollama安装与介绍参考网:Ollama
  2. 在开发板与PC机通信方面,基于WiFi通信进行,其中PC机器上部署Flask框架,用于接收请求并返回结果。Flask是一个基于Python的轻量级Web应用框架,它以简洁、灵活和易于扩展而广受欢迎。与其他重量级的框架相比,Flask保持其核心功能的精简,让开发者能够根据自身项目的需求灵活地选择和集成所需的功能扩展。这种设计理念不仅降低了开发的复杂性,还提高了应用的可定制性。无论是构建简单的个人博客,还是开发复杂的在线商城,Flask都能为开发者提供一个稳定且高效的开发环境。此外,Flask还支持与各种数据库和前端技术的无缝集成,使得开发者能够轻松地构建出功能丰富、性能卓越的Web应用程序。
  3. 在人机交互方面,基于LVGL进行开发设计了如演示所示的界面,LVGL(Light and Versatile Graphics Library)是一个开源的嵌入式图形用户界面(GUI)库,它专为资源受限的嵌入式系统设计,提供了一套完整的解决方案来创建美观且高效的图形用户界面。本项目界面包含有日历、文本输入框、默认隐藏的键盘、提交按钮、状态显示按钮以及米速表等控件用来支持丰富的交互演示功能。
  4. 基于大语言模型的文本生成技术以及Function Call功能介绍,可以构建出一个拥有决策功能以及行动功能的智能体,从而能够完成对话应答以及具体实操任务的功能。

软件流程图和关键代码介绍

image.png

总体概括一下软件流程就是,用户通过界面进行交互,比如输入文本来要求某些任务的实现,开发板收集要求,通过无线传输给PC的Flask服务器,进而要求大语言模型进行决策回应,获取回应信息后,根据对应指示,进行外设或者界面控件的控制,比如开关灯、控制灯光亮度之类的功能。

  • 其中,LVGL前端展示界面基于Squareline Studio进行设计开发,这个IDE开发起来很方便,拖拖拽拽就完成了设计。

image.png

  • 当然在界面基础上,也要绑定事件才能触发对应功能,比如下面代码就展示了如何绑定键盘输入到空文本框,点击文本框就可以唤出键盘,输入完成后隐藏的功能。
ui_TextArea1 = lv_textarea_create(ui_Screen1);
lv_obj_set_width(ui_TextArea1, 234);
lv_obj_set_height(ui_TextArea1, 54);
lv_obj_set_x(ui_TextArea1, 106);
lv_obj_set_y(ui_TextArea1, -96);
lv_obj_set_align(ui_TextArea1, LV_ALIGN_CENTER);
lv_textarea_set_placeholder_text(ui_TextArea1, "please type here");
// 键盘
ui_Keyboard1 = lv_keyboard_create(ui_Screen1);
lv_obj_set_width(ui_Keyboard1, 472);
lv_obj_set_height(ui_Keyboard1, 117);
lv_obj_set_x(ui_Keyboard1, 1);
lv_obj_set_y(ui_Keyboard1, 76);
lv_obj_set_align(ui_Keyboard1, LV_ALIGN_CENTER);
lv_obj_add_flag(ui_Keyboard1, LV_OBJ_FLAG_HIDDEN);    
// 绑定到一起
lv_keyboard_set_textarea(ui_Keyboard1, ui_TextArea1);
    //绑定事件
lv_obj_add_event_cb(ui_TextArea1, ui_textarea_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(ui_Keyboard1, ui_keyboard_event_cb, LV_EVENT_READY, NULL);  // 用户按下“完成”键

// 具体事件的实现示例
void ui_textarea_event_cb(lv_event_t * e)
{
    lv_obj_t * textarea = lv_event_get_target(e);  // 获取触发事件的对象(文本框)
   
    // 显示键盘
    lv_obj_clear_flag(ui_Keyboard1, LV_OBJ_FLAG_HIDDEN);  // 显示键盘


    // 隐藏 Arc1 和 Spinner1
    lv_obj_add_flag(ui_Arc1, LV_OBJ_FLAG_HIDDEN);
    lv_obj_add_flag(ui_Spinner1, LV_OBJ_FLAG_HIDDEN);
}


// 键盘关闭时隐藏
void ui_keyboard_event_cb(lv_event_t * e)
{
    lv_obj_t * keyboard = lv_event_get_target(e);
    lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN);  // 隐藏键盘


    // 恢复 Arc1 和 Spinner1
    lv_obj_clear_flag(ui_Arc1, LV_OBJ_FLAG_HIDDEN);
    lv_obj_clear_flag(ui_Spinner1, LV_OBJ_FLAG_HIDDEN);
}
  • PC机器上部署的Flask代码示例模版如下
app = Flask(__name__)

@app.route('/receive_data', methods=['POST'])
def receive_data():
global latest_data
# latest_data = request.get_json()
latest_data = request.form.get('value')
print("Received data:", latest_data)

# 使用UDP将数据转发
try:
global sock
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = latest_data
llm_control(sock, message)
return jsonify({"status": "Data received and forwarded"})

except Exception as e:
print(f"Error forwarding data to r1: {e}")
return jsonify({"status": "Data received but forwarding error"}), 500

app.run(host='0.0.0.0', port=5000)
  • 此外,需要用户自定义大模型可以使用的工具,本项目基于langchain库实现,定义模版可参考如下
@tool
def turn_on_lights(flag: str) -> str:
"""Turn on lights in case of darkness or emergency """
print("turn on lights")
mes = "lights:open"
sock.sendto(mes.encode(), (R1_IP, R1_PORT))
sock.close()
# print('-' * 20)
return "Lights on! Enjoy your brightness"

功能展示

image.png

如图所示,这里点击文本框,唤出软键盘进行文本输入,然后点击submit提交给本地LLM进行处理。

image.png

大模型的部分输出如图,此处使用的是deepseek的模型,所以可以看到明显的think部分,并且这里也识别出无需借助外部工具,直接给个笑话的简单文本输出即可。

image.png

同样,可以测试下工具调用的例子,比如这里基于qwen大模型进行演示,用户说太暗了,让大模型看着办吧。

image.png

大模型懂得借助工具来操作对应外设或控件,来操作打开灯光。返回开发板查看,可以发现 lights status已经是激活状态,可见确实操纵了灯光的打开。

image.png

类似地,如视频里所演示的,可以进一步说还是太暗,希望大模型处理下,让房间更亮,大模型就会调用另一个工具来调整brightness的米速表控价,最终效果如下,更多细节特别是模型输出还请参考视频展示。

image.png

心得体会

最后感谢电子森林以及赞助商提供这次机会供我学习,本次学习最大的体悟就是学到了如何基于Squareline Studio对LVGL的界面进行设计,并且能够结合大语言模型完成了一个具身智能的小demo,实在是受益匪浅,再次感谢!



附件下载
代码.zip
团队介绍
个人团队
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号