步骤1 项目背景
在数字化浪潮席卷全球的当下,物联网(IoT)与人工智能(AI)技术的深度融合已成为推动各行业转型升级的核心驱动力,开启了“智能互联”的全新时代。物联网技术实现了设备与设备、设备与人类之间的高效数据交互,而人工智能的计算机视觉、机器学习等核心能力,则赋予了设备自主感知、分析和决策的“智慧大脑”。两者的协同应用,正不断突破传统设备的功能边界,在工业巡检、智能安防、环境监测、教育科研等多个领域展现出广阔的应用前景。
远程控制小车作为一种典型的移动智能终端,其传统形态多依赖单一的远程指令传输,缺乏环境感知和自主判断能力,难以适应复杂场景下的应用需求。例如,在危险环境(如化工园区、火灾现场、核辐射区域)巡检中,传统远程小车无法精准识别障碍物、危险标识等关键信息,易导致操作失误或设备损坏;在智能安防场景中,仅能实现简单的图像传输,无法实时识别异常行为、可疑人员等,难以发挥主动预警作用;在教育科研领域,传统小车也难以直观呈现智能感知与远程控制的协同逻辑,无法满足物联网与AI融合技术的教学实践需求。
随着摄像头硬件成本的降低和图像识别算法的迭代优化,基于AI摄像头的环境感知技术已具备大规模应用的基础。通过AI摄像头实时采集场景图像,结合深度学习算法实现障碍物识别、路径规划、目标追踪等功能,再依托物联网技术构建高效的远程通信链路,可让远程控制小车具备“感知-分析-决策-执行”的全链路智能能力,弥补传统设备短板。
观看二哈识图2和MIND+2的发布会以后很是激动,赶紧下了单,终于拿到手了,把玩了几次之后就束之高阁了。这段时间工作比较忙,一直没顾得上仔细研究一下它的功能。近期抽空也体验了一下利用MIND+2.0软件来训练模型。这次正好借参加本届AI视觉应用创新挑战赛活动的同时来练习一下,于是,我又拿出了买来以后吃灰很久的行空板M10和K10。
本来想做一个手势识别来远程控制小车的例子。但发现MIND+2.0目前手势分类功能尚在开发中,于是想到用图像分类来识别,模型训练后无法部署到二哈识图中,最终我使用目标检测的功能和物联网来实现远程图像识别和控制小车。原本小车上还装了一个自己3D打印的MeArm的4自由度机械臂。但经过测试发现,行空板扩展板上的一节18650的电池好像无法同时满足4个电机驱动和4个舵的控制的供电。最终我从小车上拆除了机械臂。
步骤2 器材准备
步骤3 前期准备
1.使用豆包生成一些便于二哈2识别的交通控制和车辆控制的标志。

2.使用PS修改成自己需要的标志,打印在纸上后,用双面胶贴在卡纸做成可手持的标志牌上。


3.使用摄像头来采集、分类、标注图片,完成后进行模型训练和验证。

在此过程中需要对所采集的图片进行逐一标注。这一过程相对来说比较考验耐心和比较耗时。好在我分的类比较多,为了减少工作量,每一类只采集了20张图片。训练时使用默认参数,训练50轮次,训练完成后就可以导出训练结果了。

4.训练完成后将导出的文件拷入二哈识图2以后进行本地安装。
虽然之前在其它平台也进行过类似这样的图片采集和训练,但操作较为麻烦。使用之后一对比才发现MIND+2.0的这个模型训练工具真是太方便了!必须点赞!训练后的模型识别率和准确率也非常高!
步骤4 程序设计
1.行空板K10程序。K10主要用于接收MQTT的消息来控制小车的各种运动。为了让演示意效果更好一些,我把车轮换成了麦克纳姆轮,这样就可以实现横向左右移动了。

2.行空板M10程序。行空板M10上连接着二哈识图2,在识别成功后同时在行空板M10上显示出识别结果的图片和文字,同时将识别结果发送至MQTT。

步骤5 程序源代码
/*!
* MindPlus
* esp32s3bit
*
*/
#include <DFRobot_Iot.h>
#include "unihiker_k10.h"
#include "DFRobot_UnihikerExpansion.h"
// 函数声明
void DF_ChuShiHua();
void whenTopic0Received0();
void whenTopic0Received1();
void whenTopic0Received2();
void whenTopic0Received3();
void whenTopic0Received4();
void whenTopic0Received5();
void whenTopic0Received6();
void DF_QianJin();
void DF_HouTui();
void DF_ZuoYi();
void DF_YouYi();
void DF_YouZhuan();
void DF_ZuoZhuan();
void DF_XuanZhuan();
// 静态常量
const String topics[5] = {"fjht1XGDg","","","",""};
// 创建对象
UNIHIKER_K10 k10;
uint8_t screen_dir=2;
DFRobot_Iot myIot;
DFRobot_UnihikerExpansion_I2C eunihiker(&Wire) ;
// 主程序开始
void setup() {
k10.begin();
myIot.setMqttCallback(topic_0, "go", whenTopic0Received0);
myIot.setMqttCallback(topic_0, "back", whenTopic0Received1);
myIot.setMqttCallback(topic_0, "left", whenTopic0Received2);
myIot.setMqttCallback(topic_0, "right", whenTopic0Received3);
myIot.setMqttCallback(topic_0, "turnright", whenTopic0Received4);
myIot.setMqttCallback(topic_0, "turnleft", whenTopic0Received5);
myIot.setMqttCallback(topic_0, "turnaround", whenTopic0Received6);
k10.initScreen(screen_dir);
k10.creatCanvas();
while(!eunihiker.begin()){delay(1000);}
eunihiker.setMotorPeriod(eMotor1_2, 255);
eunihiker.setMotorPeriod(eMotor3_4, 255);
DF_ChuShiHua();
}
void loop() {
}
// 自定义函数
void DF_ChuShiHua() {
k10.canvas->canvasClear();
k10.canvas->canvasText("行空板正在连接WIFI.....", 1, 0x0000FF);
k10.canvas->updateCanvas();
myIot.wifiConnect("Xiaomi_8366_601", "Jsy");
while (!myIot.wifiStatus()) {}
k10.canvas->canvasText("WIFI 已连接!", 2, 0x0000FF);
k10.canvas->updateCanvas();
k10.canvas->canvasText("行空板正在连接MQTT...", 3, 0x0000FF);
k10.canvas->updateCanvas();
myIot.init("iot.dfrobot.com.cn","Xx3rhZtNg","26963124818363005","ubqr2ZtNRz",topics,1883);
myIot.connect();
while (!myIot.connected()) {}
k10.canvas->canvasText("MQTT已连接!", 4, 0x0000FF);
k10.canvas->updateCanvas();
}
void DF_QianJin() {
eunihiker.motorRun(eMotor1,255,1);
eunihiker.motorRun(eMotor2,255,0);
eunihiker.motorRun(eMotor3,255,1);
eunihiker.motorRun(eMotor4,255,0);
delay(2000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_HouTui() {
eunihiker.motorRun(eMotor1,255,0);
eunihiker.motorRun(eMotor2,255,1);
eunihiker.motorRun(eMotor3,255,0);
eunihiker.motorRun(eMotor4,255,1);
delay(2000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_ZuoYi() {
eunihiker.motorRun(eMotor1,200,1);
eunihiker.motorRun(eMotor2,200,1);
eunihiker.motorRun(eMotor3,200,0);
eunihiker.motorRun(eMotor4,200,0);
delay(2000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_YouYi() {
eunihiker.motorRun(eMotor1,200,0);
eunihiker.motorRun(eMotor2,200,0);
eunihiker.motorRun(eMotor3,200,1);
eunihiker.motorRun(eMotor4,200,1);
delay(2000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_YouZhuan() {
eunihiker.motorRun(eMotor1,200,0);
eunihiker.motorRun(eMotor2,200,0);
eunihiker.motorRun(eMotor3,200,0);
eunihiker.motorRun(eMotor4,200,0);
delay(1000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_ZuoZhuan() {
eunihiker.motorRun(eMotor1,200,1);
eunihiker.motorRun(eMotor2,200,1);
eunihiker.motorRun(eMotor3,200,1);
eunihiker.motorRun(eMotor4,200,1);
delay(1000);
eunihiker.motorRun(eMotorall,0,0);
}
void DF_XuanZhuan() {
eunihiker.motorRun(eMotor1,200,1);
eunihiker.motorRun(eMotor2,200,1);
eunihiker.motorRun(eMotor3,200,1);
eunihiker.motorRun(eMotor4,200,1);
delay(2500);
eunihiker.motorRun(eMotorall,0,1);
}
// 事件回调函数
void whenTopic0Received0() {
DF_QianJin();
}
void whenTopic0Received1() {
DF_HouTui();
}
void whenTopic0Received2() {
DF_ZuoYi();
}
void whenTopic0Received3() {
DF_YouYi();
}
void whenTopic0Received4() {
DF_YouZhuan();
}
void whenTopic0Received5() {
DF_ZuoZhuan();
}
void whenTopic0Received6() {
DF_XuanZhuan();
}
# MindPlus
#
import siot
import time
from unihiker import GUI
from pinpong.board import Board
from dfrobot_huskylensv2 import *
# 主程序开始
siot.init(client_id="22314163017578303",server="iot.dfrobot.com.cn",port=1883,user="Xx3rhZtNg",password="ubqr2ZtNRz")
u_gui=GUI()
Board().begin()
huskylens = HuskylensV2_I2C()
huskylens.knock()
siot.connect()
siot.loop()
huskylens.switchAlgorithm(128)
while True:
huskylens.getResult(128)
if (huskylens.available(128)):
u_gui.draw_text(text=(huskylens.getCachedResultByID(128, 1).name if huskylens.getCachedResultByID(128, 1) else -1),x=0,y=150,font_size=20, color="#FF0000")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("go"))):
u_gui.draw_text(text="go",x=100,y=160,font_size=40, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="go")
time.sleep(1)
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("back"))):
u_gui.draw_text(text="back",x=0,y=20,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=20,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="back")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("left"))):
u_gui.draw_text(text="left",x=0,y=40,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=40,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="left")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("right"))):
u_gui.draw_text(text="right",x=0,y=60,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=60,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="right")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("turnleft"))):
u_gui.draw_text(text="turnleft",x=0,y=60,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=60,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="turnleft")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("turnright"))):
u_gui.draw_text(text="turnright",x=0,y=60,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=60,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="turnright")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("turnaround"))):
u_gui.draw_text(text="turnaround",x=0,y=60,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=60,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="turnraround")
if ((huskylens.getCachedResultByIndex(128, 1-1).name if huskylens.getCachedResultByIndex(128, 1-1) else -1)==(str("stop"))):
u_gui.draw_text(text="stop",x=0,y=60,font_size=20, color="#0000FF")
time.sleep(1)
u_gui.draw_text(text=" ",x=0,y=60,font_size=20, color="#0000FF")
siot.publish(topic="fjht1XGDg", data="stop")
步骤6 视频演示
步骤7 制作反思
MIND+2.0的功能确实强大。尤其模型训练这一块,大大降低了数据集制作和训练的难度,配合二哈识图2的强大功能,我们可以制作出许多专属于自己的数据集。这将为教师和学生人工智能的学习和应用带来更多的方便和更多的可能性。由于工作忙碌,时间仓促,这个作品还有许多自己不太满意的地方。原本自己想制作能用手势来控制机械臂的夹爪张开合上机械臂上升下降以及手势来控制小车各种运动。但发现MIND+2.0手势分类功能尚在开发中所以未能实现。相信不久以后这一功能就能实现。同时作品中K10由于时间关系没有增加语音和屏幕的反馈和互动。感谢DFrobot团队的工程师们的不懈努力,给我们带来了MIND+2.0和二哈识图2,也很感谢社区里许多大神们的贴子,解决了我的许多困惑。请各位多多指教!
附件

返回首页
回到顶部


评论