아두이노 IDE code
//TRIG, ECHO PIN 정의//
const int TRIG = 2;
const int ECHO = 3;
//평균 이동 필터의 Buffer Size 정의//
//변수 정의//
float duration;
void setup(){
Serial.begin(9600); //시리얼 통신 설정
Serial.flush(); //시리얼 통신 후 다시 시리얼 통신을 시작할 때, 들어오거나 남아있던 값을 제거
//핀 모드 설정
pinMode(TRIG,OUTPUT);
pinMode(ECHO,INPUT);
digitalWrite(TRIG,LOW);
}
void loop(){
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
duration = pulseIn(ECHO, HIGH);
Serial.println(duration);
delay(1000);
}
Python code
# pip install pySerial
import serial
arduino = serial.Serial('COM8', 9600)
class Student():
def __init__(self):
self.z=2
print("z init as :", self.z)
a = list()
duration = 0
distance = 0
cm_distance = 0
new_cm_distance = 0
pre_distance = 0
sum_of_buffer = 0
new_duration = 0
alpha = 0.3
beta = 0.01
cnt = 0
s = 1 # 0.1
cnt_hit = 0
h = 1 # 0.1
outCount = 0
human_sig = False
for i in range(10):
duration = float(arduino.readline().decode()[:-1])
cm_distance = ((duration * 340) / 10000) / 2
a.append(cm_distance)
if cm_distance > 280:
cm_distance = 280
elif cm_distance < 2:
cm_distance = 2
sum_of_buffer += a[i]
pre_distance = sum_of_buffer / len(a)
while 1:
new_duration = float(arduino.readline().decode()[:-1])
new_cm_distance = ((new_duration * 340) / 10000) / 2
if new_cm_distance < 190:
distance = alpha * pre_distance + (1-alpha) * new_cm_distance
elif new_cm_distance < 280:
distance = (1-alpha) * pre_distance + alpha * new_cm_distance
else:
distance = (1-beta) * pre_distance + beta * new_cm_distance
if distance > 280:
distance = 280
elif distance < 2:
distance = 2
print("distance(cm) :", int(distance), end=' ')
cnt += s
times = 100000 - cnt * 930 # 300000 - cnt * 930
print("Sample Number :", cnt, end=' ')
if distance <= 150:
cnt_hit += h
print("Human Cnt_Number :", cnt_hit, end=' ')
if cnt_hit < 16.10:
if times <= 60000: #120000
# delay(120000) 고정된 2분
cnt = 0
# 시간을 뽑아 실시간 기록하거나 찾을 필요없이 현회차와 전회차 비교해서 오차 확인 가능
cnt_hit = 0
times = 0
outCount += 1
else:
human_sig = True
print("Human_Signal :", human_sig)
# delay(times) 측정되는 3분 중에 남는 시간 + 2분
human_sig = False
cnt = 0
# 시간을 뽑아 엑셀이든 txt파일 데이터가 실시간으로 밀리지 않는지 확인하지 위해서
cnt_hit = 0
times = 0
outCount = 0
pre_distance = distance
print("outCount :", outCount)
# outCount 18cycle일때 경고 메세지, 24cycle일땐 out
# 가방이나 물건 올려놨을때 움직임 없을때 확인하는 로직 현회차와 전회차 비교해서 오차 확인 가능
# 실시간 실내/실외 온도나 습도에 따른 파라메타 변화 적용 or not
20210707 Ver2.5
Arduino IDE code
const int ECHO1=13; // mini_size 3
const int TRIG1=12; // mini_size 2
const int ECHO2=9; // mini_size 5
const int TRIG2=8; // mini_size 4
const int ECHO3=3; // mini_size 7
const int TRIG3=2; // mini_size 6
#define SIZE 20 // Buffer of setting Moving average filter
#define DIST 280*58.8 // mini_size 50*58.8
int buffer1[SIZE];
int buffer2[SIZE];
int buffer3[SIZE];
const int maxdist = 280; //mini_size 50
const int mindist = 2; //mini_size 2
double kalman1(double U1);
double kalman2(double U2);
double kalman3(double U3);
double tmp1 = 200; //tmp_value for invalid, mini_size 32
double tmp2 = 200; //tmp_value for invalid, mini_size 32
double tmp3 = 200; //tmp_value for invalid, mini_size 32
long Cm1, Cm2, Cm3;
long duration1, duration2, duration3;
float sum1 = 0;
float sum2 = 0;
float sum3 = 0;
float distance1, distance2, distance3;
char ch;
double kalman1(double U1){
static const double R1 = 40;
static const double H1 = 1.00;
static double Q1 = 10;
static double P1 = 0;
static double U_hat1 = 170; // mini_size 20
static double K1 = 0;
K1 = P1*H1/(H1*P1*H1+R1);
U_hat1 += + K1*(U1-H1*U_hat1);
P1 = (1-K1*H1)*P1+Q1;
return U_hat1;
}
double kalman2(double U2){
static const double R2 = 40;
static const double H2 = 1.00;
static double Q2 = 10;
static double P2 = 0;
static double U_hat2 = 170; // mini_size 20
static double K2 = 0;
K2 = P2*H2/(H2*P2*H2+R2);
U_hat2 += + K2*(U2-H2*U_hat2);
P2 = (1-K2*H2)*P2+Q2;
return U_hat2;
}
double kalman3(double U3){
static const double R3 = 40;
static const double H3 = 1.00;
static double Q3 = 10;
static double P3 = 0;
static double U_hat3 = 170; // mini_size 20
static double K3 = 0;
K3 = P3*H3/(H3*P3*H3+R3);
U_hat3 += + K3*(U3-H3*U_hat3);
P3 = (1-K3*H3)*P3+Q3;
return U_hat3;
}
void setup() {
Serial.begin(9600);
Serial.flush();
pinMode(TRIG1,OUTPUT);
pinMode(ECHO1,INPUT);
digitalWrite(TRIG1,LOW);
pinMode(TRIG2,OUTPUT);
pinMode(ECHO2,INPUT);
digitalWrite(TRIG2,LOW);
pinMode(TRIG3,OUTPUT);
pinMode(ECHO3,INPUT);
digitalWrite(TRIG3,LOW);
for(int i=0; i<=SIZE-1; i++)
{
digitalWrite(TRIG1,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG1,LOW);
duration1=pulseIn(ECHO1,HIGH);
Cm1=duration1*0.034/2;
if(Cm1>maxdist) Cm1=maxdist;
else if(Cm1<mindist) Cm1=mindist;
buffer1[i]=Cm1;
sum1+=buffer1[i];
}
for(int i=0; i<=SIZE-1; i++)
{
digitalWrite(TRIG2,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG2,LOW);
duration2=pulseIn(ECHO2,HIGH);
Cm2=duration2*0.034/2;
if(Cm2>maxdist) Cm2=maxdist;
else if(Cm2<mindist) Cm2=mindist;
buffer2[i]=Cm2;
sum2+=buffer2[i];
}
for(int i=0; i<=SIZE-1; i++)
{
digitalWrite(TRIG3,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG3,LOW);
duration3=pulseIn(ECHO3,HIGH);
Cm3=duration3*0.034/2;
if(Cm3>maxdist) Cm3=maxdist;
else if(Cm3<mindist) Cm3=mindist;
buffer3[i]=Cm3;
sum3+=buffer3[i];
}
Serial.flush();
}
void loop() {
if(Serial.available() >0){
ch = Serial.read();
if(ch == 'a') delay(1000); // Break time
}
digitalWrite(TRIG1,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG1,LOW);
duration1=pulseIn(ECHO1,HIGH, DIST); // invaild value(280cm 이상) 1차 예외처리(timeout 조절)
Cm1=duration1*0.034/2;
if(Cm1==0) Cm1 = tmp1; // 1차 예외처리 된 data 직전 typical data 사용
else{
if(Cm1>maxdist) Cm1=maxdist; // extreme case 2차 예외처리(distance limiting)
else if(Cm1<mindist) Cm1=mindist;
tmp1 = Cm1;
}
sum1-=buffer1[0];
for(int i=0; i<SIZE-1; i++)
{
buffer1[i]=buffer1[i+1];
}
buffer1[SIZE-1]=Cm1;
sum1+=buffer1[SIZE-1];
if(Cm1 > 150){ // mini_size 22
distance1=sum1/SIZE; // Moving average filtered distance 사용
}
else{
distance1 = kalman1(Cm1); // Kalman filtered distance 사용
}
delay(50);
/////////////////////////////////////////////////////////////////////////////////////
digitalWrite(TRIG2,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG2,LOW);
duration2=pulseIn(ECHO2,HIGH,DIST); // invaild value(280cm 이상) 1차 예외처리(timeout 조절)
Cm2=duration2*0.034/2;
if(Cm2==0) Cm2 = tmp2; // 1차 예외처리 된 data 직전 typical data 사용
else{
if(Cm2>maxdist) Cm2=maxdist; // extreme case 2차 예외처리(distance limiting)
else if(Cm2<mindist) Cm2=mindist;
tmp2 = Cm2;
}
sum2-=buffer2[0];
for(int i=0; i<SIZE-1; i++)
{
buffer2[i]=buffer2[i+1];
}
buffer2[SIZE-1]=Cm2;
sum2+=buffer2[SIZE-1];
if(Cm2 > 150){ // mini_size 22
distance2=sum2/SIZE; // Moving average filtered distance 사용
}
else{
distance2 = kalman2(Cm2); // Kalman filtered distance 사용
}
delay(50);
///////////////////////////////////////////////////////////////////////////////
digitalWrite(TRIG3,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG3,LOW);
duration3=pulseIn(ECHO3,HIGH, DIST); // invaild value(280cm 이상) 1차 예외처리(timeout 조절)
Cm3=duration3*0.034/2;
if(Cm3==0) Cm3 = tmp3; // 1차 예외처리 된 data 직전 typical data 사용
else{
if(Cm3>maxdist) Cm3=maxdist; // extreme case 2차 예외처리(distance limiting)
else if(Cm3<mindist) Cm3=mindist;
tmp3 = Cm3;
}
sum3-=buffer3[0];
for(int i=0; i<SIZE-1; i++)
{
buffer3[i]=buffer3[i+1];
}
buffer3[SIZE-1]=Cm3;
sum3+=buffer3[SIZE-1];
if(Cm3 > 150){ // mini_size 22
distance3=sum3/SIZE; // Moving average filtered distance 사용
}
else{ // 3차 칼만필터 filtering 구간
distance3 = kalman3(Cm3); // Kalman filtered distance 사용
}
delay(50);
//Serial.print(distance1); Serial.print(" "); Serial.println(Cm1);// Serial.print(" "); Serial.println(distance3);
Serial.print(distance1); Serial.print(' '); Serial.print(distance2); Serial.print(' '); Serial.println(distance3);
delay(50);
}
Python code (Server)
import serial
import time
select_comport = input('select port:')
arduino = serial.Serial(select_comport, 9600)
sample_cnt = 200
human_exist_dist = 150 # 22
yellow_card = 45
red_card = 60
sec = 1
line_list = list()
desk_list = list()
full_cycle_time = 60
with open("number_of_student.txt", "r") as f:
line_tmp = f.readlines()
max_desk_cnt = len(line_tmp)
cnt = max_desk_cnt * sample_cnt
class Desk:
def __init__(self):
self.distance = 0
self.hit_cnt = 0
self.out_cnt = 0
self.flag = False
self.stu_phone = ""
def print_desk_info(self):
print(self.stu_phone, "(Dist:", int(self.distance), "Hit:", self.hit_cnt, "Out:", self.out_cnt, end=')\t\t\t\t')
def read_distance():
if arduino.readable():
try:
tmp_distance = arduino.readline().decode().split(' ')
tmp_distance = [float(e) for e in tmp_distance]
for m in range(max_desk_cnt - len(tmp_distance)):
tmp_distance.append(0)
return tmp_distance
except ValueError:
return [0]
# Initialize
for i in range(max_desk_cnt):
desk_list.append(Desk())
first_time = time.time() # real time checking
# Loop
while True:
try:
# 실시간으로 number_of_student value 들이 kiosk 에 의해 변했는지 확인
with open("number_of_student.txt", "r") as f:
line_list = f.readlines()
for i in range(len(line_list)):
temp = desk_list[i]
temp.stu_phone = line_list[i][:-1]
if temp.stu_phone == "0": # 학생 번호가 있어야하는 위치에 0이 있으면 객체 데이터 초기화
temp.flag = False
temp.out_cnt = 0
temp.hit_cnt = 0
else: # 학생 번호가 입장되었으면 flag value on (distance 에 따른 logic 실행)
temp.flag = True
dist_list = read_distance() # 아두이노를 통해 들어온 데이터 1차 정제
if len(dist_list) == max_desk_cnt:
for i in range(max_desk_cnt): # 각 센서의 데이터별로 처리 시작
tmp_desk = desk_list[i]
tmp_desk.distance = dist_list[i]
cnt -= sec
print(cnt, end=': ')
if cnt == -sec:
cnt = max_desk_cnt * sample_cnt # break time 이후 cnt 값 초기화
cycle_time = time.time() - first_time
print("BREAK")
if cycle_time < full_cycle_time:
time.sleep(full_cycle_time - cycle_time)
arduino.read_all()
with open("data_list.txt", "a") as f: # cycle time 기록
f.write(str(cycle_time) + ";")
for j in desk_list:
f.write(str(j.hit_cnt) + ";")
f.write("\n")
for j in desk_list: # break time 이후 cnt_hit 초기화
j.hit_cnt = 0
first_time = time.time()
break
if tmp_desk.flag == True: # flag 가 1일때, 즉 사람이 입장했을때 실행
if 0 < tmp_desk.distance <= human_exist_dist: # 사람이 있다는 거리
tmp_desk.hit_cnt += sec
if cnt < max_desk_cnt: # break 전 마지막 루프에서 cnt_hit 값에 따른 결과 처리
if tmp_desk.hit_cnt <= sample_cnt * 0.2: # sensing time 의 20%, 사람이 일정이상 앉아 있지 않을 때
tmp_desk.out_cnt += 1
if tmp_desk.out_cnt == red_card: # 퇴장 처리
with open("sign_list.txt", "a") as f: # 퇴장 메세지 전송
f.write(tmp_desk.stu_phone + "Red" + "\n")
with open("out_student.txt", "a") as f: # kiosk 에게 퇴장한 사람 데이터 전송
out_student_list = [str(i), "'", tmp_desk.stu_phone, "'", str(time.time()), "'",
str(time.strftime('%c', time.localtime(time.time()))), "\n"]
f.writelines(out_student_list)
line_list[i] = "0\n" # number_of_student 에서 받아들였던 데이터 수정
with open("number_of_student.txt", "w") as f: # 수정된 number_of_student 데이터 전송
for line in line_list:
f.write(line)
tmp_desk.stu_phone = "0" # 객체 데이터 수정
tmp_desk.flag = False
tmp_desk.out_cnt = 0
tmp_desk.hit_cnt = 0
elif tmp_desk.out_cnt == yellow_card: # 경고 처리
with open("sign_list.txt", "a") as f: # 경고 메세지 전송
f.write(tmp_desk.stu_phone + "Yellow" + '\n')
else: # 사람이 일정이상 앉아 있을 때
tmp_desk.out_cnt = 0
tmp_desk.print_desk_info()
print()
except IOError:
pass
Python code (Kiosk)
import os
import time
from openpyxl import load_workbook
data_list = list()
with open("number_of_student.txt", "r") as f:
line_ori = f.readlines()
num_of_data = len(line_ori)
def read_data():
with open("number_of_student.txt", "r") as p:
line_ori_tmp = p.readlines()
line_tmp = [line_ori_tmp[x][:-1] for x in range(num_of_data)]
with open("out_student.txt", "r") as s:
line_stu = s.readlines()
if line_stu != "":
for x in range(len(line_stu)):
line_stu_tmp = line_stu[x].split("'")
for data_tmp in data_list:
if (data_tmp[0] == int(line_stu_tmp[0])) & (data_tmp[1] == line_stu_tmp[1]):
data_tmp[2] = int(-(float(line_stu_tmp[2]) - data_tmp[2]))
data_tmp.append(line_stu_tmp[3][:-1])
print(data_tmp) # 엑셀에 프린트
wb_tmp = load_workbook('using_list.xlsx')
ws_tmp = wb_tmp['Sheet1']
ws_tmp.append(data_tmp)
wb_tmp.save('using_list.xlsx')
data_list.remove(data_tmp)
with open("out_student.txt", "w") as s:
s.write("")
return line_tmp
while True:
enter_or_exit = input("입장 / 퇴장 : ")
line = read_data()
if enter_or_exit == "입장":
print("사용 가능한 좌석 : ")
for i in range(num_of_data):
if line[i] == '0':
print(i+1, "번", end=" ")
print()
num_sit = input("입장할 좌석번호 / 나가기(0) : ")[:1]
line = read_data()
if num_sit.isnumeric() == 1:
num_sit = int(num_sit) - 1
if 0 <= num_sit < num_of_data:
if line[num_sit] == '0':
num_student = input("사용자의 핸드폰 번호 : ")
line = read_data()
line[num_sit] = num_student
if (len(num_student) == 11) & (num_student.isnumeric() == 1) & (num_student[:2] == "01"):
data_list.append([num_sit, num_student, time.time(), time.strftime('%c', time.localtime(time.time()))])
with open("number_of_student.txt", "w") as f:
for j in range(num_of_data):
f.write(line[j]+'\n')
print("회원님의 자리는", num_sit + 1, "번 입니다.")
else:
print("잘못된 입력입니다.")
else:
print("잘못된 좌석을 지정하였습니다.")
else:
print("잘못된 좌석을 지정하였습니다.")
else:
print("잘못된 좌석을 지정하였습니다.")
print("메인화면으로 돌아갑니다.")
input("아무버튼이나 눌러주세요.")
os.system('cls')
elif enter_or_exit == "퇴장":
num_sit = input("퇴장할 좌석번호 / 나가기(0) : ")[:1]
line = read_data()
if num_sit.isnumeric() == 1:
num_sit = int(num_sit) - 1
if 0 <= num_sit < num_of_data:
if line[num_sit] != '0':
num_student = input("사용자의 핸드폰 번호 : ")
if (len(num_student) == 11) & (num_student.isnumeric() == 1):
with open("number_of_student.txt", "r") as f:
line_ori = f.readlines()
line = [line_ori[x][:-1] for x in range(num_of_data)]
for data in data_list:
if (num_sit == data[0]) & (num_student == data[1]):
data[2] = int(time.time() - data[2])
# data[2] = data[2] // 60
data.append(time.strftime('%c', time.localtime(time.time())))
line_ori[num_sit] = "0\n"
with open("number_of_student.txt", "w") as f:
for j in line_ori:
f.write(j)
print(data) # 엑셀에 프린트
wb = load_workbook('using_list.xlsx')
ws = wb['Sheet1']
ws.append(data)
wb.save('using_list.xlsx')
data_list.remove(data)
print(num_sit + 1, "번 자리가 퇴장처리 되었습니다. 이용해주셔서 감사합니다.")
break
else:
print("올바르지 않은 번호입니다.")
else:
print("잘못된 입력입니다.")
else:
print("잘못된 좌석을 지정하였습니다.")
else:
print("잘못된 좌석을 지정하였습니다.")
print("메인화면으로 돌아갑니다.")
input("아무버튼이나 눌러주세요.")
os.system('cls')
else:
print("잘못된 입력입니다.")
print("메인화면으로 돌아갑니다.")
input("아무버튼이나 눌러주세요.")
os.system('cls')
Python code (Message)
import requests
import time
while 1:
try:
with open("sign_list.txt", "r") as f:
signs = f.readlines()
num_of_sign = len(signs)
for i in range(num_of_sign):
a = signs[i][:11] + " 고객님께 보내는 알림입니다.\n"
if signs[i][11:-1] == "Yellow":
b = "경고 알림: 15분 동안 자리를 비우셨습니다.\n25분 동안 자리를 비울시 퇴장 처리됩니다."
else:
b = "퇴장 알림: 25동안 자리를 비우셨습니다.\n좌석이 퇴장 처리됩니다."
try:
TARGET_URL = 'https://notify-api.line.me/api/notify'
TOKEN = 'i8DYbNCp5k6B6m7QP4kfiLYzvJZNzQX27cMwd5E8Jo5' # 발급받은 토큰
headers = {'Authorization': 'Bearer ' + TOKEN}
data = {'message': a+b}
response = requests.post(TARGET_URL, headers=headers, data=data)
except Exception as ex:
print(ex)
if num_of_sign != 0:
with open("sign_list.txt", "w") as f:
f.write("")
except IOError:
pass
time.sleep(1)