【嵌入式Linux项目】基于Linux的全志H616开发板智能垃圾桶项目

news2024/12/23 15:58:51

目录

一、功能需求

二、涵盖的知识点

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

2、线程(未使用互斥锁和条件)

 3、父子进程

4、网络编程(socket套接字)

5、进程间通信(共享内存和信号量)

三、开发环境

1、硬件:

2、软件:

3、引脚分配:

四、代码

1、服务端代码:server.c

2、客户端代码:client.c

五、编译和运行

六、视频功能展示


一、功能需求

  • 靠近时,垃圾桶开盖2秒,2秒后关盖
  • 垃圾桶开盖、关盖带滴滴声(蜂鸣器发声)
  • 垃圾桶开盖超过10秒,滴滴声报警
  • 通过Socket网络编程,开发板运行服务端,上位机运行客户端。实现Socket客户端发送指令远程打开和关闭垃圾桶

二、涵盖的知识点

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

包括wiringPi库的初始化,蜂鸣器、超声波测距、sg90舵机的输入输出引脚配置和高低电平设置,超声波测距中的时间函数和距离测算,sg90舵机中的PWM信号控制、Linux定时器和信号处理。

2、线程(未使用互斥锁和条件)

服务端创建了三个线程:超声波测距、sg90舵机、socket命令。(下面括号中为对于线程函数名)

  • 超声波测距(*ultrasonic):在while循环中每隔0.5s计算一次距离。
  • sg90舵机(*sg90):在while循环中每隔20ms通过所测距或客户端指令,执行垃圾桶开盖/关盖。
  • socket命令(*socketCmd):完成共享内存和信号量的创建,在while循环中每隔0.5s查看客户端是否发出指令,若服务端收到指令(open or close),则执行垃圾桶开盖/关盖。

 3、父子进程

在main函数中,父进程完成设备初始化和socket服务端搭建。while循环中,父进程阻塞在accept函数处,等待多个客户端接入;子进程实现对共享内存和信号量的获取,在while循环中读取客户端发出的指令,并将指令写到共享内存。

4、网络编程(socket套接字)

通过Socket网络编程,开发板运行服务端,上位机运行客户端。实现Socket客户端发送指令远程打开和关闭垃圾桶。

5、进程间通信(共享内存和信号量)

父子进程间的通信通过共享内存来完成,信号量用于实现进程间的互斥与同步。

                                                             图1 cmd指令流向图

三、开发环境

1、硬件:

Orangepi Zero2 全志H616开发板,超声波测距模块,蜂鸣器,sg90舵机,一台电脑

2、软件:

MobaXterm、Ubuntu linux操作系统(虚拟机)

3、引脚分配:

在MobaXterm命令控制终端输入gpio readall可以查看开发板上的所有引脚。蜂鸣器、超声波测距和sg90舵机的引脚接线在下图框出。

图2 引脚分配

四、代码

1、服务端代码:server.c

#include <stdio.h>
#include <sys/time.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>      
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
 
#define Trig 9
#define Echo 10
#define BEEP 0 //设置针脚0为蜂鸣器的控制引脚
#define SG90Pin 6
#define OPEN  1
#define CLOSE 2

static int curAngle;//当前角度
static int prevAngel;//上一个角度
static int i 			    = 0;
static int j 			    = 5;
static double distance 		= 0;
static int 	  openCnt 		= 0;
static int    openSocket 	= 0;
static int    closeSocket 	= 0;
char buf[128];

struct Msg
{
    int type;
    char cmd[128];
};

/**************信号量**************/
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO */
};

void pGetKey(int id)
{
	struct sembuf set;
	set.sem_num = 0;//信号量编号 不写也可以 默认是0
	set.sem_op = -1;//把钥匙 -1
	set.sem_flg=SEM_UNDO;//设置为等待
	semop(id, &set, 1);//在semop函数中取钥匙
	//printf("getkey\n");
}

void vPutBackKey(int id)
{
	struct sembuf set;
	set.sem_num = 0;
	set.sem_op = 1;//把钥匙 +1 (锁的数量+1)
	set.sem_flg=SEM_UNDO;
	semop(id, &set, 1);
	//printf("put back the key\n");
}

/**************服务器控制指令**************/
void *socketCmd(){
	//父进程 共享内存和信号量创建
	int shmid;
	int semid;
	char *shmaddr;//指向共享内存
	int retCmp = 0;

	//key是16进制整数,读写的key相同就会访问同一个共享内存 
	key_t key1;
	key_t key2;
	key1 = ftok(".",1);//“."为当前路径 key值由ftok中两个参数确定,与shmr.c中的key值相同,shmid也相同
	key2 = ftok(".",2);
	
	//共享内存 存储空间大小以 兆为单位对齐
	//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
	//1兆,可读可写的权限
	shmid = shmget(key1,1024*1,IPC_CREAT|0666);
	if(shmid == -1){
		printf("shmget fail\n");
		exit(-1);
	}
	//shmat 共享内存映射:将共享内存挂载到进程的存储空间(连接共享内存到当前进程的地址空间)
	//成功返回0,失败返回-1
	//获取的共享内存ID,0为 linux内核为我们自动安排共享内存,0为映射进来的共享内存为可读可写
	//void *shmat(int shmid, const void *shmaddr, int shmflg);
	shmaddr = shmat(shmid,0,0);//参数 0 0默认即可
	 
	//信号量集合中有一个信号量  1为信号量集中信号量的个数
	semid = semget(key2, 1, IPC_CREAT|0666);//获取/创建信号量
	union semun initsem;
	initsem.val = 1;//一开始无锁的状态(无钥匙);若是1,则有锁 
	//0代表 操作第0个信号量 ,只有1个信号量 ,SETVAL是设置信号量的初值
	semctl(semid, 0, SETVAL, initsem);//初始化信号量
                  //SETVAL设置信号量的值,设置为inisem
		
	while(1){
		//初始化作用:
		//(1)避免程序多次运行,shmaddr是同一个内存空间,这样初始值为open/close
		//(2)执行open开盖5s后,客户端再输入open,仍可执行
		pGetKey(semid);
		strcpy(buf, "\0");
		strcpy(shmaddr, "\0");
		vPutBackKey(semid);
		
		strcpy(buf, shmaddr);
		usleep(500000);//每隔0.5s检查
		retCmp = strcmp(buf, shmaddr);//返回值为0 表示buf和shmaddr内容相同
		if(retCmp != 0){//客户端输入了指令 open or close
			if(!strcmp("open", shmaddr)){//open 开盖5s
				openSocket = 1;
				printf("*******cmd open******\n");
			}
			if(!strcmp("close", shmaddr)){//close
				if(openSocket == 1){//在客户端执行open指令前提下(5s内),再执行close
					closeSocket = 1;
					printf("*******cmd close******\n");
				}			
			}//只有以上两种情况,if语句中的表达式 更具可读性,故第二种情况不用else
		}	
	}
	//销毁锁
	semctl(semid,0,IPC_RMID);
	//卸载(断开)共享内存(退出连接):成功返回0,失败返回-1
	shmdt(shmaddr);
	//将共享内存释放:成功返回0,失败返回-1
	shmctl(shmid, IPC_RMID, 0);//保持默认
}



/******************蜂鸣器******************/
void beepInit(){
	pinMode(BEEP, OUTPUT);//设置IO口的输入输出,输出
	digitalWrite(BEEP, HIGH);
}

void beepOn(){
    digitalWrite(BEEP, LOW);  // low输出低电平,蜂鸣器响
}

void beepOff(){
    digitalWrite(BEEP, HIGH);  // high输出高电平,蜂鸣器不响
}

/******************超声波******************/
void ultrasonicInit(){
    pinMode(Trig, OUTPUT);
    pinMode(Echo, INPUT);
}

double getDistance(){
    double dis;
    struct timeval start;
    struct timeval stop;
    //pinMode(Trig, OUTPUT);
    //pinMode(Echo, INPUT);
    digitalWrite(Trig ,LOW);
    usleep(5);
    digitalWrite(Trig ,HIGH);
    usleep(10);
    digitalWrite(Trig ,LOW);

	//超声波未发出前,1号引脚一直处于低电平0,一但发出声波则高电平,跳出循环
    while(!digitalRead(Echo));
    gettimeofday(&start,NULL);
	//超声波回来瞬间,Echo引脚变为低电平,跳出循环
	while(digitalRead(Echo));
    gettimeofday(&stop,NULL);
	
	//秒*10^6转换成微秒
    long diffTime = 1000000*(stop.tv_sec-start.tv_sec)+(stop.tv_usec -start.tv_usec);
    //printf("diffTime = %ld\n",diffTime);
    dis = (double)diffTime/1000000 * 34000 / 2;//单位厘米
    return dis;
}

void *ultrasonic(){
	while(1){
		usleep(500000);//每0.5s计算一次距离
		distance = getDistance();
		//printf("distance = %lf cm\n",distance);
	}
}

/*******************舵机******************/
void signal_handler(int signum)
{
	//curAngle: 1-0° 2-45° 3-90° 4-135°
	if(i <= curAngle){
		digitalWrite(SG90Pin, HIGH);
	}else{
		digitalWrite(SG90Pin, LOW);
	} 
	if(i == 40){
		i = 0;
	} 
	i++;
}

void sg90Init(){
	struct itimerval itv;
	curAngle = 4;//角度初始化 关盖
	prevAngel = 4;
	pinMode(SG90Pin, OUTPUT);
	//设定定时时间,每500um触发一次SIGALRM信号
	itv.it_interval.tv_sec = 0;
	itv.it_interval.tv_usec = 500;
	//设定开始生效,启动定时器的时间
	itv.it_value.tv_sec = 1;
	itv.it_value.tv_usec = 0;
	//设定定时方式
	if( -1 == setitimer(ITIMER_REAL, &itv, NULL)){
		perror("error");
		exit(-1);
	} 
	//信号处理
	signal(SIGALRM, signal_handler);
}

void openLid(){
	curAngle = 1;
	if(curAngle != prevAngel){//条件成立:关盖->开盖
		beepOn();usleep(200000);
		beepOff();
	}
	prevAngel = curAngle;
	usleep(20000);
}

void closeLid(){
	curAngle = 4;
	if(curAngle != prevAngel){//条件成立:开盖->关盖
		beepOn();usleep(200000);
		beepOff();usleep(100000);
		beepOn();usleep(200000);
		beepOff();
	}
	prevAngel = curAngle;
	usleep(20000);
}

void *sg90(){
	while(1){
		j = 5;
		usleep(20000);
		if(distance < 10.0 || openSocket == 1){
			//printf("=====开盖=====\n");
			if(openSocket == 1){//开盖5s
				openLid();
				while(j--){//j=5 相当于sleep(5)
					sleep(1);	
					if(closeSocket == 1){//客户端发送了close指令
						goto closeInterrupt;
					}
				}
				openSocket = 0;			
			}
			else{
				openLid();
				sleep(2);
				openCnt++;
				if(openCnt == 5){//连续开盖到达10秒,报警
					while(distance < 10.0){
						beepOn();
						usleep(200000);
						beepOff();
						usleep(100000);
					}
				}
			}			
		}
		else{
			//printf("=====关盖=====\n");
		closeInterrupt:
			openSocket = 0;
			closeSocket = 0;
			openCnt = 0;
			closeLid();	
		}
	}
}

void deviceInit(){
	// == -1 说明库的初始化失败
    if(wiringPiSetup() == -1){
        fprintf(stderr,"%s","initWringPi error");
        exit(-1);
    }
	
	beepInit();
    ultrasonicInit();
    sg90Init();
	sleep(1);
	/*线程创建成功后,线程ID存放在此
	pthread_t ultrasonicID;
	pthread_t sg90ID;

	wiringPi库下的线程创建
int piThreadCreate (void *(*fn)(void *)){
	pthread_t myThread ;
	return pthread_create (&myThread, NULL, fn, NULL) ;
}
*/	
	int retUltrasonic = piThreadCreate(ultrasonic);	
	int retSg90 = piThreadCreate(sg90);
	int retSocketCmd = piThreadCreate(socketCmd);
}

/***********运行格式 sudo ./server IP地址 端口号************/
int main(int argc, char **argv)
{
	int s_fd;
	int c_fd;
	int n_read;
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	struct Msg msg;
	
	if(argc != 3){
		printf("The number of parameters does not match\n");
		exit(-1);
	}

	deviceInit();
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	//1.socket
	s_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	}
	//将协议类型,IP地址,端口号信息放在结构体重
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));//ASCII 转换成 int
	inet_aton(argv[1],&s_addr.sin_addr);
	
	//2. bind
	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
	
	//3. listen
	listen(s_fd,10);
	
	//4. accept
	int clen = sizeof(struct sockaddr_in);
	
	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
		if(c_fd == -1){
			perror("accept");
		}
		printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
		if(fork() == 0){	
			//子进程 获取共享内存和信号量
			int shmid;
			int semid;
			char *shmaddr;//指向共享内存
			key_t key1;
			key_t key2;
			
			key1 = ftok(".",1);//“."为当前路径 key值由ftok中两个参数确定,与shmr.c中的key值相同,shmid也相同
			key2 = ftok(".",2);
			shmid = shmget(key1,1024*1,0);
			if(shmid == -1){
				printf("shmget fail\n");
				exit(-1);
			}
			shmaddr = shmat(shmid,0,0);//参数 0 0默认即可
			
			semid = semget(key2, 1, IPC_CREAT|0666);	
			union semun initsem;
			initsem.val = 1;//一开始无锁的状态(无钥匙);若是1,则有锁
			semctl(semid, 0, SETVAL, initsem);

			while(1){
				memset(msg.cmd, 0, sizeof(msg.cmd));
				n_read = read(c_fd, &msg, sizeof(msg));	
				if(n_read == 0){
					printf("client out\n");
					break;
				}else if(n_read > 0){
					printf("server get msg:%s\n",msg.cmd);
					//msg_cmd_handler(msg);//指令一来就调用函数
					pGetKey(semid);
					strcpy(shmaddr, msg.cmd);//指令放入共享内存
					vPutBackKey(semid);
				}
			}
			shmdt(shmaddr);
		}
	}
	close(c_fd);
	close(s_fd);
	return 0;
}

2、客户端代码:client.c

#include <stdio.h>
#include <sys/types.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

#define OPEN  0
#define CLOSE 1
#define QUIT 2
#define ERROR 3

struct Msg
{
    int type;
    char cmd[128];
};

int get_cmd_type(char *cmd)
{
	if(!strcmp("open",cmd))    return OPEN;
	if(!strcmp("close",cmd))   return CLOSE;
	if(!strcmp("quit",cmd))   return QUIT;
	return ERROR;
}

void msg_handler(struct Msg msg, int c_fd)
{

	switch(get_cmd_type(msg.cmd)){
		case OPEN:
		case CLOSE:
			write(c_fd,msg,sizeof(msg));;
			break;
		case QUIT:
			exit(1);
		case ERROR:
			printf("wrong cmd\n");
			break;
	}
}

/***********运行格式 sudo ./client IP地址 端口号************/
int main(int argc, char **argv)
{
	int c_fd;
	int n_read;
	struct sockaddr_in c_addr;
	struct Msg msg;

	memset(&c_addr,0,sizeof(struct sockaddr_in));

	if(argc != 3){
		printf("The number of parameters does not match\n");
		exit(-1);
	}

	//1. socket
	c_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(c_fd == -1){
		perror("socket");
		exit(-1);
	}
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);

	//2.connect	
	if(connect(c_fd, (struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(-1);
	}
	
	while(1){
		//子进程 写指令
		if(fork()==0){
			while(1){
				memset(msg.cmd,0,sizeof(msg.cmd));
				msg.type = 1;
				printf(">");
				gets(msg.cmd);//open close alarm
				msg_handler(msg,c_fd);
			}
		}
		
		//父进程读服务端的数据 
		while(1){
		//	memset(msg,0,sizeof(msg));
			n_read = read(c_fd, &msg, sizeof(msg));
			if(n_read == 0){
				printf("server is out,quit\n");
				exit(-1);
			}		
			printf("\n%s\n",msg.cmd);
			//printf("nread=%d\n",n_read);		
		}
	}
	return 0;
}

五、编译和运行

编译服务端和客户端代码:

gcc server.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -o server
gcc client.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -o client

运行服务端和客户端代码:

sudo ./server 127.0.0.1 9999
sudo ./client 127.0.0.1 9999

需要注意的是,开发板与上位机需要同一网段才可通信,在运行程序前先配置好ip地址。

六、视频功能展示

全志H616垃圾桶功能展示

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

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

相关文章

[MySQL]MySQL内置函数

[MySQL]MySQL内置函数 文章目录 [MySQL]MySQL内置函数1. 日期函数2. 字符串函数3. 数学函数4. 其他函数 1. 日期函数 常用日期函数如下&#xff1a; 函数名称描述current_date()获取当前日期current_time()获取当前时间current_timestamp()获取当前时间戳now()获取当前日期时…

Redis_安装配置(2)

目录 一、安装redis 1、安装gcc依赖 2、下载并解压安装包 3、编译 4、安装 5、设置全局命令 二、启动redis 1、前台启动 2、后台启动 2.1 修改redis.conf文件 2.2 使用指定配置启动redis 2.3 kill redis的进程 3、开机启动 三、配置redis 远程访问设置 设置密码…

Magic3D: High-Resolution Text-to-3D Content Creation(高分辨率文本到3d内容创建)

Magic3D: High-Resolution Text-to-3D Content Creation&#xff08;高分辨率文本到3d内容创建&#xff09; Paper&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4738271534435532801&noteId1848084184935912192 Project&#xff1a;https://research.nvidia…

InstructGPT:语言模型的人类反馈指令对齐

论文标题&#xff1a;Training language models to follow instructions with human feedback论文链接&#xff1a;https://arxiv.org/abs/2203.02155论文来源&#xff1a;OpenAI 一、概述 大型语言模型&#xff08;Large language models&#xff0c;LLMs&#xff09;可以通过…

TortoiseGit的安装和使用

1、TortoiseGit的下载安装 安装说明:因为TortoiseGit 只是一个程序壳,必须依赖一个 Git Core,所以安装前请确定已完成git安装和配置。 TortoiseGit下载地址 https://download.tortoisegit.org/tgit/ ,最新稳定版本2.11.0.0。 点进去下载程序包和语言包(非必须),安装时…

【网络】网络基础(一)

目录 一、网络协议初识 1、协议分层 2、OSI七层模型 3、 TCP/IP五层(或四层)模型 4、对网络协议栈的理解 二、网络传输基本流程 1、网络传输流程图 1.1、同一个网段内的两台主机进行文件传输 1.2、跨网段的主机的文件传输 三、数据包封装和分用 四、网络中的地址管理…

配置微服务负载均衡的两种方式

说明&#xff1a;在微服务开发中&#xff0c;每个服务是通过服务名称来区分的&#xff0c;当两个微服务名称相同时&#xff0c;注册中心会视为这两个服务是相同的。配置对应的负载均衡策略&#xff0c;当其他服务发送请求过来的时&#xff0c;可以对这两个微服务进行规则访问。…

Linux的起源

UNIX 与 Linux 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中&#xff0c;UNIX 诞生于 20 世纪 60 年代末&#xff0c;Windows 诞生于 20 世纪 80 年代中期&#xff0c;Linux 诞生于 20 世纪 90 年代初&#xff0c;可以说 UNIX 是操作系统中的"老大哥&…

Prompt本质解密及Evaluation实战与源码解析(二)

9.4 Evaluation for Agents源码解析 如图9-4所示,转过来,我们再看一下LangChain框架代理评估(Evaluation for Agents)的源代码。 图9- 4 LangChain的evaluation agents目录 在trajectory_eval_prompt.py文件里面,写了一个非常经典的提示词。 1. """提示…

抽象轻松有点使用的JavaScript

数据类型转换 定义概念&#xff1a;将一种数据类型通过用特定的方法转换成另一种数据类型 拆分&#xff1a; 数据类型转换 A1&#xff08;一种数据类型&#xff09; A2&#xff08;方法&#xff09; A3&#xff08;转换成另一种数据类型&#xff09; 理解&#xff1a;A1 &…

使用STM32 再实现电动车防盗钥匙扣

实现目标 1. 点击遥控器 A 按键&#xff0c;系统进入警戒模式&#xff0c;一旦检测到震动&#xff08;小偷偷车&#xff09;&#xff0c;则喇叭发出声响报警 2. 点击遥控器 B 按键&#xff0c;系统退出警戒模式&#xff0c;再怎么摇晃系统都不会报警 硬件介绍 1. 震动传感器…

安装orcle报错:指定的 Oracle 系统标识符 (SID) 已在使用

安装orcle报错&#xff1a;[INS-35075]指定的 Oracle 系统标识符 (SID) 已在使用 说明前面的orcle没有彻底删除 解决这个问题&#xff1a; 搜索框 —— > 输入&#xff1a;regedit ——> 回车 运行regedit&#xff0c;选择HKEY_LOCAL_MACHINE SOFTWARE ORACLE&#xff…

【Android】从零搭建组件化项目

组件化系列文章介绍的内容稍微多了点&#xff0c;本着研究透这玩意的精神&#xff0c;从组件化的简介开始说起。 目录 简介组件化、模块化与插件化开始创建配置共享文件打包模式配置APT与JavaPoet 简介 什么是组件化&#xff1f; 将多个功能模板拆分、重组的过程。 为什么要使…

GEE入门学习,遥感云大数据分析、管理与可视化以及在林业应用丨灾害、水体与湿地领域应用丨GPT模型应用

目录 ①海量遥感数据处理与GEE云计算技术实践应用 ②GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例实践应用 ③GEE遥感云大数据林业应用典型案例实践及GPT模型应用 ④遥感云大数据在灾害、水体与湿地领域典型案例实践及GPT模型应用 ①海量遥感…

78. 左旋转字符串

目录 链接&#xff1a; 题目&#xff1a; 思路&#xff1a; 代码&#xff1a; 图片&#xff1a; 链接&#xff1a; 原题链接 题目&#xff1a; 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。 请定义一个函数实现字符串左旋转操作的功能。 比如输入字…

Talk预告 | 天津大学博士生赵煜:从平面图像中理解空间语义 - 视觉空间位置描述

本期为TechBeat人工智能社区第512期线上Talk&#xff01; 北京时间7月12日(周三)20:00&#xff0c; 天津大学博士生—赵煜的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “从平面图像中理解空间语义-视觉空间位置描述”&#xff0c;届时将与大家…

webpack插件安装

webpack插件安装 1、html-webpack-plugin插件2 、css-loader和style-loader插件3、less-load插件 1、html-webpack-plugin插件 1、下载插件 yarn add html-webpack-plugin -D2、webpack.config.js添加配置 *const HtmlWebpackPlugin require(html-webpack-plugin); const p…

Linux系统编程(信号处理机制)

文章目录 前言一、中断&#xff0c;异常&#xff0c;信号的区别二、信号在Linux中的标识三、信号处理相关函数四、代码实验总结 前言 本篇文章我们来讲解信号的处理机制&#xff0c;信号处理在Linux操作系统中必不可少&#xff0c;这一点值得大家注意&#xff0c;信号又会与中…

打通前后端 -- 创建第一个JSP页面

前言 JSP是由Sun Microsystems公司主导创建的一种动态网页技术标准。 JSP部署于网络服务器上&#xff0c;可以响应客户端发送的请求&#xff0c;并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页&#xff0c;然后返回给请求者。 JSP技术以Java语言作为脚本语言&…

从0-100:约拍小程序开发笔记

背景 摄影师预约小程序功能是一种方便摄影师和客户之间进行预约和安排拍摄的工具。通过该功能&#xff0c;摄影师可以在小程序上设置自己的可预约时间&#xff0c;客户可以根据摄影师的日程安排选择合适的时间进行预约。这样可以提高预约的效率&#xff0c;减少沟通成本&#…