- 项目介绍
看到MAX78000的手势识别功能,脑子不知为何突然想起葛优老师和范伟老师的分歧终端机。好吧,那就做一个更高级的分歧终端机,不但无法耍赖,还可以由机器判断输赢,实现更高层面的无法耍赖。
通过整合MAX78000的手势识别功能,我们可以利用该技术来感知玩家的手势动作。具备以下特点:
-
手势识别功能:利用MAX78000的强大算法和传感器,我们能够实现准确的手势识别。
-
机器智能判断:基于预先训练的模型和算法,分歧终端机能够分析玩家的手势,并根据规则确定最终的输赢结果。
-
无法耍赖:玩家无法慢出或耍赖。增加游戏的公平性和竞争性。
无论是在家庭聚会还是社交活动中,这个终端机都能为参与者带来快乐和互动,同时展示了MAX78000技术的应用潜力。接下来,我们将详细看看该终端机的硬件设计和软件实现。
- 项目设计思路
+-------------------+
| |
| MAX78000 |
| |
+--+---------+------+
| |
| |
| |
| Camera Module
|
|
+----------------v----------------+
| |
| Image Capture |
| |
+---------------+-----------------+
|
|
|
| Gesture Recognition
|
+---------------v-----------------+
| |
| Pretrained Gesture Model |
| |
+---------------+-----------------+
|
|
|
| Result Comparison
|
+---------------v-----------------+
| |
| Game Result Display |
| |
+---------------------------------+
硬件设计:
-
利用MAX78000板载的摄像头进行图像抓取。摄像头可以捕捉到玩家的手势动作,提供图像数据用于后续的手势识别。
-
使用外接的2.8英寸 IL9341显示屏来进行信息提示和结果显示。该显示屏可以显示游戏阶段的提示信息,以及最终的输赢结果。
-
引入板载按钮PB1来触发游戏的不同阶段。通过按下按钮,玩家可以开始进行手势动作的抓取、手势识别以及结果判断等步骤。
软件实现:
-
图像处理与手势识别:利用MAX78000的图像处理功能,对摄像头捕获的图像进行预处理和特征提取。然后,使用预训练的手势模型来对手势进行识别。根据模型的输出,确定玩家选择的是石头、剪刀还是布。
-
结果判断与显示:将两名玩家的手势识别结果进行对比,并根据规则判断输赢。最终的结果可以通过外接的2.8英寸 IL9341显示屏来显示,以便玩家和观众可以清楚地看到游戏结果。
- 关键代码说明
项目的主要硬件部分涉及对摄像头和LCD屏幕的驱动。在整个项目的资源初始化部分中,我们通过下方的代码来完成初始化操作。这份代码是基于现有的例程进行修改的,原始例程在设计时考虑了兼容性,可以同时适配Feather Board和Evaluation Kit。因此,在代码中我没有删除Evaluation Kit相关的部分,而是通过使用宏定义来选择执行适用于Feather Board的代码,以确保原始程序的兼容性。这样的设计使得我们能够在不同硬件平台上灵活使用和扩展我们的项目。
int i, dma_channel;
int digs, tens;
int ret = 0;
uint8_t user1_choice = 0, user2_choice = 0;
int result[CNN_NUM_OUTPUTS] = {0};
#if defined(BOARD_FTHR_REVA)
// Wait for PMIC 1.8V to become available, about 180ms after power up.
MXC_Delay(200000);
/* Enable camera power */
Camera_Power(POWER_ON);
printf("\n\nRPS Feather Demo\n");
#else
printf("\n\nRPS Evkit Demo\n");
#endif
MXC_ICC_Enable(MXC_ICC0); // Enable cache
// Switch to 100 MHz clock
MXC_SYS_Clock_Select(MXC_SYS_CLOCK_IPO);
SystemCoreClockUpdate();
printf("Waiting...\n");
// DO NOT DELETE THIS LINE:
MXC_Delay(SEC(2)); // Let debugger interrupt if needed
// Enable peripheral, enable CNN interrupt, turn on CNN clock
// CNN clock: 50 MHz div 1
cnn_enable(MXC_S_GCR_PCLKDIV_CNNCLKSEL_PCLK, MXC_S_GCR_PCLKDIV_CNNCLKDIV_DIV1);
// Configure P2.5, turn on the CNN Boost
mxc_gpio_cfg_t gpio_out;
gpio_out.port = MXC_GPIO2;
gpio_out.mask = MXC_GPIO_PIN_5;
gpio_out.pad = MXC_GPIO_PAD_NONE;
gpio_out.func = MXC_GPIO_FUNC_OUT;
MXC_GPIO_Config(&gpio_out);
MXC_GPIO_OutSet(gpio_out.port, gpio_out.mask);
#ifdef TFT_ENABLE
// Initialize TFT display.
printf("Init LCD.\n");
#ifdef BOARD_EVKIT_V1
MXC_TFT_Init();
MXC_TFT_ClearScreen();
// Rotate screen 180 degree
MXC_TFT_SetRotation(SCREEN_FLIP);
MXC_TFT_ShowImage(0, 0, image_bitmap_1);
#endif
#ifdef BOARD_FTHR_REVA
/* Initialize TFT display */
MXC_TFT_Init(MXC_SPI0, 1, NULL, NULL);
MXC_TFT_SetRotation(ROTATE_90);
MXC_TFT_ShowImage(0, 0, image_bitmap_1);
MXC_TFT_SetForeGroundColor(WHITE); // set chars to white
#endif
MXC_Delay(3000000);
#endif
// Initialize DMA for camera interface
MXC_DMA_Init();
dma_channel = MXC_DMA_AcquireChannel();
// Initialize camera.
printf("Init Camera.\n");
camera_init(CAMERA_FREQ);
ret = camera_setup(IMAGE_SIZE_X, IMAGE_SIZE_Y, PIXFORMAT_RGB888, FIFO_THREE_BYTE, USE_DMA,
dma_channel);
if (ret != STATUS_OK)
{
printf("Error returned from setting up camera. Error %d\n", ret);
return -1;
}
在主循环部分,我们通过按键触发下一步操作。首先,我们使用摄像头分别采集两名玩家的手势照片。接下来,我们将这些照片通过卷积神经网络(CNN)进行处理,以识别出手势的类型。然后,我们将识别结果与预设手势进行比较,得出最终获胜的玩家。
最后,我们将抓取到的双方图像以及相关的文字信息显示在LCD屏幕上,以便玩家观看比赛结果。通过LCD屏幕的显示,玩家可以看到双方玩家的手势图像以及获胜者的信息,为整个游戏增添了互动性和可视化效果。
其中,最关键的部分是图像的捕捉与识别,具体实现方法如下:
// Capture a single camera frame.
printf("\nCapture a camera frame %d\n", ++frame);
capture_camera_img();
// Copy the image data to the CNN input arrays.
printf("Copy camera frame to CNN input buffers.\n");
process_camera_img(input_3_camera, input_4_camera, input_5_camera);
convert_img_unsigned_to_signed(input_0_camera, input_1_camera, input_2_camera);
cnn_init(); // Bring state machine into consistent state
cnn_load_weights(); // Load kernels
cnn_load_bias();
cnn_configure(); // Configure state machine
cnn_start(); // Start CNN processing
load_input(); // Load data input via FIFO
MXC_TMR_SW_Start(MXC_TMR0);
while (cnn_time == 0) __WFI(); // Wait for CNN
softmax_layer();
printf("Time for CNN1: %d us\n\n", cnn_time);
printf("Classification results:\n");
for (i = 0; i < CNN_NUM_OUTPUTS; i++)
{
digs = (1000 * ml_softmax[i] + 0x4000) >> 15;
tens = digs % 10;
digs = digs / 10;
result[i] = digs;
printf("[%7d] -> Class %d %8s: %d.%d%%\r\n", ml_data[i], i, classes[i], digs, tens);
}
printf("\n");
if (result[0] > 60)
{
user1_choice = 1;
TFT_Print(buff, 30, 55, font_2, snprintf(buff, sizeof(buff), "Paper"));
printf("User choose: %s \r\n", classes[0]);
}
else if (result[1] > 60)
{
user1_choice = 2;
TFT_Print(buff, 30, 55, font_2, snprintf(buff, sizeof(buff), "Rock"));
printf("User choose: %s \r\n", classes[1]);
}
else if (result[2] > 60)
{
user1_choice = 3;
TFT_Print(buff, 30, 55, font_2, snprintf(buff, sizeof(buff), "Scissors"));
printf("User choose: %s \r\n", classes[2]);
}
else
{
user1_choice = 0;
TFT_Print(buff, 30, 55, font_2, snprintf(buff, sizeof(buff), "Unknown"));
}
convert_img_signed_to_unsigned(input_0_camera, input_1_camera, input_2_camera);
- 实现结果展示
TFT屏幕通过面包板与MAX78000相连,开机画面我做了一下修改,把经典的原版电影图片放了上去。修改的方法比较简单,和一般单片机显示图片取模方法没什么差别,这里就不做赘述了。
整个流程都是通过PB1这个按键来控制的,每一步都在屏幕上显示了指示,按下PB1后进入下一步。
最终结果显示:
- 未来计划
根据实际测试结果,官方提供的预训练模型在单片机上的识别准确度较低,与在电脑上运行的类似Python项目相比存在明显差距。这主要是由于单片机的计算能力有限,难以实现更高的精度要求。
然而,我们不能排除官方预训练模型使用的素材与我用摄像头抓取到的素材之间存在较大差异的可能性。为了尝试提高识别精度,未来我会考虑使用板载摄像头自行拍摄素材,并以此重新训练模型。这样做可以更贴近实际应用场景,看看能否提高识别精度并更好的满足需求。