回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

光影之约·AI赋能:基于行空板K10 + 二哈识图2的手势互动启动装置 简单

头像 火星涛 2025.12.19 9 0

摘要:拒绝枯燥的按钮启动!本项目利用二哈识图2捕捉手势(点赞、Yeah、握拳),通过行空板K10与MQTT联动大屏粒子特效。视觉代码完全由 Google AI Studio 生成,是一次“用AI代码展示AI视觉”的硬核尝试。

 

一、 项目简介

 

🔖 项目概述

本项目专为校园科技节或展会设计了一套“沉浸式”的启动仪式装置。区别于传统的“按按钮”或“剪彩”,本装置利用 二哈识图2 进行边缘 AI 视觉识别,捕捉参与者的手势动作(大拇指、Yeah、握拳)。通过 行空板 K10 进行逻辑处理与物联网通讯(MQTT),联动 PC 端的大屏粒子特效(可延申拓展现场物理灯光控制)。

 

✨ 创意亮点

值得一提的是,本项目的上位机视觉代码采用了 AIGC(Google AI Studio) 辅助生成,实现了“用 AI 生成的代码来展示 AI 视觉的魅力”,构建了从感知到生成的全链路 AI 体验。

 

二、 硬件与软件清单

 

2.1 硬件清单

 

设备名称数量说明
行空板 K101核心主控,带彩屏,负责逻辑与网络
二哈识图 21AI视觉传感器,负责手势识别
PC 电脑1运行 SIOT 服务器及大屏展示网页
连接线1I2C 线 (PH2.0)

 

2.2 软件工具

 

💠Mind+ V2.0:编写 K10 控制程序

💠SIOT v2:局域网 MQTT 服务器
💠Google AI Studio:生成 p5.js 视觉特效代码

 

三、 制作步骤

步骤1 二哈识图2 手势训练

1. 二哈识图2 固件更新至 V1.2

最新版本HUSKYLENS 2固件:HUSKYLENSV2v1.2[251208]

固件更新教程:二哈识图 2 固件更新教程

2. 手势识别训练

我们将二哈识图2设置为“手势识别”模式,长按学习键分别录制三种手势:

1.png

ID 1:大拇指(点赞) -> 对应大屏“能量汇聚”

ID 2:Yeah(剪刀手) -> 对应大屏“情感注入”
ID 3:握拳(力量) -> 对应大屏“最终启动”

二哈试图2 使用教程 - 手势识别

步骤2 SIOT 服务器搭建

SIoT是一个针对学校场景的开源免费的MQTT服务器软件, 可一键创建本地物联网服务器。


SIOT包含一个MQTT服务器和一个数据库以及一个网页界面,MQTT服务器负责数据的转发以及判断是否存储,网页界面可以方便的查看数据或者简单的测试程序。


SIoT V2是新的升级,性能提升,可以支持更快的速度,同时使用QOS区分了快速数据以及存入数据的数据以应对不同的使用场景,网页界面也进行了更新更美观。

1. SIoT软件下载

  • SIoT V2版本 下载链接:

腾讯微云下载(推荐):
链接: https://share.weiyun.com/6SFhgLQj

百度云盘下载:
链接:https://pan.baidu.com/s/17clVjJXWTZh02FteKy3mcA?pwd=mind
提取码:mind

2. 在电脑上运行SIoT

image.png

注意:启动时需要将SIoT添加到允许应用通过防火墙,勾选专用网络和公用网络,否则外部设备可能无法访问。

3. 添加主题

在浏览器输入 127.0.0.1:8080 即可打开网页端口,登录账号依然为siot,密码为dfrobot,打开后可以新建主题(Topic) 。

image.png

Mind+数据可视化面板-SIoT V2下载使用-教程

步骤3 行空板 K10 编程 (Mind+ V2.0)

K10 作为中枢,连接 WiFi 和 MQTT 服务器。当接收到二哈的 ID 信号后,发送对应指令给电脑。
核心逻辑:为了防止误触,我们设计了状态机,必须按照 ID1 -> ID2 -> ID3 的顺序才能触发最终效果。

使用说明

- 上电后自动进入识别流程并显示当前模式
- 若要改为手动识别:按 A 键切换到“手动识别”,按 B 依次推进步骤
- 若要恢复自动识别:再次按 A 键切换回“自动识别”,设备会在 loop() 中继续自动检测并推进

– 重置流程:快速依次按下并释放 A+B

(以下为 Mind+ V2 上传模式核心代码)

代码
/*!
 * MindPlus
 * DFRobot, 行空板 K10
 */
#include <DFRobot_Iot.h>
#include "unihiker_k10.h"
#include "DFRobot_HuskylensV2.h"

// 动态变量
volatile float mind_n_a, mind_n_b, mind_n_c, mind_n_JinDu;
volatile bool autoMode = true;

// 静态常量
const String topics[] = {"siot/launch_event","","","",""};

// 创建对象
HuskylensV2 huskylens;
DFRobot_Iot myIot;
UNIHIKER_K10 k10;
uint8_t screen_dir=2;

void DF_ShiBieShouShiJieGuo();
void DF_LiuChengQiDong();
void DF_ShuaXinPingMuWenZi(String mind_s_string, float mind_n_row);
void onButtonAUnPressed();
void onButtonBUnPressed();
void DF_TextColor(String s, float row, uint32_t color);
void DF_ShowStatus(String s);
void DF_ShowHint(String s);
void DF_ShowProgress(int step, String detail);
void DF_ClearRows(int fromRow, int toRow);
void DF_UpdateModeHint();
void DF_ResetFlow();
void onButtonABPressed();
const uint32_t COL_TITLE = 0xFF66CC;
const uint32_t COL_HINT = 0x66CCFF;
const uint32_t COL_PENDING = 0x999999;
const uint32_t COL_SUCCESS = 0x33DD77;
const uint32_t COL_FINAL = 0xFF6699;
const uint32_t COL_INFO = 0x00AAFF;
const uint32_t COL_WARN = 0xFFCC66;

// 主程序开始
void setup() {
	k10.begin();
	k10.buttonA->setUnPressedCallback(onButtonAUnPressed);
	k10.initScreen(screen_dir);
	k10.creatCanvas();
	k10.buttonB->setUnPressedCallback(onButtonBUnPressed);
    k10.buttonAB->setPressedCallback(onButtonABPressed);
	Wire.begin();
    while (!huskylens.begin(Wire)) {
        delay(100);
    }
	huskylens.switchAlgorithm(ALGORITHM_HAND_RECOGNITION);
    delay(5000);
	DF_TextColor("行空版", 1, COL_TITLE);
	myIot.wifiConnect("BCM-RJ", "bcm123456");
	while (!myIot.wifiStatus()) {}
	DF_ShowStatus("WiFi已连接");
	myIot.init("192.168.1.14","siot","2627086117766205","dfrobot", topics, 1883);
	myIot.connect();
	while (!myIot.connected()) {}
	DF_ShowStatus("MQTT已连接");
	DF_UpdateModeHint();
	DF_LiuChengQiDong();
}
void loop() {
	if (autoMode) {
		if (mind_n_JinDu==1) {
			DF_ShiBieShouShiJieGuo();
			if (mind_n_a>2) {
				DF_TextColor((String("识别到:") + String(RET_ITEM_STR(huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 1), Result, name))), 5, COL_SUCCESS);
				myIot.publish(topic_0, "step1");
				DF_ShowProgress(1, "完成");
				mind_n_b = 0;
				mind_n_JinDu = 2;
			}
		} else if (mind_n_JinDu==2) {
			DF_ShiBieShouShiJieGuo();
			if (mind_n_b>2) {
				DF_TextColor((String("识别到:") + String(RET_ITEM_STR(huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 2), Result, name))), 7, COL_SUCCESS);
				myIot.publish(topic_0, "step2");
				DF_ShowProgress(2, "完成");
				mind_n_c = 0;
				mind_n_JinDu = 3;
			}
		} else if (mind_n_JinDu==3) {
			DF_ShiBieShouShiJieGuo();
			if (mind_n_c>2) {
				DF_TextColor((String("识别到:") + String(RET_ITEM_STR(huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 3), Result, name))), 9, COL_SUCCESS);
				myIot.publish(topic_0, "step3");
				DF_TextColor("科技节启动", 11, COL_FINAL);
				mind_n_JinDu = 4;
			}
		}
	}
}

// 自定义函数
void DF_ShiBieShouShiJieGuo() {
	mind_n_a = 0;
	mind_n_b = 0;
	mind_n_c = 0;
	for (int index = 0; index < 3; index++) {
		huskylens.getResult(ALGORITHM_HAND_RECOGNITION);
		if (huskylens.available(ALGORITHM_HAND_RECOGNITION)) {
			if ((huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 1) != NULL)) {
				mind_n_a += 1;
			}
			else if ((huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 2) != NULL)) {
				mind_n_b += 1;
			}
			else if ((huskylens.getCachedResultByID(ALGORITHM_HAND_RECOGNITION, 3) != NULL)) {
				mind_n_c += 1;
			}
			else {
			}
		}
		delay(100);
	}
}
void DF_LiuChengQiDong() {
	myIot.publish(topic_0, "step0");
	DF_ClearRows(3,11);
	DF_TextColor("准备启动流程", 3, COL_INFO);
	DF_TextColor("", 4, COL_PENDING);
	DF_TextColor("手势1 待识别", 5, COL_PENDING);
	DF_TextColor("", 6, COL_PENDING);
	DF_TextColor("手势2 待识别", 7, COL_PENDING);
	DF_TextColor("", 8, COL_PENDING);
	DF_TextColor("手势3 待识别", 9, COL_PENDING);
	DF_TextColor("", 10, COL_PENDING);
	mind_n_a = 0;
	mind_n_b = 0;
	mind_n_c = 0;
	mind_n_JinDu = 1;
	if (!autoMode) {
		DF_ShowProgress(1, "待手动激活");
		DF_ShowProgress(2, "待手动激活");
		DF_ShowProgress(3, "待手动激活");
	}
}
void DF_ShuaXinPingMuWenZi(String mind_s_string, float mind_n_row) {
	k10.canvas->canvasClear(mind_n_row);
	k10.canvas->canvasText(mind_s_string, mind_n_row, 0x0000FF);
	k10.canvas->updateCanvas();
}

void DF_TextColor(String s, float row, uint32_t color) {
	k10.canvas->canvasClear(row);
	k10.canvas->canvasText(s, row, color);
	k10.canvas->updateCanvas();
}

void DF_ShowStatus(String s) {
	DF_TextColor(s, 1, COL_INFO);
}

void DF_ShowHint(String s) {
	DF_TextColor(s, 3, COL_HINT);
}

void DF_ShowProgress(int step, String detail) {
	if (step==1) DF_TextColor((String("手势1 ") + detail), 5, COL_SUCCESS);
	if (step==2) DF_TextColor((String("手势2 ") + detail), 7, COL_SUCCESS);
	if (step==3) DF_TextColor((String("手势3 ") + detail), 9, COL_SUCCESS);
}

void DF_ClearRows(int fromRow, int toRow) {
	for (int r=fromRow; r<=toRow; r++) {
		k10.canvas->canvasClear(r);
	}
	k10.canvas->updateCanvas();
}
void DF_UpdateModeHint() {
	if (autoMode) {
		DF_TextColor("模式:自动识别  按A键切换  A+B重置", 3, COL_HINT);
	} else {
		DF_TextColor("模式:手动识别  按A键切换  A+B重置", 3, COL_HINT);
	}
}

// 事件回调函数
void onButtonAUnPressed() {
	autoMode = !autoMode;
	DF_UpdateModeHint();
}
void onButtonBUnPressed() {
	if ((mind_n_JinDu==1)) {
		DF_TextColor((String("手动激活:") + String("1")), 5, COL_WARN);
		myIot.publish(topic_0, "step1");
		DF_ShowProgress(1, "已手动激活");
		mind_n_JinDu = 2;
	}
	else if ((mind_n_JinDu==2)) {
		DF_TextColor((String("手动激活:") + String("2")), 7, COL_WARN);
		myIot.publish(topic_0, "step2");
		DF_ShowProgress(2, "已手动激活");
		mind_n_JinDu = 3;
	}
	else if ((mind_n_JinDu==3)) {
		DF_TextColor((String("手动激活:") + String("3")), 9, COL_WARN);
		myIot.publish(topic_0, "step3");
		DF_TextColor("科技节启动", 11, COL_FINAL);
	}
	else {
		DF_TextColor("按A+B键,重置", 11, COL_HINT);
	}
}
void DF_ResetFlow() {
	myIot.publish(topic_0, "reset");
	mind_n_a = 0;
	mind_n_b = 0;
	mind_n_c = 0;
	mind_n_JinDu = 1;
	autoMode = true;
	DF_UpdateModeHint();
	DF_LiuChengQiDong();
}
void onButtonABPressed() {
	DF_ResetFlow();
}

步骤4 生成大屏视觉程序 (Google AI Studio)

这是本项目的最有趣的部分。我没有手写网页代码,而是向 Google AI Studio 发送了以下提示词:

Prompt: "角色设定:你是一个精通 Creative Coding 和 IoT 的前端开发专家。
任务:请帮我编写一个单文件的 HTML 网页,用于互动装置展示。
技术栈:
使用 CDN 引入 p5.js 用于绘图。
使用 CDN 引入 mqtt.js 用于连接 SIOT v2 服务器。
功能要求:
网页背景为黑色。
建立 MQTT 连接:
连接地址:ws://127.0.0.1:1888/ws(注意使用 WebSocket 协议)。
Topic:siot/launch_event。
账号密码留空变量让我填写。
屏幕中央显示文字“等待接入...”。
当收到 MQTT 消息时触发分步骤的视觉特效

视觉设计要求:
待机状态:屏幕中心有一个微弱呼吸的白色光圈,周围有少量漂浮粒子。
阶段一 (接收到 "step1"):触发【能量汇聚】特效。从屏幕四周向中心汇聚蓝色的光流粒子,速度较快,形成科技感。
阶段二 (接收到 "step2"):触发【情感注入】特效。在蓝色光流的基础上,增加粉色/暖色的光晕粒子,柔和地扩散并交融。
阶段三 (接收到 "step3"):触发【最终启动】。
粒子先极速收缩成一个点。
然后瞬间爆炸(Boom),满屏五彩粒子四散。
2秒后,粒子慢慢汇聚成文字 "科技节启动" 。

音效设计:

背景音乐 步骤音效 都改为 本地mp3文件

bgm.mp3 - 背景音乐
step1.mp3 - 能量汇聚音效
step2.mp3 - 情感注入音效
step3.mp3 - 启动爆发音效

代码要求:
使用 p5.js 的粒子系统(Particle System)来实现。
动画要流畅,转换要有过渡感。
输出:请直接给出完整的 index.html 代码。"

项目源码:

github仓库地址:dhtbo/iot-launch-interface

AI 生成的代码直接运行效果惊人!

12月18日-1.gif
12月18日(1)-1.gif
12月18日(2)-1.gif
12月18日(3)-1.gif

四、 演示效果 (见视频)

生成项目封面图片 (3).png

初始状态:大屏显示 "等待接入..."。


互动开始:体验者竖起大拇指,屏幕蓝光升腾。


气氛升温:体验者比出 Yeah,屏幕彩带飞舞。


高潮启动:体验者用力握拳,屏幕瞬间炸裂显示 "科技节启动",完成启动仪式。

五、 总结

通过行空板 K10 和二哈识图2,我们用极低的成本实现了一个看似复杂的互动装置。更重要的是,在 Google AI Studio 的帮助下,我们验证了 "AI 工具 + 开源硬件 = 无限创意" 的可能性。希望这个项目能为校园科技节增添一抹亮色!

评论

user-avatar