라즈베리파이 4를 서버로 사용하고, 브라우저에서 음성 입력 → LLM 처리 → 음성 출력이 한 번에 이루어지는 전체 통합 시스템을 구현해 보겠습니다.
🏗️ 시스템 아키텍처
이 시스템은 사용자가 웹 페이지의 버튼을 누르고 말하면, 서버가 이를 처리하여 다시 목소리로 응답하는 구조입니다.
1. 사전 준비 (필수 설치)
먼저 라즈베리파이 터미널에서 필요한 라이브러리를 설치합니다.
# 1. Ollama 및 모델 설치
curl -fsSL https://ollama.com/install.sh | sh
ollama run gemma3:270m # 가벼운 모델 추천
# 2. Python 라이브러리 설치
pip install flask flask-cors faster-whisper
# 3. Piper TTS 및 한국어 모델 준비 (앞서 안내한 ~/piper 디렉토리 기준)
# piper 실행 파일과 ko_KR-kyuri-low.onnx 파일이 해당 폴더에 있어야 합니다.
2. 백엔드 코드 (app.py)
Flask를 사용하여 음성 파일을 받고 처리하는 서버를 구축합니다.
from flask import Flask, request, send_file, render_template
from flask_cors import CORS
from faster_whisper import WhisperModel
import requests
import subprocess
import os
app = Flask(__name__)
CORS(app)
# STT 모델 로드 (라즈베리파이용 tiny 모델)
stt_model = WhisperModel("tiny", device="cpu", compute_type="int8")
@app.route('/')
def index():
return render_template('index.html')
@app.route('/voice', methods=['POST'])
def process_voice():
# 1. 클라이언트로부터 녹음된 데이터 받기
audio_file = request.files['audio']
audio_path = "input.wav"
audio_file.save(audio_path)
# 2. STT (음성 -> 텍스트)
segments, _ = stt_model.transcribe(audio_path, language="ko")
user_text = "".join([s.text for s in segments])
print(f"User: {user_text}")
# 3. LLM (Ollama 질의)
ollama_response = requests.post(
"http://localhost:11434/api/generate",
json={"model": "gemma3:270m", "prompt": user_text, "stream": False}
)
answer_text = ollama_response.json().get('response', "죄송해요, 이해하지 못했어요.")
print(f"AI: {answer_text}")
# 4. TTS (텍스트 -> 음성 파일 생성)
# Piper 경로를 본인의 환경에 맞게 수정하세요.
tts_cmd = f'echo "{answer_text}" | ./piper/piper --model ./piper/ko_KR-kyuri-low.onnx --output_file output.wav'
subprocess.run(tts_cmd, shell=True)
# 5. 생성된 음성 파일 반환
return send_file("output.wav", mimetype="audio/wav")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
3. 프론트엔드 코드 (templates/index.html)
브라우저에서 마이크를 제어하고 서버와 통신하는 코드입니다.
<!DOCTYPE html>
<html>
<head>
<title>RPi Voice AI</title>
</head>
<body>
<h1>라즈베리파이 음성 비서</h1>
<button id="recordBtn">🎤 말하기 시작</button>
<p id="status">대기 중...</p>
<audio id="audioPlayer" controls></audio>
<script>
let mediaRecorder;
let audioChunks = [];
document.getElementById('recordBtn').onclick = async () => {
if (!mediaRecorder || mediaRecorder.state === "inactive") {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = e => audioChunks.push(e.data);
mediaRecorder.onstop = async () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
const formData = new FormData();
formData.append('audio', audioBlob);
document.getElementById('status').innerText = "생각 중...";
const response = await fetch('/voice', { method: 'POST', body: formData });
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const player = document.getElementById('audioPlayer');
player.src = url;
player.play();
document.getElementById('status').innerText = "답변 중!";
};
mediaRecorder.start();
document.getElementById('recordBtn').innerText = "🛑 중지 및 전송";
document.getElementById('status').innerText = "듣고 있어요...";
} else {
mediaRecorder.stop();
document.getElementById('recordBtn').innerText = "🎤 말하기 시작";
}
};
</script>
</body>
</html>
4. 실행 방법
- 디렉토리 구조 확인:
project/ ├── app.py ├── templates/ │ └── index.html └── piper/ (piper 바이너리와 모델 파일 위치)
```
- 서버 실행:
python app.py - 접속: 같은 네트워크에 연결된 PC나 스마트폰 브라우저에서
http://라즈베리파이IP:5000접속.
⚠️ 주의사항
- HTTPS: 외부 네트워크나 최신 안드로이드/iOS 브라우저에서 마이크 기능을 쓰려면 HTTPS가 필요합니다. 로컬 테스트는
localhost또는127.0.0.1에서 가장 잘 작동합니다. - 성능: 라즈베리파이 4에서 STT(
tiny)와 LLM(270M)을 동시에 돌리면 답변까지 약 3~7초 정도 소요될 수 있습니다.
728x90
'Hardware > 라즈베리파이' 카테고리의 다른 글
| 라즈베리파이 Nodejs 설치 (0) | 2026.01.22 |
|---|---|
| 라즈베리파이 시작하기 - 운영체제 (0) | 2024.07.31 |
| 라즈베리 파이 시작하기 - Llama 3 설치 (0) | 2024.07.30 |
| 라즈베리파이 시작하기 - AI 설치 1 (0) | 2024.07.26 |
| 라즈베리파이 시작하기 - 스크래치 연동 (0) | 2024.07.26 |
