【嵌入式Linux项目】基于Linux的全志H616开发板智能家居项目(语音控制、人脸识别、安卓APP和PC端QT客户端远程操控)有视频功能展示

目录

一、功能需求

二、开发环境

1、硬件:

2、软件:

3、引脚分配:

三、关键点

1、设计模式之工厂模式

2、wiringPi库下的相关硬件操作函数调用

3、语音模块的串口通信

4、线程

5、摄像头的实时监控和拍照功能

6、人脸识别

四、编译和运行

五、视频功能展示


一、功能需求

  • 火焰传感器检测到火焰时,蜂鸣器报警,直到无火焰时停止报警
  • 语音控制多个LED灯的开和关(二楼灯、餐厅灯、客厅灯、卫生间灯)
  • 语音开启摄像头,并在对应IP地址的网页上实时监控画面
  • 语音控制摄像头拍摄照片,存在当前文件夹下(通过filezilla将照片传到PC上查看)
  • 语言开启人脸识别功能,将拍摄照片与本人照片对比,识别成功蜂鸣器滴一声,失败滴四声
  • 通过socket网络,实现开发板跑服务端,安卓手机跑客户端APP或PC上位机跑客户端,实现手机或上位机远程发送指令完成以上功能,并实时将温湿度传感器所测数据在安卓APP或PC上位机的QT界面上显示

二、开发环境

1、硬件:

Orangepi Zero2 全志H616开发板,语音模块SU-03T,摄像头模组OV9726,蜂鸣器,火焰传感器,4个LED等,4路继电器组,6v电源,若干杜邦线。

2、软件:

MobaXterm、VS Code、FileZilla

3、引脚分配:

在MobaXterm命令控制终端输入gpio readall可以查看开发板上的所有引脚。语音模块、蜂鸣器、火焰传感器和4路继电器组的引脚接线在下图框出。

由于加了温湿度传感器之后板子上引脚不够,所以在第二个视频演示时会把语言识别模块替换下来。温湿度传感器对应IO为,VCC--5V, GND--GND, DAT--3wPi。

三、关键点

1、设计模式之工厂模式

工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。本文通过使用工厂模式,将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象(链表)。

    //1.指令工厂初始化pCommandHead = addVoiceContrlToInputCommandLink(pCommandHead);//串口pCommandHead = addSocketContrlToInputCommandLink(pCommandHead);//2.设备控制工厂初始化//四个LED+火灾+蜂鸣器+摄像头pdeviceHead = addBathroomLightToDeviceLink(pdeviceHead);pdeviceHead = addUpstairLightToDeviceLink(pdeviceHead);pdeviceHead = addRestaurantLightToDeviceLink(pdeviceHead);pdeviceHead = addLivingroomLightToDeviceLink(pdeviceHead);pdeviceHead = addFireToDeviceLink(pdeviceHead);pdeviceHead = addBeepToDeviceLink(pdeviceHead);pdeviceHead = addCameraToDeviceLink(pdeviceHead);

2、线程

主函数中创建了三个线程:语音线程、火焰检测线程、网络线程。

语音线程(voiceThread):完成串口的配置和初始化,在while循环里每隔0.3s检查串口是否有语音命令词到来,有则执行对应操作。

火焰检查线程(socketThread):while循环里每隔0.5s检测是否有火焰,有则蜂鸣器发出警报,知道无火焰。

网络线程(socketThread):接收客户端指令,实现上位机远程发送指令完成 [ 功能要求 ] 所述功能;给客户端发送温湿度传感器所测数据,实时显示在QT界面上。

//1、接收客户端的指令控制灯和摄像头 执行指令功能与语音控制复用
//2、向客户端发送温湿度数据 
void *socket_thread(){struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));int clen = sizeof(struct sockaddr_in);socketHandler = findCommandByName("socketServer", pCommandHead);if(socketHandler == NULL){printf("find socketHandler error\n");pthread_exit(NULL);}printf("%s init success\n", socketHandler->commandName);socketHandler->Init(socketHandler, NULL, NULL);while(1){c_fd = accept(socketHandler->sfd, (struct sockaddr *)&c_addr, &clen);piThreadCreate(read_thread);pthread_tempAndHumi_create();}
}

 在网络线程中,首先对套接字进行初始化配置,包括IPV4因特网协议、TCP协议的配置(socket),绑定IP地址和端口号(bind),通过套接字标识符监听对应端口(listen)并等待客户端接入(accept)。

客户端接入后,即创建读数据线程,在while循环中进程阻塞在read函数,直到客户端发出指令,指令的执行函数与语音模块的语音指令执行函数复用(voiceContrlFunc函数在第4小点展示)。例如:在QT界面按下 [开客厅灯] 按钮,客户端通过网络发出字符串"OLL" ,在服务端read读到指令放入socketHandler->command,调用函数voiceContrlFunc(socketHandler->command)执行指令。

同时客户端在接入之后会调用函数pthread_tempAndHumi_create()创建发数据的线程,即实时发送温湿度的数据,QT中配合信号槽接收数据并在QT界面上显示,代码如下。

void *read_thread(){while(1){int n_read = 0;memset(socketHandler->command, '\0', sizeof(socketHandler->command));n_read = read(c_fd, socketHandler->command, sizeof(socketHandler->command));//n_read是读到字节数voiceContrlFunc(socketHandler->command);if(n_read == -1){perror("read");}else if(n_read>0){printf("\nget: %d, %s\n",n_read, socketHandler->command);}else{			printf("client quit\n");break;}}
}

 QT程序,控制开关客厅灯的函数:

void Widget::on_livingRoomLight_clicked()
{if(livingRoomLightFlag == 1){//客户端向服务端发送消息if(tcpSocket->state() == QAbstractSocket::ConnectedState){ui->livingRoomLight->setText("关客厅灯");tcpSocket->write("OLL");ui->textBrowser->append("> 客厅灯已打开\n");livingRoomLightFlag = 0;}else{ui->textBrowser->append("请先与服务端连接!");}}else{if(tcpSocket->state() == QAbstractSocket::ConnectedState){ui->livingRoomLight->setText("开客厅灯");tcpSocket->write("CLL");ui->textBrowser->append("> 客厅灯已关闭\n");livingRoomLightFlag = 1;}else{ui->textBrowser->append("请先与服务端连接!");}}
}

 QT程序中的信号槽连接和TCP网络读函数:

connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(receiveMessages()));void Widget::receiveMessages()
{QByteArray tmpByteArray = tcpSocket->readAll();char* tempHumi;tempHumi = tmpByteArray.data();ui->tempLabel->setText(QString::number(tempHumi[0]));ui->humiLabel->setText(QString::number(tempHumi[2]));
}

 

                                                                   PC端QT界面

                                                                         APP界面

3、wiringPi库下的相关硬件操作函数调用

包括wiringPi库的初始化,蜂鸣器、火焰传感器、继电器组的输入输出引脚配置和高低电平设置。wiringP库下的串口配置和初始化。

4、语音模块的串口通信

本文用的是全志H616芯片中的串口设备/dev/ttyS5,波特率115200,实现与语音模块的串口通信。语音模块收到我们发出的命令词,将命令词转化为16进制数据通过串口发送到开发板,在程序中完成对串口数据接收、存储、判断是哪种命令、执行对应命令的操作,包括开关二楼灯、餐厅灯、客厅灯、卫生间灯、开启摄像头、拍一张照片和人脸识别。

语音模块SU-03T需要烧入对应命令的SDK,本文配置的SDK是在智能公元/AI产品零代码平台上完成,免费的。

int get_voice_type(char *cmd)
{if(!strcmp("OLL", cmd))    return OLL;if(!strcmp("ORL", cmd))    return ORL;if(!strcmp("OUL", cmd))    return OUL;if(!strcmp("OBL", cmd))    return OBL;if(!strcmp("CLL", cmd))    return CLL;if(!strcmp("CRL", cmd))    return CRL;if(!strcmp("CUL", cmd))    return CUL;if(!strcmp("CBL", cmd))    return CBL;if(!strcmp("OC" , cmd))    return OC ;if(!strcmp("TAP", cmd))    return TAP;if(!strcmp("OFR", cmd))    return OFR;perror("voice recognition failure");
}void voiceContrlFunc(char *cmd){switch(get_voice_type(cmd)){case OLL://OLL ASCII对应的16进制4f 4c 4cprintf("open livingroom light\n");struct Devices *tmpOpenLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);tmpOpenLivingroomLight->open(tmpOpenLivingroomLight->pinNum);break;case ORL://ORL 4f 52 4cprintf("open restaurant light\n");struct Devices *tmpOpenRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);tmpOpenRestaurantLight->open(tmpOpenRestaurantLight->pinNum);break;case OUL://OUL 4f 55 4cprintf("open upstair light\n");struct Devices *tmpOpenUpstairLight = findDeviceByName("upstairLight", pdeviceHead);tmpOpenUpstairLight->open(tmpOpenUpstairLight->pinNum);break;case OBL://OBL 4f 42 4cprintf("open bathroom light\n");struct Devices *tmpOpenBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);tmpOpenBathroomLight->open(tmpOpenBathroomLight->pinNum);break;case CLL://CLL 43 4c 4cprintf("close livingroom light\n");struct Devices *tmpCloseLivingroomLight = findDeviceByName("livingroomLight", pdeviceHead);tmpCloseLivingroomLight->close(tmpCloseLivingroomLight->pinNum);break;case CRL://CRL 43 52 4cprintf("close restaurant light\n");struct Devices *tmpCloseRestaurantLight = findDeviceByName("restaurantLight", pdeviceHead);tmpCloseRestaurantLight->close(tmpCloseRestaurantLight->pinNum);break;case CUL://CUL 43 55 4cprintf("close upstair light\n");struct Devices *tmpCloseUpstairLight = findDeviceByName("upstairLight", pdeviceHead);tmpCloseUpstairLight->close(tmpCloseUpstairLight->pinNum);break;case CBL://CBL 43 42 4cprintf("close bathroom light\n");struct Devices *tmpCloseBathroomLight = findDeviceByName("bathroomLight", pdeviceHead);tmpCloseBathroomLight->close(tmpCloseBathroomLight->pinNum);break;case OC://OC 4f 43printf("open camera\n");printf("   -------------------------------------------------------------------\n");printf("   --\033[1;32m 已开启摄像头,请到指定网页观看画面 https//192.168.43.206:8081 \033[0m--\n");//黄色字体printf("   -------------------------------------------------------------------\n");printf("\n");break;case TAP://TAP 54 41 50printf("take a picture\n");struct Devices *tmpTakeAPictureCamera = findDeviceByName("camera", pdeviceHead);tmpTakeAPictureCamera->takeAPicture();printf("   --------------------------------------\n");printf("   --\033[1;32m 已拍照,请在当前文件夹下查看照片 \033[0m--\n");//黄色字体printf("   --------------------------------------\n");printf("\n");break;case OFR://OFR 4f 46 52printf("open face recognition\n");struct Devices *tmpFaceRecCamera = findDeviceByName("camera", pdeviceHead);tmpFaceRecCamera->faceRecognition();break;}
}

5、摄像头的实时监控和拍照功能

参考这篇文章:(1031条消息) 香橙派Orange Pi Zero 2开发板配置USB摄像头的方法_阿龙还在写代码的博客-CSDN博客

6、人脸识别

通过命令词【人脸识别】让摄像头拍摄一张照片并与本地的本人照片做对比,成功则蜂鸣器滴一声,失败则滴四声。人脸识别的处理程序调用的是翔云OCR人脸识别 (netocr.com)的人脸识别API,该接口地址https://netocr.com/api/faceliu.do是https协议的地址,https协议是在http和tcp之间多添加了一层,进行身份验证和数据加密。

若要访问https协议的地址,则需要用到Libcurl这个跨平台的网络协议库,配合OpenSSL库,就可以访问https协议的接口。(编译OpenSSL支持Libcurl的https访问,如果直接编译Libcurl,只能访问http不能访问https,需要OpenSSL库才能访问https)

 camera.c

#include "contrlDevices.h"
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>extern struct Devices *pdeviceHead;//extern表面变量或函数是定义在其他文件中,声明 为全局变量在该源文件使用
struct Devices* findDeviceByName(char *name, struct Devices *phead);#define true 1
#define false 0
struct Devices camera;
char buf[1024] = {'\0'};//全局void cameraTakeAPicture(){system("(fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg) > tmpFile");//照片存放在当前目录下
}size_t readData(void *ptr, size_t size, size_t nmemb, void *stream){strncpy(buf, ptr, 1024);
}char *getPicBase64FromFile(char *filePath){char *bufPic = NULL;char cmd[128] = {'\0'};sprintf(cmd, "base64 %s > tmpFile", filePath);system(cmd);//图片的base64流数据存入tmpFile文件中int fd = open("./tmpFile", O_RDWR);int filelen = lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET);//重新让文件的光标回到初始位置bufPic = (char *)malloc(filelen + 2);//+1也可以 多加点没毛病memset(bufPic, '\0', filelen + 2);read(fd, bufPic, filelen);close(fd);system("rm -f tmpFile");return bufPic;
}void cameraFaceRecognition(){camera.takeAPicture();CURL *curl;CURLcode res;char *postString;char *img1;char *img2;char *key = "DYRrmZz2rTwYGywyWdhKzR";char *secret = "56bc8e083a9b4d9fbf590413ddcb3a61";int  typeId = 21;char *format = "xml";char *bufPic1 = getPicBase64FromFile("./image.jpg");char *bufPic2 = getPicBase64FromFile("./zyl.jpg");int len = strlen(key) + strlen(secret) + strlen(bufPic1) + strlen(bufPic2) + 128;postString = (char *)malloc(len);memset(postString, '\0', len);//sizeof(postString)替换成len,因为postString是指针sprintf(postString, "&img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", bufPic1, bufPic2, key, secret, 21, format);//拼接字符串curl = curl_easy_init();if (curl){curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); // 指定cookie文件curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);    // 指定post内容curl_easy_setopt(curl, CURLOPT_URL,"https://netocr.com/api/faceliu.do");   // 指定urlcurl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);//当有数据回来 调用回调函数res = curl_easy_perform(curl);struct Devices *beepHandler = findDeviceByName("beep", pdeviceHead);if(strstr(buf, "是") != NULL){ beepHandler->open(beepHandler->pinNum); usleep(300000);beepHandler->close(beepHandler->pinNum);printf("\n");printf("   -----------------------------------\n");printf("   --\033[1;32m 人脸识别成功: the same person \033[0m--\n");//绿色字体printf("   -----------------------------------\n");printf("\n");}else{int i = 4;while(i--){beepHandler->open(beepHandler->pinNum); usleep(200000);beepHandler->close(beepHandler->pinNum); usleep(100000);}printf("\n");printf("   ------------------------------------\n");printf("   --\033[1;31m 人脸识别失败: different person \033[0m--\n");//红色字体printf("   ------------------------------------\n");printf("\n");}curl_easy_cleanup(curl);}
}//实例化对象
struct Devices camera = {.deviceName = "camera",.takeAPicture = cameraTakeAPicture,.faceRecognition = cameraFaceRecognition};struct Devices* addCameraToDeviceLink(struct Devices *phead){if(phead == NULL){return &camera;}else{//头插camera.next = phead;phead = &camera;return phead;}
}

7、qt程序跨平台运行(编译成安卓APP)

搭建环境所需的安装包:

四、编译和运行

编译时需要用到一些库文件和该库文件里的头文件,用到温湿度传感器时加上tempAndHumi.c

gcc bathroomLight.c livingroomLight.c restaurantLight.c upstairLight.c socketContrl.c voiceContrl.c fireDetection.c beep.c camera.c usartContrl.c main.c -I ../httpHandler/curl-7.71.1/_install/include/ -L ../httpHandler/curl-7.71.1/_install/lib/ -lcurl -lwiringPi -lpthread

运行:

sudo ./a.out

五、视频功能展示

           智能家居功能展示

        PC端QT界面功能展示

            安卓app功能展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/14794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot实践四 --集中式缓存Redis

随着时间的积累&#xff0c;应用的使用用户不断增加&#xff0c;数据规模也越来越大&#xff0c;往往数据库查询操作会成为影响用户使用体验的瓶颈&#xff0c;此时使用缓存往往是解决这一问题非常好的手段之一。Spring 3开始提供了强大的基于注解的缓存支持&#xff0c;可以通…

ios私钥证书的创建方法

ios私钥证书是苹果公司为ios开发者打包app&#xff0c;推出的一种数字证书&#xff0c;只有同一个苹果开发者账号生成的ios私钥证书打的包&#xff0c;才能上架同一个开发者账号的app store。因此不要指望别人给你共享私钥证书和描述文件&#xff0c;因为别人的证书和描述文件打…

Nginx下载、安装与使用

Nginx下载 简介&#xff1a; Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务&#xff08;邮件服务&#xff09;。 官网下载地址&#xff1a; https://nginx.org/en/download.html 国内镜像地址&#xff1a; https://mirrors.huawe…

基于RASC的keil电子时钟制作(瑞萨RA)(6)----定时器驱动数码管

基于RASC的keil电子时钟制作6_定时器驱动数码管 概述硬件准备视频教程选择定时器定时器做计数器配置定时器回调函数timer_smg.ctimer_smg.h演示效果主程序 概述 要想让每个数码管显示不同的数字&#xff0c;但是数码管必须依次地被持续驱动&#xff0c;数码管之间的刷新速度应…

【图论】Prim算法

一.介绍 Prim算法是一种用于解决最小生成树问题的贪心算法。最小生成树问题是指在一个连通无向图中找到一个生成树&#xff0c;使得树中所有边的权重之和最小。 Prim算法的基本思想是从一个起始顶点开始&#xff0c;逐步扩展生成树&#xff0c;直到覆盖所有顶点。具体步骤如下…

SpringBoot面试题及答案整理

1、什么是 Spring Boot&#xff1f; 多年来&#xff0c;随着新功能的增加&#xff0c;spring 变得越来越复杂。访问spring官网页面&#xff0c;我们就会看到可以在我们的应用程序中使用的所有 Spring 项目的不同功能。如果必须启动一个新的 Spring 项目&#xff0c;我们必须添…

动脑学院Jetpack Compose学习笔记

最近b站学习了一下Compose相关内容&#xff0c;整理了相关笔记&#xff0c;仅供大家参考。 资源链接如下&#xff0c;象征性收取1个积分 https://download.csdn.net/download/juliantem/88125198

SOC FPGA介绍及开发设计流程

目录 一、SoC FPGA简介 二、SoC FPGA开发流程 2.1 硬件开发 2.2 软件开发 一、SoC FPGA简介 SOC FPGA是在FPGA架构中集成了基于ARM的硬核处理器系统(HPS)&#xff0c;包括处理器、外设和存储器控制器。相较于传统的仅有ARM处理器或 FPGA 的嵌入式芯片&#xff0c;SOC FPGA既…

CSS Flex 笔记

1. Flexbox 术语 Flex 容器可以是<div> 等&#xff0c;对其设置属性&#xff1a;display: flex, justify-content 是沿主轴方向调整元素&#xff0c;align-items 是沿交叉轴对齐元素。 2. Cheatsheet 2.1 设置 Flex 容器&#xff0c;加粗的属性为默认值 2.1.1 align-it…

1,复杂度和简单排序算法【p2-p3】

复杂度和简单排序算法 1&#xff0c;时间复杂度1.1选择排序1.2冒泡排序1.3异或运算1.3.1性质&#xff1a;1.3.2案例例1例2 1.4插入排序1.5二分法1.5.1在一个有序数组中&#xff0c;找某个数是否存在1.5.2在一个有序数组中&#xff0c;找>某个数最左侧的位置1.5.3局部最小值问…

Individual household electric power consumption个人家庭用电量数据挖掘与时序预测建模

今天接到一个任务就是需要基于给定的数据集来进行数据挖掘分析相关的计算&#xff0c;并完成对未来时段内数据的预测建模&#xff0c;话不多说直接看内容。 官方数据详情介绍在这里&#xff0c;如下所示&#xff1a; 数据集中一共包含9个不同的字段&#xff0c;详情如下&#…

手把手一起实现Visual Studio 2022本地工程提交(和克隆)Gitee

1、VS2022本地工程提交Gitee 登录Gitee&#xff0c;创建空仓库&#xff0c;如图&#xff1a; 新建仓库&#xff1a; 打开Visual Studio 2022创建的工程&#xff0c;点击创建Git存储库&#xff1a; 复制Gitee仓库URL&#xff1a; 将URL填入&#xff0c;点击创建并推送&#xff…

Windows 10 安装 PostgreSQL 12.x 报错 ‘psql‘ 不是内部或外部命令 由于找不到文件libintl-9.dll等问题

目录 序言一、问题总结问题 1 psql 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。问题 2 “由于找不到文件libintl-9.dll&#xff0c;无法继续执行代码&#xff0c;重新安装程序可能会解决此问题。“1、卸载2、安装3、安装 Stack Builder &#xff08;这个可…

LeetCode.189(轮转数组)

对于轮转数组这个题&#xff0c;文章一共提供三种思路&#xff0c;对于每种思路均提供其对应代码的时间、空间复杂度。 目录 1. 创建变量来保存最后一个数&#xff0c;并将其余数组向前挪动一位 &#xff1a; 1.1 原理解析&#xff1a; 1.2 代码实现&#xff1a; 2.创建一个…

NFT和数字藏品的安全方案解析

一、NFT和数字藏品 01 NFT是什么&#xff1f; NFT 是Non-Fungible Tokens 的缩写&#xff0c;意思是不可互换的代币&#xff0c;它是相对于可互换的代币而言的。不可互换的代币也称为非同质代币。什么是可互换的代币&#xff1f;比如BTC&#xff08;比特币&#xff09;、ETH&…

前端,js , Error in created hook: TypeError ,有bug了

怎么兄弟&#xff0c;遇到bug了&#xff1f;&#xff1f;&#xff1f;你开心吗&#xff0c;哈哈哈哈

如何在MacBook上彻底删除mysql

好久以前安装过&#xff0c;但是现在配置mysql一直出错&#xff0c;索性全部删掉重新配置。 一、停止MySQL服务 首先&#xff0c;请确保 MySQL 服务器已经停止运行&#xff0c;以免影响后续的删除操作。 sudo /usr/local/mysql/support-files/mysql.server stop如果你输入之…

kotlin 编写一个简单的天气预报app(四)

编写界面来显示返回的数据 用户友好性&#xff1a;通过界面设计和用户体验优化&#xff0c;可以使天气信息更易读、易理解和易操作。有效的界面设计可以提高用户满意度并提供更好的交互体验。 增加城市名字的TextView <TextViewandroid:id"id/textViewCityName"…

Kyuubi入门简介

一、官方简介 HOME — Apache Kyuubi 二、概述 1、一个企业级数据湖探索平台 2、一个高性能的通用JDBC和SQL执行引擎 3、一个基于spark的查询引擎服务 三、优点 1、提供hiveserver2查询spark sql的能力&#xff0c;查询效率更为高效&#xff0c;首次构建连接时会持续保持连…

628. 三个数的最大乘积

628. 三个数的最大乘积 class Solution {public int maximumProduct(int[] nums) {Arrays.sort(nums); return Math.max(nums[nums.length-1]*nums[nums.length-2]*nums[nums.length-3],nums[0]*nums[1]*nums[nums.length-1]);} }