

void setup() {
// 초기화 코드: 한 번만 실행
pinMode(13, OUTPUT);
}
void loop() {
// 반복 실행 코드
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}

"프로그래밍은 단순히 코드를 작성하는 것이 아니라, 논리적 사고를 통해 문제를 해결하는 과정입니다. 오늘 배운 기초 개념들은 앞으로 로봇을 제어하는 모든 과정의 토대가 됩니다."
// 모터 핀 정의
const int ENA = 10;
const int IN1 = 9;
const int IN2 = 8;
const int ENB = 5;
const int IN3 = 7;
const int IN4 = 6;
void setup() {
// 모터 제어 핀을 출력으로 설정
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
}
void loop() {
// 앞으로 이동
forward(150);
delay(2000);
// 정지
stop();
delay(1000);
// 뒤로 이동
backward(150);
delay(2000);
// 좌회전
left(150);
delay(1000);
// 우회전
right(150);
delay(1000);
}
void forward(int speed) {
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
void backward(int speed) {
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
void left(int speed) {
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
void right(int speed) {
analogWrite(ENA, speed);
analogWrite(ENB, speed);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
// 센서 핀 정의
const int sensorPin[3] = {A0, A1, A2};
int sensorValue[3] = {0, 0, 0};
int threshold = 500; // 임계값
void setup() {
Serial.begin(9600);
// 센서 핀을 입력으로 설정
for(int i=0; i<3; i++) {
pinMode(sensorPin[i], INPUT);
}
}
void loop() {
// 센서값 읽기
for(int i=0; i<3; i++) {
sensorValue[i] = analogRead(sensorPin[i]);
Serial.print(sensorValue[i]);
Serial.print("\t");
}
Serial.println();
delay(100);
}
void followLine() {
// 센서값 읽기
for(int i=0; i<3; i++) {
sensorValue[i] = analogRead(sensorPin[i]);
}
// 라인 감지 여부 확인 (threshold 기준)
bool leftOnLine = (sensorValue[0] < threshold);
bool centerOnLine = (sensorValue[1] < threshold);
bool rightOnLine = (sensorValue[2] < threshold);
// 라인 위치에 따른 동작 결정
if(leftOnLine && !centerOnLine && !rightOnLine) {
// 라인이 왼쪽에 있음
left(150);
}
else if(!leftOnLine && centerOnLine && !rightOnLine) {
// 라인이 중앙에 있음
forward(200);
}
else if(!leftOnLine && !centerOnLine && rightOnLine) {
// 라인이 오른쪽에 있음
right(150);
}
else if(!leftOnLine && !centerOnLine && !rightOnLine) {
// 라인을 찾지 못함
searchLine();
}
else if(leftOnLine && centerOnLine && rightOnLine) {
// 모든 센서가 라인 위에 있음 (교차로 등)
forward(100); // 속도를 줄이고 진행
}
}
void searchLine() {
// 라인을 잃었을 때 찾는 알고리즘
// 간단한 구현: 제자리에서 회전
stop();
delay(100);
left(120);
delay(300);
}
// 초음파 센서 핀 정의
const int trigPin = 12;
const int echoPin = 13;
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
// 초음파 센서로 거리 측정 함수
float getDistance() {
// 초음파 발사 전 Trig 핀 초기화
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// 10μs 동안 Trig 핀에 HIGH 신호 인가
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Echo 핀의 HIGH 상태 지속 시간 측정
long duration = pulseIn(echoPin, HIGH);
// 거리 계산 (cm)
// 음속: 340m/s = 0.034cm/μs
// 왕복이므로 나누기 2
float distance = duration * 0.034 / 2;
return distance;
}// 안전 거리 설정 (cm)
const float safeDistance = 20.0;
void loop() {
// 현재 전방 거리 측정
float currentDistance = getDistance();
// 장애물 감지 및 회피
if(currentDistance < safeDistance) {
// 장애물 감지, 회피 동작 실행
avoidObstacle();
} else {
// 안전 거리 확보, 정상 주행 계속
followLine(); // 라인 트레이싱 함수 호출
}
delay(100); // 측정 간격
}
void avoidObstacle() {
// 장애물 감지 시 회피 알고리즘
// 1. 일시 정지
stop();
delay(500);
// 2. 회피 방향 결정 (여기서는 무작위로 선택)
bool turnRight = random(2); // 0 또는 1 반환
// 3. 회피 기동 실행
if(turnRight) {
// 우회전으로 회피
right(150);
delay(700); // 약 90도 회전
forward(150);
delay(1000); // 장애물 옆으로 이동
left(150);
delay(700); // 다시 원래 방향으로
} else {
// 좌회전으로 회피
left(150);
delay(700); // 약 90도 회전
forward(150);
delay(1000); // 장애물 옆으로 이동
right(150);
delay(700); // 다시 원래 방향으로
}
// 4. 정상 주행 재개
forward(150);
}
#include
// 소프트웨어 시리얼 핀 설정
// 하드웨어 시리얼(0,1)은 업로드 시 충돌 가능성
const int BT_RX = 2; // 아두이노 D2 핀
const int BT_TX = 3; // 아두이노 D3 핀
// 소프트웨어 시리얼 객체 생성
SoftwareSerial BTSerial(BT_RX, BT_TX);
void setup() {
Serial.begin(9600); // PC와 통신용
BTSerial.begin(9600); // 블루투스 통신용
Serial.println("블루투스 통신 테스트");
}
void loop() {
// 블루투스로 받은 데이터를 PC로 전송
if (BTSerial.available()) {
char data = BTSerial.read();
Serial.write(data);
}
// PC에서 받은 데이터를 블루투스로 전송
if (Serial.available()) {
char data = Serial.read();
BTSerial.write(data);
}
}#include
// 소프트웨어 시리얼 설정
SoftwareSerial BTSerial(2, 3); // RX, TX
// 모터 제어 핀 (이전 차시와 동일)
// 변수 및 함수 정의는 생략...
// 주행 모드 변수
bool autoMode = false; // 기본값은 수동 모드
void setup() {
// 모터 핀 설정
// 센서 핀 설정
// 이전 차시와 동일...
// 블루투스 통신 시작
BTSerial.begin(9600);
}
void loop() {
// 블루투스 명령 확인
if (BTSerial.available()) {
char command = BTSerial.read();
executeCommand(command);
}
// 자율주행 모드일 경우 센서 기반 주행 실행
if (autoMode) {
// 장애물 감지
float distance = getDistance();
if (distance < safeDistance) {
// 장애물 회피
avoidObstacle();
} else {
// 라인 트레이싱
followLine();
}
}
delay(50);
}
// 블루투스 명령 실행 함수
void executeCommand(char command) {
switch(command) {
case 'F': // 전진
forward(200);
break;
case 'B': // 후진
backward(200);
break;
case 'L': // 좌회전
left(150);
break;
case 'R': // 우회전
right(150);
break;
case 'S': // 정지
stop();
break;
case 'A': // 자율주행 모드
autoMode = true;
BTSerial.println("Auto mode activated");
break;
case 'M': // 수동 제어 모드
autoMode = false;
stop(); // 안전을 위해 정지
BTSerial.println("Manual mode activated");
break;
}
}
// 명령 수신 후 응답 전송
BTSerial.print("Received: ");
BTSerial.println(command);
// 주기적으로 상태 정보 전송
if (millis() - lastStatusTime > 1000) { // 1초마다
BTSerial.print("Distance: ");
BTSerial.print(getDistance());
BTSerial.print("cm, Mode: ");
BTSerial.println(autoMode ? "Auto" : "Manual");
lastStatusTime = millis();
}
# 기본 TensorFlow 설치
pip install tensorflow
# 추가 유용한 패키지들
pip install matplotlib
pip install pandas
pip install scikit-learn
pip install pillow
import tensorflow as tf
print(tf.__version__)
import tensorflow as tf
from tensorflow.keras import layers, models
import matplotlib.pyplot as plt
import numpy as np
# MNIST 데이터셋 로드
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
# 데이터 전처리: 정규화 (0-1 범위로 스케일링)
train_images = train_images / 255.0
test_images = test_images / 255.0
# 모델에 입력하기 위해 이미지 차원 확장 (28x28 -> 28x28x1)
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1)
# 간단한 CNN 모델 구성
model = models.Sequential([
# 첫 번째 합성곱 층
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
# 두 번째 합성곱 층
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
# 세 번째 합성곱 층
layers.Conv2D(64, (3, 3), activation='relu'),
# 완전 연결 층을 위한 펼치기
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dense(10, activation='softmax') # 10개 클래스 (0-9)
])
# 모델 컴파일
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 모델 구조 요약
model.summary()
# 모델 학습
history = model.fit(train_images, train_labels, epochs=5,
validation_data=(test_images, test_labels))
# 모델 평가
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f'테스트 정확도: {test_acc:.3f}')
# 학습 과정 시각화
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# 몇 가지 테스트 이미지에 대한 예측 결과 확인
predictions = model.predict(test_images[:5])
for i in range(5):
plt.figure(figsize=(6, 3))
plt.subplot(1, 2, 1)
plt.imshow(test_images[i].reshape(28, 28), cmap='gray')
plt.title(f"실제: {test_labels[i]}")
plt.subplot(1, 2, 2)
plt.bar(range(10), predictions[i])
plt.xticks(range(10))
plt.title(f"예측: {np.argmax(predictions[i])}")
plt.show()
import cv2
import os
import time
def capture_images(class_name, output_folder, num_images=50):
# 출력 폴더 생성
class_folder = os.path.join(output_folder, class_name)
os.makedirs(class_folder, exist_ok=True)
# 카메라 열기
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("카메라를 열 수 없습니다.")
return
print(f"{class_name} 클래스의 이미지 캡처를 시작합니다.")
print("준비되면 아무 키나 누르세요...")
# 카메라 미리보기 표시
while True:
ret, frame = cap.read()
if not ret:
break
cv2.putText(frame, f"Press any key to start capturing {class_name}",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.imshow("Camera Preview", frame)
# 키 입력 대기
if cv2.waitKey(1) != -1:
break
# 이미지 캡처 시작
count = 0
while count < num_images:
ret, frame = cap.read()
if not ret:
break
# 캡처 화면에 정보 표시
cv2.putText(frame, f"Capturing {class_name}: {count+1}/{num_images}",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.imshow("Camera Preview", frame)
# 이미지 저장
filename = os.path.join(class_folder, f"{class_name}_{count:04d}.jpg")
cv2.imwrite(filename, frame)
print(f"저장됨: {filename}")
count += 1
time.sleep(0.2) # 약간의 지연으로 다양한 이미지 캡처
# ESC 키를 누르면 중단
if cv2.waitKey(1) == 27:
break
# 자원 해제
cap.release()
cv2.destroyAllWindows()
print(f"{class_name} 클래스의 이미지 캡처가 완료되었습니다.")
def main():
# 데이터셋 저장 경로
output_folder = "dataset"
os.makedirs(output_folder, exist_ok=True)
# 캡처할 클래스 목록 (예: 병, 컵, 책)
classes = ["bottle", "cup", "book"]
# 각 클래스별로 이미지 캡처
for class_name in classes:
capture_images(class_name, output_folder, num_images=50)
print("다음 클래스로 넘어가기 전에 객체를 바꿔주세요.")
input("준비되면 Enter 키를 누르세요...")
if __name__ == "__main__":
main()
# pip로 설치
pip install labelImg
# 실행
labelImg
dataset
bottle_0001.jpg
/path/to/dataset/bottle_0001.jpg
640
480
3
import os
import cv2
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
def preprocess_images(input_folder, output_folder, target_size=(224, 224), grayscale=False):
# 출력 폴더 생성
os.makedirs(output_folder, exist_ok=True)
# 클래스별 폴더 처리
for class_name in os.listdir(input_folder):
class_path = os.path.join(input_folder, class_name)
output_class_path = os.path.join(output_folder, class_name)
# 클래스 폴더가 아닌 경우 건너뛰기
if not os.path.isdir(class_path):
continue
# 출력 클래스 폴더 생성
os.makedirs(output_class_path, exist_ok=True)
# 클래스 내 각 이미지 처리
for img_name in os.listdir(class_path):
if not img_name.lower().endswith(('.png', '.jpg', '.jpeg')):
continue
img_path = os.path.join(class_path, img_name)
output_img_path = os.path.join(output_class_path, img_name)
# 이미지 로드
if grayscale:
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
else:
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR to RGB
if img is None:
print(f"이미지를 로드할 수 없습니다: {img_path}")
continue
# 이미지 리사이징
img_resized = cv2.resize(img, target_size)
# 정규화 (0-1 범위로)
img_normalized = img_resized / 255.0
# 그레이스케일 이미지를 3채널로 확장 (필요시)
if grayscale and len(img_normalized.shape) == 2:
img_normalized = np.stack([img_normalized] * 3, axis=-1)
# 전처리된 이미지 저장
# 정규화된 이미지를 다시 0-255로 변환하여 저장
img_to_save = (img_normalized * 255).astype(np.uint8)
if grayscale:
cv2.imwrite(output_img_path, img_to_save[:,:,0])
else:
cv2.imwrite(output_img_path, cv2.cvtColor(img_to_save, cv2.COLOR_RGB2BGR))
print(f"전처리 완료: {output_img_path}")
def create_data_generator(use_augmentation=True):
"""데이터 증강을 위한 ImageDataGenerator 생성"""
if use_augmentation:
return ImageDataGenerator(
rescale=1./255, # 정규화
rotation_range=20, # 회전
width_shift_range=0.2, # 수평 이동
height_shift_range=0.2, # 수직 이동
shear_range=0.2, # 전단
zoom_range=0.2, # 확대/축소
horizontal_flip=True, # 수평 뒤집기
fill_mode='nearest', # 빈 픽셀 채우기 방식
validation_split=0.2 # 검증 데이터 비율
)
else:
return ImageDataGenerator(
rescale=1./255, # 정규화만 적용
validation_split=0.2 # 검증 데이터 비율
)
def main():
input_folder = "dataset"
output_folder = "preprocessed_dataset"
# 이미지 전처리
preprocess_images(input_folder, output_folder, target_size=(224, 224), grayscale=False)
print("이미지 전처리가 완료되었습니다.")
# 데이터 생성기 예시
datagen = create_data_generator(use_augmentation=True)
print("데이터 증강 생성기가 생성되었습니다. 학습 시 사용할 수 있습니다.")
if __name__ == "__main__":
main()
def visualize_augmentation(image_path, output_folder):
"""데이터 증강 효과를 시각화하여 저장"""
import matplotlib.pyplot as plt
# 이미지 로드
img = cv2.imread(image_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224))
# 데이터 증강 생성기
datagen = ImageDataGenerator(
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
# 배치 크기 1로 이미지 포맷 변경
x = img.reshape((1,) + img.shape)
# 증강된 이미지 생성 및 저장
os.makedirs(output_folder, exist_ok=True)
plt.figure(figsize=(12, 12))
plt.subplot(3, 3, 1)
plt.imshow(img)
plt.title('Original Image')
plt.axis('off')
i = 1
for batch in datagen.flow(x, batch_size=1):
plt.subplot(3, 3, i + 1)
plt.imshow(batch[0].astype(np.uint8))
plt.title(f'Augmented {i}')
plt.axis('off')
i += 1
if i > 8: # 8개의 증강된 이미지 생성
break
plt.tight_layout()
plt.savefig(os.path.join(output_folder, 'augmentation_examples.png'))
plt.close()
print(f"데이터 증강 시각화가 저장되었습니다: {os.path.join(output_folder, 'augmentation_examples.png')}")
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import numpy as np
import os
def create_model(input_shape, num_classes):
"""CNN 모델 생성"""
model = models.Sequential([
# 첫 번째 합성곱 블록
layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=input_shape),
layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 두 번째 합성곱 블록
layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 세 번째 합성곱 블록
layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 완전 연결 층
layers.Flatten(),
layers.Dense(512, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
# 모델 컴파일
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
def train_model(model, train_data, validation_data, epochs=15):
"""모델 학습 함수"""
# 조기 종료 설정 (과적합 방지)
early_stopping = tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=5,
restore_best_weights=True
)
# 모델 학습
history = model.fit(
train_data,
epochs=epochs,
validation_data=validation_data,
callbacks=[early_stopping]
)
return history
def plot_training_history(history, output_folder):
"""학습 과정 시각화"""
# 정확도 그래프
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.title('Training and Validation Accuracy')
# 손실 그래프
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('Training and Validation Loss')
# 그래프 저장
os.makedirs(output_folder, exist_ok=True)
plt.tight_layout()
plt.savefig(os.path.join(output_folder, 'training_history.png'))
plt.close()
def main():
# 데이터셋 경로
dataset_path = "preprocessed_dataset"
output_folder = "model_output"
os.makedirs(output_folder, exist_ok=True)
# 이미지 크기 및 배치 크기 설정
img_height, img_width = 224, 224
batch_size = 32
# 데이터 증강 생성기
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
validation_split=0.2 # 검증 데이터 비율
)
# 훈련 데이터 생성기
train_generator = train_datagen.flow_from_directory(
dataset_path,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='categorical',
subset='training'
)
# 검증 데이터 생성기
validation_generator = train_datagen.flow_from_directory(
dataset_path,
target_size=(img_height, img_width),
batch_size=batch_size,
class_mode='categorical',
subset='validation'
)
# 클래스 정보 출력
class_names = list(train_generator.class_indices.keys())
num_classes = len(class_names)
print(f"클래스: {class_names}")
print(f"총 클래스 수: {num_classes}")
# 모델 생성
model = create_model(input_shape=(img_height, img_width, 3), num_classes=num_classes)
model.summary()
# 모델 학습
history = train_model(model, train_generator, validation_generator, epochs=15)
# 학습 과정 시각화
plot_training_history(history, output_folder)
# 모델 저장
model.save(os.path.join(output_folder, 'object_recognition_model.h5'))
print(f"모델이 저장되었습니다: {os.path.join(output_folder, 'object_recognition_model.h5')}")
# 클래스 이름 저장
with open(os.path.join(output_folder, 'class_names.txt'), 'w') as f:
for class_name in class_names:
f.write(f"{class_name}\n")
print(f"클래스 이름이 저장되었습니다: {os.path.join(output_folder, 'class_names.txt')}")
if __name__ == "__main__":
main()
def evaluate_model(model, test_data):
"""모델 평가 함수"""
# 모델 평가
results = model.evaluate(test_data)
print(f"테스트 손실: {results[0]:.4f}")
print(f"테스트 정확도: {results[1]:.4f}")
return results
def predict_image(model, image_path, class_names):
"""단일 이미지에 대한 예측 함수"""
# 이미지 로드 및 전처리
img = tf.keras.preprocessing.image.load_img(
image_path, target_size=(224, 224)
)
img_array = tf.keras.preprocessing.image.img_to_array(img)
img_array = img_array / 255.0 # 정규화
img_array = np.expand_dims(img_array, axis=0) # 배치 차원 추가
# 예측
predictions = model.predict(img_array)
predicted_class = np.argmax(predictions[0])
confidence = np.max(predictions[0])
return {
'class_name': class_names[predicted_class],
'confidence': confidence,
'predictions': predictions[0]
}
def visualize_predictions(model, test_data, class_names, num_samples=5):
"""테스트 데이터에 대한 예측 시각화"""
plt.figure(figsize=(15, 10))
# 무작위 샘플 선택
for i, (images, labels) in enumerate(test_data.take(1)):
if i >= num_samples:
break
for j in range(min(num_samples, len(images))):
# 예측
predictions = model.predict(np.expand_dims(images[j], axis=0))
predicted_class = np.argmax(predictions[0])
true_class = np.argmax(labels[j])
# 이미지 표시
plt.subplot(1, num_samples, j + 1)
plt.imshow(images[j])
color = 'green' if predicted_class == true_class else 'red'
title = f"Predicted: {class_names[predicted_class]}\nTrue: {class_names[true_class]}"
plt.title(title, color=color)
plt.axis('off')
plt.tight_layout()
plt.savefig('prediction_examples.png')
plt.close()
print("예측 시각화가 저장되었습니다: prediction_examples.png")
def create_transfer_learning_model(input_shape, num_classes):
"""전이 학습을 위한 모델 생성"""
# 사전 학습된 MobileNetV2 모델 로드 (가벼운 모델)
base_model = tf.keras.applications.MobileNetV2(
input_shape=input_shape,
include_top=False, # 최상위 분류기 제외
weights='imagenet' # ImageNet으로 사전 학습된 가중치 사용
)
# 기존 층 고정 (학습하지 않음)
base_model.trainable = False
# 새로운 모델 구성
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
# 모델 컴파일
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
loss='categorical_crossentropy',
metrics=['accuracy']
)
return model
# 전이 학습 모델 생성 및 학습
transfer_model = create_transfer_learning_model(
input_shape=(img_height, img_width, 3),
num_classes=num_classes
)
transfer_model.summary()
# 모델 학습
transfer_history = train_model(
transfer_model,
train_generator,
validation_generator,
epochs=10
)
# 모델 저장
transfer_model.save(os.path.join(output_folder, 'transfer_learning_model.h5'))
import tensorflow as tf
import numpy as np
import cv2
import serial
import time
import os
def load_model_and_classes(model_path, class_names_path):
"""학습된 모델과 클래스 이름 로드"""
# 모델 로드
model = tf.keras.models.load_model(model_path)
# 클래스 이름 로드
with open(class_names_path, 'r') as f:
class_names = [line.strip() for line in f.readlines()]
print(f"모델을 로드했습니다. 클래스: {class_names}")
return model, class_names
# 모델과 클래스 이름 로드
model_path = "model_output/object_recognition_model.h5"
class_names_path = "model_output/class_names.txt"
model, class_names = load_model_and_classes(model_path, class_names_path)
def preprocess_image(frame, target_size=(224, 224)):
"""카메라 프레임을 모델 입력에 맞게 전처리"""
# 이미지 리사이징
img = cv2.resize(frame, target_size)
# BGR을 RGB로 변환 (OpenCV는 BGR, TensorFlow는 RGB 사용)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 정규화 (0-1 범위)
img = img / 255.0
# 배치 차원 추가
img = np.expand_dims(img, axis=0)
return img
def predict_frame(model, frame, class_names):
"""프레임에 대한 예측 수행"""
# 이미지 전처리
processed_img = preprocess_image(frame)
# 예측
predictions = model.predict(processed_img)
predicted_class_idx = np.argmax(predictions[0])
confidence = predictions[0][predicted_class_idx]
return {
'class_idx': predicted_class_idx,
'class_name': class_names[predicted_class_idx],
'confidence': confidence
}
def run_realtime_detection(model, class_names, confidence_threshold=0.7):
"""실시간 객체 인식 수행"""
# 카메라 열기
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("카메라를 열 수 없습니다.")
return
print("실시간 객체 인식을 시작합니다. 종료하려면 'q'를 누르세요.")
while True:
# 프레임 읽기
ret, frame = cap.read()
if not ret:
break
# 예측 수행
result = predict_frame(model, frame, class_names)
# 결과 시각화
text = f"{result['class_name']}: {result['confidence']:.2f}"
# 신뢰도가 임계값 이상인 경우에만 표시
if result['confidence'] >= confidence_threshold:
cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 객체 주변에 박스 표시 (여기서는 전체 화면에 표시)
cv2.rectangle(frame, (50, 50), (frame.shape[1]-50, frame.shape[0]-50), (0, 255, 0), 2)
else:
cv2.putText(frame, "Unknown", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 결과 표시
cv2.imshow('Object Recognition', frame)
# 'q' 키를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 자원 해제
cap.release()
cv2.destroyAllWindows()
def setup_arduino_communication(port='COM3', baudrate=9600):
"""아두이노와 시리얼 통신 설정"""
try:
arduino = serial.Serial(port, baudrate, timeout=1)
print(f"아두이노 연결 성공: {port}")
time.sleep(2) # 아두이노 리셋 시간 대기
return arduino
except Exception as e:
print(f"아두이노 연결 실패: {e}")
return None
def send_command_to_arduino(arduino, command):
"""아두이노로 명령 전송"""
if arduino is None:
print("아두이노가 연결되어 있지 않습니다.")
return False
try:
arduino.write(command.encode())
time.sleep(0.05) # 안정적인 전송을 위한 지연
return True
except Exception as e:
print(f"명령 전송 실패: {e}")
return False
// 블루투스/시리얼로 받은 명령을 처리하는 코드
// (이전 6차시 코드와 유사)
void setup() {
// 모터 핀 설정
// 이전 차시와 동일...
// 시리얼 통신 시작
Serial.begin(9600);
}
void loop() {
// 명령 확인
if (Serial.available()) {
char command = Serial.read();
executeCommand(command);
}
delay(50);
}
// 명령 실행 함수
void executeCommand(char command) {
switch(command) {
case 'F': // 전진
forward(200);
Serial.println("Moving Forward");
break;
case 'B': // 후진
backward(200);
Serial.println("Moving Backward");
break;
case 'L': // 좌회전
left(150);
Serial.println("Turning Left");
break;
case 'R': // 우회전
right(150);
Serial.println("Turning Right");
break;
case 'S': // 정지
stop();
Serial.println("Stopped");
break;
// 추가 명령어...
}
}def run_ai_robot_control(model, class_names, arduino, confidence_threshold=0.7):
"""AI 객체 인식 기반 로봇 제어"""
# 카메라 열기
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("카메라를 열 수 없습니다.")
return
print("AI 기반 로봇 제어를 시작합니다. 종료하려면 'q'를 누르세요.")
# 각 클래스에 대한 로봇 동작 정의
class_actions = {
'bottle': 'F', # 병 감지 시 전진
'cup': 'S', # 컵 감지 시 정지
'book': 'B' # 책 감지 시 후진
}
# 기본 동작 (아무것도 감지되지 않을 때)
default_action = 'S' # 정지
# 마지막 명령 시간 및 내용 추적
last_command_time = time.time()
last_command = None
command_interval = 1.0 # 명령 간 최소 간격 (초)
while True:
# 프레임 읽기
ret, frame = cap.read()
if not ret:
break
# 예측 수행
result = predict_frame(model, frame, class_names)
current_time = time.time()
# 결과 시각화
text = f"{result['class_name']}: {result['confidence']:.2f}"
# 신뢰도가 임계값 이상인 경우에만 동작
if result['confidence'] >= confidence_threshold:
cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 해당 클래스에 대한 동작이 정의되어 있으면 실행
if result['class_name'] in class_actions:
command = class_actions[result['class_name']]
# 일정 시간이 지났거나 명령이 변경된 경우에만 새 명령 전송
if (current_time - last_command_time > command_interval or
last_command != command):
success = send_command_to_arduino(arduino, command)
if success:
print(f"명령 전송: {command} ({result['class_name']})")
last_command_time = current_time
last_command = command
# 현재 동작 표시
action_text = {
'F': "Moving Forward",
'B': "Moving Backward",
'L': "Turning Left",
'R': "Turning Right",
'S': "Stopped"
}.get(command, "Unknown Action")
cv2.putText(frame, action_text, (10, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
else:
cv2.putText(frame, "No object detected", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 일정 시간 동안 객체가 감지되지 않으면 기본 동작 실행
if current_time - last_command_time > command_interval and last_command != default_action:
success = send_command_to_arduino(arduino, default_action)
if success:
print(f"명령 전송: {default_action} (Default)")
last_command_time = current_time
last_command = default_action
# 결과 표시
cv2.imshow('AI Robot Control', frame)
# 'q' 키를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
# 종료 전 로봇 정지
send_command_to_arduino(arduino, 'S')
break
# 자원 해제
cap.release()
cv2.destroyAllWindows()
def main():
# 모델과 클래스 이름 로드
model_path = "model_output/object_recognition_model.h5"
class_names_path = "model_output/class_names.txt"
model, class_names = load_model_and_classes(model_path, class_names_path)
# 아두이노 연결
arduino_port = 'COM3' # 실제 포트에 맞게 변경
arduino = setup_arduino_communication(arduino_port)
if arduino is None:
print("아두이노 연결 없이 객체 인식만 실행합니다.")
run_realtime_detection(model, class_names)
else:
# AI 기반 로봇 제어 실행
run_ai_robot_control(model, class_names, arduino)
# 연결 종료
arduino.close()
if __name__ == "__main__":
main()
# 기본 동작 + 속도 정보
# 예: 'F150' - 150의 속도로 전진
def send_command_with_speed(arduino, action, speed):
command = f"{action}{speed}"
return send_command_to_arduino(arduino, command)
# 복잡한 정보를 JSON 형식으로 전송
import json
def send_complex_command(arduino, data_dict):
json_str = json.dumps(data_dict)
return send_command_to_arduino(arduino, json_str + '\n')
# 예: {"action": "F", "speed": 150, "object": "bottle", "confidence": 0.95}

"이번 프로젝트를 통해 경험한 AI와 로봇 기술을 바탕으로, 미래에 이러한 기술이 어떻게 발전하고 우리 사회에 어떤 영향을 미칠지 생각해봅시다. 여러분이 생각하는 AI 로봇의 가능성과 한계, 그리고 이를 활용한 창의적인 아이디어는 무엇인가요?"
import tensorflow as tf
# 저장된 모델 로드
model = tf.keras.models.load_model('object_recognition_model.h5')
# TFLite 변환기 생성
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 양자화 적용 (선택사항, 모델 크기 추가 감소)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
# 변환 실행
tflite_model = converter.convert()
# 모델 저장
with open('model.tflite', 'wb') as f:
f.write(tflite_model)
# YOLOv5 설치 및 실행 예시
!git clone https://github.com/ultralytics/yolov5
%cd yolov5
!pip install -r requirements.txt
# 사전 학습된 모델 사용
import torch
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
# 이미지에 적용
results = model('path/to/image.jpg')
# 결과 표시
results.print()
results.show()
# 결과 분석
results.pandas().xyxy[0] # 바운딩 박스 좌표와 클래스
# ROS2 Python 노드 예시
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
class ObjectDetectionNode(Node):
def __init__(self):
super().__init__('object_detection_node')
# 이미지 구독자 생성
self.subscription = self.create_subscription(
Image,
'camera/image_raw',
self.image_callback,
10)
# 명령 발행자 생성
self.command_publisher = self.create_publisher(
String,
'robot/command',
10)
self.bridge = CvBridge()
def image_callback(self, msg):
# ROS 이미지 메시지를 OpenCV 형식으로 변환
cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
# 여기에 객체 인식 코드 추가
# ...
# 인식 결과에 따라 로봇 명령 발행
command_msg = String()
command_msg.data = 'forward' # 예시 명령
self.command_publisher.publish(command_msg)
self.get_logger().info(f'Published command: {command_msg.data}')
def main(args=None):
rclpy.init(args=args)
node = ObjectDetectionNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
"지금까지 배운 것은 로봇과 AI의 기초에 불과합니다. 실제 산업 현장에서는 더 복잡하고 정교한 시스템이 사용되지만, 여러분이 경험한 기본 원리는 동일합니다. 이 기초를 바탕으로 더 깊이 탐구하고 창의적인 프로젝트에 도전해보세요."