今天要分享的是一个极具趣味性的开源硬件项目——模拟人眼追踪与跟随运动的完整例程。开发者仅借助几张人眼图片素材,便在智能屏上实现了眼珠转动、眨眼动作、人脸识别与实时跟随等丰富功能,整体效果十分逼真生动。下面就来详细拆解一下整个实现过程与技术要点。
01 方案概述与设计思路
1. UI图片素材准备
迪文智能屏采用图片素材完成UI开发,这种基于图层的设计方式能够非常便捷地实现各类细腻的显示效果。本次项目所涉及的素材包括不同状态的眼皮、眼珠图片,通过将这些素材灵活组合,即可逼真模拟人眼的多种动作与表情。

2. 界面开发与布局
通过DGUS软件开发界面十分简洁高效,仅需两个图形控件即可完成主体搭建。本例中,工程师选用了一款2.1寸圆形智能屏,其圆形显示区域恰好适合模拟人眼形态,视觉呈现更加自然。

3. 眨眼功能实现原理
眨眼的实现逻辑非常直观:每隔一段时间,让眼皮图片按照顺序轮流显示,从而形成从闭合到张开的完整动画过程。以下是实现眨眼功能的核心代码:
//眨眼动画
void blink_animat(void)
{
if(blink_flag == 0)
{
blink_cnt++;
if(blink_cnt >= 4)
{
blink_flag = 1;
}
}
else
{
blink_cnt--;
if(blink_cnt <= 0)
{
blink_flag = 0;
}
}
write_dgus_vp(0x3000, (u8 *)&blink_cnt, 2);
}
void blink_run()
{
static u32 run_timer_cnt = 0;
run_timer_cnt++;
if(run_timer_cnt >= 2000000)
{
run_timer_cnt = 0;
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
blink_animat();
Delay_ms(30);
}
}
代码中通过计时器进行累计计数,当达到预设次数后,连续调用眨眼动画函数,并结合延时控制,完整模拟出人眼从闭合到张开的自然过渡过程。
4. 眼珠自然左右运动实现
眼珠运动机制与眨眼类似,但需要更加精细的时间控制,使眼珠移动轨迹看起来平滑自然,而非生硬跳变。工程师经过多轮调试与优化,最终采用分阶段累加的控制方式,让眼珠在多个位置之间实现平滑过渡。核心代码如下:
//眼珠动画
void eyeball_animat(void)
{
eyeball_timer_cnt++;
if(eyeball_timer_cnt < 50)
{
eyeball_cnt = 20;
}
else if(eyeball_timer_cnt < 51)
{
eyeball_cnt = 50;
}
else if(eyeball_timer_cnt < 52)
{
eyeball_cnt = 80;
}
else if(eyeball_timer_cnt < 53)
{
eyeball_cnt = 94;
}
else if(eyeball_timer_cnt < 103)
{
eyeball_cnt = 94;
}
else if(eyeball_timer_cnt < 104)
{
eyeball_cnt = 80;
}
else if(eyeball_timer_cnt < 105)
{
eyeball_cnt = 50;
}
else if(eyeball_timer_cnt < 106)
{
eyeball_cnt = 20;
}
else if(eyeball_timer_cnt < 107)
{
eyeball_cnt = -10;
}
else if(eyeball_timer_cnt < 108)
{
eyeball_cnt = -40;
}
else if(eyeball_timer_cnt < 158)
{
eyeball_cnt = -54;
}
else if(eyeball_timer_cnt < 159)
{
eyeball_cnt = -40;
}
else if(eyeball_timer_cnt < 160)
{
eyeball_cnt = -10;
}
else if(eyeball_timer_cnt < 161)
{
eyeball_cnt = 20;
eyeball_timer_cnt = 0;
}
//左右移动
// if(eyeball_flag == 0)
// {
// eyeball_cnt++;
// if(eyeball_cnt >= 94)
// {
// eyeball_flag = 1;
// }
// }
// else
// {
// eyeball_cnt--;
// if(eyeball_cnt <= -54)
// {
// eyeball_flag = 0;
// }
// }
if(eyeball_cnt >= 0)
{
eyeball_pos[0] = 0x00;
eyeball_pos[1] = eyeball_cnt;
}
else
{
eyeball_pos[0] = 0xFF;
eyeball_pos[1] = (eyeball_cnt & 0xFF);
}
write_dgus_vp(0x3111, (u8 *)&eyeball_pos, 2);
}
void eyeball_run()
{
static u32 run_timer_cnt = 0;
run_timer_cnt++;
if(run_timer_cnt >= 20000)
{
run_timer_cnt = 0;
eyeball_animat();
}
}
值得注意的是,这里采用了复杂的分段控制策略,而非简单的来回摆动。这种设计使得眼珠的运动轨迹更接近真实人眼的行为模式——先快速扫向一侧,再缓慢回正,随后转向另一侧,整体效果更加自然流畅。被注释掉的代码展示了一种简单的往返方案,但显然逼真度不足,因此被更优的分段控制所取代。
5. 融合ESP32人脸识别,实现眼珠实时跟随
这一功能是整个项目的点睛之笔。当ESP32模块检测到人脸时,眼珠便不再进行随机自主运动,而是根据人脸位置实时调整视线方向,呈现出智能追踪的效果。具体的处理逻辑是:定义一个变量在while循环中持续自增,当自增达到一定数值时,眼珠执行自主运动;当串口接收到外部数据时,该变量立即清零,并仅根据人脸位置信息驱动眼珠移动。关键代码如下:
if(rec_data_timer_cnt < 1000000)
{
rec_data_timer_cnt++;
}
else
{
eyeball_run();
}
extern u32 rec_data_timer_cnt;
extern u16 eyeball_timer_cnt;
void Communication_CMD(u8 st)
{
if((uart[st].Rx_F==1 )&&(uart[st].Rx_T==0))
{
rec_data_timer_cnt = 0;
eyeball_timer_cnt = 0;
#if(Type_Communication==1)
Describe_8283(st);
#elif(Type_Communication==2)
Describe_Modbus(st);
#endif
uart[st].Rx_F=0;
uart[st].Rx_Num=0;
}
}
整体逻辑非常清晰:串口收到数据时,立即重置计时器,同时将眼珠动画计时归零,使眼珠迅速切换到跟随模式;如果长时间未收到数据(例如人脸离开摄像头检测范围),计时器自增到设定值后,眼珠便自动恢复自主运动状态,让屏幕始终保持一种灵动的“生命感”。
整个项目巧妙地将图形界面开发、定时动画控制与外部传感器数据通信融为一体,虽然代码量不大,但最终呈现的效果相当惊艳。对于希望利用智能屏打造交互式显示应用的开发者来说,这无疑是一个非常经典的参考案例与学习范例。
