Merge branch 'main' into lesson3

pull/432/head
Hyejin Kim 3 years ago committed by GitHub
commit 3198758da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
# 야간 조명 만들기 - 라즈베리 파이
# 야간 조명 만들기 - Raspberry Pi
라즈베리 파이에 LED를 추가하여 야간 조명을 만들어봅시다.
@ -54,7 +54,8 @@ Grove 조명 센서와 Grove LED를 사용하여 야간 조명 프로그래밍
1. 이 할당의 이전 부분에서 만든 VS Code에서 야간 조명 프로젝트를 Pi에서 직접 실행하거나 원격 SSH 확장을 사용하여 연결합니다.
1. 필요한 라이브러리를 가져오려면 아래 코드를 `app.py` 파일에 추가하십시오. 이는 다른`import` 줄 바로 아래 추가되어야 합니다..
1. 필요한 라이브러리를 가져오려면 아래 코드를 `app.py` 파일에 추가하십시오. 이는 다른`import` 줄 바로 아래 추가되어야 합니다.
```python
from grove.grove_led import GroveLed

@ -1,4 +1,4 @@
# 야간 조명 만들기 - 라즈베리 파이
# 야간 조명 만들기 - Raspberry Pi
이 강의에서 여러분의 라즈베리 파이에 광센서를 적용해봅시다
@ -74,7 +74,7 @@
time.sleep(1)
```
1. VS Code의 터미널에서 아래 코드로 Python 앱을 실행 해 봅시다..
1. VS Code의 터미널에서 아래 코드로 Python 앱을 실행 해 봅시다.
```sh
python3 app.py

@ -0,0 +1,472 @@
# 인터넷에 장치 연결하기
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-4.jpg)
> Nitya Narasimhan의 스케치 노트. 더 큰 버전을 보려면 이미지를 클릭하세요.
이 수업은 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn)의 [Hello IoT series](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 의 일부로 진행되었습니다. 이 수업은 2개의 비디오 - 1 시간의 수업, 1시간의 강의에 대한 집중 탐구 및 질의 응답으로 구성되어 있습니다.
![https://img.youtube.com/vi/O4dd172mZhs/0.jpg](https://img.youtube.com/vi/O4dd172mZhs/0.jpg)
![https://img.youtube.com/vi/j-cVCzRDE2Q/0.jpg](https://img.youtube.com/vi/j-cVCzRDE2Q/0.jpg)
> 🎥 상단의 이미지를 클릭하여 비디오를 시청할 수 있습니다.
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/7)
## 개요
IoT에서 **I****인터넷**을 의미합니다. - 장치에 연결된 센서에서 측정값 수집, 액추에이터 제어를 위한 메세지 전송과 같은 IoT의 많은 기능을 가능하게 하는 클라우드 연결 및 서비스를 의미합니다. IoT 장치는 일반적으로 표준 통신 프로토콜을 사용하여 단일 클라우드 IoT 서비스에 연결되며 해당 서비스는 데이터에 대한 현명한 결정을 내리는 AI 서비스에 제어 또는 보고를 위한 웹 앱에 이르기까지 나머지 IoT 어플리케이션에 연결됩니다.
> 🎓 센서에서 수집되어 클라우드로 전송되는 데이터를 telemetry라고 합니다.
IoT 장치는 클라우드에서 메시지를 수신할 수 있습니다. 종종 메시지에는 다음 명령들이 포함되어 있습니다. - 내부적으로 작업 수행하기(예: 재부팅 또는 펌웨어 업데이트) 또는 엑추에이터 사용하기(예: 조명 켜기).
이 강의에서는 IoT 장치가 클라우드에 연결하는데 사용할 수 있는 몇가지 통신 프로토콜과 송수신할 수 있는 데이터 유형을 소개합니다. 또한 야간 조명에 인터넷 제어를 추가하고, LED 제어 논리를 로컬에서 실행되는 ‘서버’ 코드로 이동하는 실습을 하게 됩니다.
이 강의에서 다룰 내용은 다음과 같습니다:
- [통신 프로토콜](#communication-protocols)
- [메시지 큐 원격 분석 전송(MQTT)](##message-queueing-telemetry-transport-mqtt)
- [텔레메트리(원격 측정)](#telemetry)
- [명령](#commands)
## 통신 프로토콜
IoT 장치가 인터넷과 통신하는 데 널리 사용하는 통신 프로토콜이 많이 있습니다. 가장 인기있는 것은 일종의 브로커 형태의 발행/구독 메시징을 기반으로 합니다. IoT 장치는 브로커에 연결하고 원격 측정을 게시하고 명령을 구독합니다. 또한 클라우드 서비스는 브로커에 연결하고 모든 원격 측정 메시지를 구독하고 특정 장치, 장치 그룹에 명령을 게시합니다.
![IoT devices connect to a broker and publish telemetry and subscribe to commands. Cloud services connect to the broker and subscribe to all telemetry and send commands to specific devices.](../../../../images/pub-sub.png)
MQTT는 IoT 장치에 가장 널리 사용되는 통신 프로토콜이며 이 강의에서 다룹니다. 다른 프로토콜에는 AMQP와 HTTP/HTTPS가 있습니다.
## 메시지 큐 원격 분석 전송 (MQTT)
[MQTT](http://mqtt.org/) 는 장치 간에 메시지를 보낼 수 있는 가벼운 개방형 표준 메시징 프로토콜 입니다. 1999년에 송유관을 모니터링하도록 설계되었으며 15년 후 IBM에서 공개 표준으로 발표했습니다.
MQTT 에는 단일 브로커와 여러 클라이언트가 있습니다. 모든 클라이언트는 브로커에 연결되고, 브로커는 메시지를 관련 클라이언트에게 라우팅합니다. 메시지는 개별 클라이언트에게 직접 전송되지 않고 명명된 주제를 사용하여 라우팅됩니다. 클라이언트는 주제를 게시할 수 있으며 해당 주제를 구독하는 모든 클라이언트는 메시지를 받습니다.
![IoT device publishing telemetry on the /telemetry topic, and the cloud service subscribing to that topic](../../../../images/mqtt.png)
✅ 조사를 해보십시오. IoT 장치가 많은 경우, MQTT 브로커가 모든 메시지를 처리하려면 어떻게 해야 합니까?
### IoT장치를 MQTT에 연결하기
야간 조명에 인터넷 제어를 추가하는 첫 번째 단계는 이를 MQTT 브로커에 연결하는 것입니다.
### 작업
장치를 MQTT 브로커에 연결합니다.
이 수업에서는 IoT 야간 조명을 인터넷에 연결하여 원격으로 제어할 수 있도록 합니다. 수업의 뒷부분에서, IoT 장치는 MQTT를 통해 가벼운 수준의 공용 MQTT 브로커로 원격 분석 메시지를 보내고, 당신이 작성할 서버 코드에 의해 선택될 것입니다.
이러한 설정의 실제 사용 사례는 경기장과 같이 조명이 많은 위치에서 조명 키는 것을 결정하기 전에 여러 광 센서에서 데이터를 수집하는 것이 있습니다. 이렇게 하면 구름이나 새가 한 센서를 가려도, 다른 센서에서 충분한 빛을 감지한 경우 조명이 켜지지 않을 수 있습니다.
✅ 명령을 보내기 전에 여러 센서의 데이터를 평가해야 하는 다른 상황은 무엇입니까?
이 과제의 일부로 MQTT 브로커를 설정하는 복잡성을 처리하는 대신 오픈 소스 MQTT 브로커인 [Eclipse Mosquitto](https://www.mosquitto.org/) 를 실행하는 공개 테스트 서버를 사용할 수 있습니다. 이 테스트 브로커는 [test.mosquitto.org](https://test.mosquitto.org/)에서 공개적으로 사용할 수 있으며, 계정을 설정할 필요가 없으므로 MQTT 클라이언트와 서버를 테스트에 훌륭한 도구 입니다.
> 💁 이 테스트 브로커는 공개되어 있으며 안전하지 않습니다. 누구나 귀하가 게시한 내용을 들을 수 있으므로, 비공개로 유지해야 하는 데이터와 함께 사용해서는 안됩니다.
![../../../images/assignment-1-internet-flow.png](../../../../images/assignment-1-internet-flow.png)
아래 단계에 따라 장치를 MQTT 브로커에 연결하십시오:
- [Arduino - Wio Terminal](notion://www.notion.so/wio-terminal-mqtt.md) 아두이노 - Wio 터미널
- [Single-board computer - Raspberry Pi/Virtual IoT device](notion://www.notion.so/single-board-computer-mqtt.md) 싱글-보드 컴퓨터 - Raspberry Pi/가상 IoT 장치
### MQTT 자세히 알아보기
주제에는 계층이 있을 수 있으며, 클라이언트는 와일드카드를 사용하여 다른 수준의 계층에 가입할 수 있습니다. 예를 들어, `/telemetry/temperature` 주제에 온도 원격 측정 메시지를 보내고 , `/telemetry/humidity` 주제에 습도 메시지를 보낸 뒤, 클라우드 앱에서 `/telemetry/*` 주제를 구독하여 온도 와 습도 원격 측정 메시지를 모두 수신할 수 있습니다.
메시지는 수신되는 메시지의 보장을 결정하는 서비스의 품질(QoS)과 함께 보낼 수 있습니다.
- 최대 한 번 - 메세지는 한 번만 전송되고 클라이언트와 브로커는 전송을 확인하기 위해 추가 단계를 수행하지 않습니다(실행 후 잊어버림).
- 최소 한 번 - 확인 메시지가 수신될 때까지 보낸 사람이 여러번 다시 시도합니다.(전송 보장).
- 정확히 한 번 - 보낸 사람과 받는 사람이 2 단계의 핸드셰이크에 참여하여 메시지 사본을 하나만 수신하도록 합니다(전달 보장).
✅ 어떤 상황에서 실행 후 잊어버림 메시지보다 전달 보장 메시지가 필요할 수 있습니까?
이름은 메시지 큐잉(MQTT의 이니셜)이지만, 실제로는 메시지 큐를 지원하지 않습니다. 클라이언트가 연결을 끊었다가, 다시 연결하면 QoS 프로세스를 사용하여 이미 처리를 시작한 메시지를 제외하고 연결이 끊긴 동안 보낸 메시지를 받지 않는다는 의미입니다. 메시지에는 보유 플래그가 설정되어 있을 수 있습니다. 이것이 설정되면 MQTT 브로커는 이 플래그를 사용하여 주제에 대해 보낸 마지막 메시지를 저장하고 나중에 주제를 구독하는 모든 클라이언트에게 이를 보냅니다. 이런 식으로 클라이언트는 항상 최신 메시지를 받습니다.
또한 MQTT는 메시지 간의 긴 간격 동안 연결이 여젼히 활성 상태인지 확인하는 연결 유지 기능을 지원합니다.
> 🦟 [Mosquitto from the Eclipse Foundation](https://mosquitto.org/) 에는 [test.mosquitto.org](https://test.mosquitto.org/)에서 호스팅되는 코드 테스트에 사용할 수 있는 공개 MQTT 브로커와 함께 MQTT를 실험하기 위해 직접 실행할 수 있는 무료 MQTT 브로커가 있습니다.
MQTT 연결은 공개되고 공개되거나 사용자의 이름과 암호 또는 인증서를 사용하여 암호화되고 보호될 수 있습니다.
> 💁 MQTT 는 HTTP와 동일한 기본 네트워크 프로토콜 이지만 다른 포트에서 TCP/IP를 통해 통신합니다. 또한 웹소켓을 통한 MQTT 를 사용하여 브라우저에서 실행되는 웹 앱과 통신하거나, 방화벽이나 기타 네트워크 규칙이 표준 MQTT 연결을 차단하는 상황에서도 사용할 수 있습니다.
## 텔레메트리(원격 측정)
텔레메트리(Telemetry)는 원격 측정이라는 그리스어에서 파생되었습니다. 원격 측정은 센서에서 데이터를 수집하여 클라우드로 보내는 행위입니다..
> 💁 최초의 원격 측정 장치 중 하나는 1874년 프랑스에서 발명되었으며 실시간으로 날씨와 눈 깊이를 몽블랑에서 파리로 보냈습니다. 당시에는 무선 기술을 사용할 수 없었기 때문에 물리적 와이어를 사용했습니다.
수업 1의 예시 스마트 온도 조절기를 다시 살펴보겠습니다.
![../../../images/telemetry.png](../../../../images/telemetry.png)
온도 조절 장치에는 원격 측정을 수집하는 온도 센서가 있습니다. 하나의 온도 센서가 내장되어 있을 가능성이 높으며 BLE([Bluetooth Low Energy](https://wikipedia.org/wiki/Bluetooth_Low_Energy))와 같은 무선 프로토콜을 통해 여러 외부 온도 센서에 연결할 수 있습니다.
전송할 원격 측정 데이터의 예는 다음과 같습니다:
| Name | Value | Description |
| ------------------------ | ----- | ---------------------------------------------------------------------- |
| `thermostat_temperature` | 18°C | 온도 조절 장치에 내장된 온도 센서로 측정된 온도 |
| `livingroom_temperature` | 19°C | `livingroom` 이라고 명명된 방에 있는 원격 온도 센서에 의해 측정된 온도 |
| `bedroom_temperature` | 21°C | `bedroom`이라고 명명된 방에 있는 원격 온도 센서에 의해 측정된 온도 |
클라우드 서비스는 이 원격 측정 데이터를 사용하여 난방을 제어하기 위해 보낼 명령을 결정할 수 있습니다.
### IoT 장치에서 원격 분석 보내기
야간 조명에 인터넷 제어를 추가하는 다음 과정은 조명 수준 원격 분석을 MQTT 브로커의 텔레메트리 주제를 보내는 것입니다.
### 작업 - IoT 장치에서 원격 분석 보내기
MQTT 브로커에게 가벼운 수준의 원격 분석을 보냅니다.
데이터는 키/값 쌍을 사용해 텍스트로 데이터를 인코딩하는 표준인 JSON(JavaScriopt Object Notation의 약자)으로 인코딩되어 전송됩니다.
✅ 이전에 JSON을 접한 적이 없다면, [JSON.org documentation](https://www.json.org/)에서 더 자세히 알아볼 수 있습니다.
장치에서 MQTT 브로커로 원격 분석을 보내려면 아래 단계를 따르십시오:
- [아두이노 - Wio 터미널](wio-terminal-telemetry.md)
- [싱글 보드 컴퓨터 - Raspberry Pi/가상 IoT 장치](single-board-computer-telemetry.md)
### MQTT 브로커로부터 텔레메트리 수신
다른 쪽 끝에 수신할 것이 없으면 원격 분석을 보낼 의미가 없습니다. 광도 원격 측정은 데이터를 처리하기 위해 이를 수신하는 무언가가 필요합니다. 이 '서버' 코드는 더 큰 IoT 애플리케이션의 일부로 클라우드 서비스에 배포할 코드 유형이지만, 여기에서는 이 코드를 컴퓨터에서 로컬로(또는 직접 코딩하는 경우 Pi에서 실행할 것입니다). 서버 코드는 가벼운 수준의 MQTT를 통해 원격 분석 메시지를 수신하는 Python 앱으로 구성됩니다. 이 수업의 뒷부분에서 LED를 켜거나 끄도록 지시하는 명령 메시지로 응답하게 할 것입니다.
✅ 조사해보기: 리스너가 없으면 MQTT 메시지는 어떻게 됩니까?
### Python 과 VS Code 설치
Python 및 VS Code를 로컬에 설치하지 않은 경우 서버를 코딩하려면 둘 다 설치해야 합니다. 가상 IoT 장치를 사용 중이거나 Raspberry Pi에서 작업하는 경우 이미 설치 및 구성되어 있어야 하므로 이 단계를 건너뛸 수 있습니다.
### 작업 - Python 과 VS Code 설치
Python 과 VS Code 를 설치합니다.
1. Python을 설치합니다. 최신 버전의 Python 설치에 대한 지침은 [Python downloads page](https://www.python.org/downloads/) 를 참조하십시오.
2. Visual Studio Code (VS Code)를 설치합니다. 이것은 Python으로 가상 장치 코드를 작성하는데 사용할 편집기입니다. VS Code 설치에 대한 지침은 [VS Code documentation](https://code.visualstudio.com/?WT.mc_id=academic-17441-jabenn) 를 참조하십시오.
> 💁 선호하는 편집기가 있는 경우 이 수업에 IDE 또는 편집기를 자유롭게 사용할 수 있지만, 수업에서는 VS Code 사용을 기반으로 지침을 제공합니다.
3. VS Code Pylance 확장을 설치합니다. 이것은 Python 언어 지원을 제공하는 VS Code 의 확장입니다. VS Code에서 이 확장을 설치하는 방법은 [Pylance extension documentation](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 를 참조하십시오.
### Python 가상 환경 구성
Python의 강력한 기능 중 하나는 [pip packages](https://pypi.org/)를 설치하는 것입니다. - 이는 다른 사람이 작성하여 인터넷에 게시한 코드 패키지입니다. 하나의 명령으로 컴퓨터에 pip 패키지를 설치한 다음, 코드에서 해당 패키지를 사용할 수 있습니다. pip를 사용하여 MQTT를 통해 통신하는 패키지를 설치합니다.
기본적으로 패키지를 설치하면 컴퓨터의 모든 곳에서 사용할 수 있으며, 이것은 패키지 버전에 문제를 발생시킬 수 있습니다 - 예를 들어, 패키지의 한 버전에 따라 하나의 응용 프로그램이 다른 응용 프로그램의 새 버전을 설치할 때 중단되는 경우. 이 문제를 해결하기 위해, [Python 가상 환경](https://docs.python.org/3/library/venv.html)을 사용할 수 있습니다, 기본적으로 전용 폴더에 있는 폴더에 있는 Python의 복사본이며 pip 패키지를 설치하면 해당 폴더에만 설치됩니다.
### 작업 - Python 가상 환경 구성
Python 가상 환경을 구성하고 MQTT pip 패키지를 설치합니다.
1. 터미널 또는 명령줄에서 원하는 위치에서 다음을 실행하여 새 디렉터리를 만들고 탐색합니다:
```
mkdir nightlight-server
cd nightlight-server
```
2. 이제 다음을 실행하여 `.venv` 폴더에 가상 환경을 만듭니다.
```
python3 -m venv .venv
```
> 💁 Python 3 (최신 버전) 외에 Python2가 설치된 경우에만 가상 환경을 생성하기 위해 명시적으로 호출해야 합니다. Python2 가 설치된 경우 python 호출 시 Python 3 대신 Python 2 가 사용됩니다.
3. 가상 환경 활성화:
- On Windows:
- 명령 프롬프트 또는 Windows 터미널을 통한 명령 프롬프트를 사용하는 경우 다음을 실행합니다:
```
.venv\\Scripts\\activate.bat
```
- PowerShell을 사용하는 경우 다음을 실행합니다:
```
.\\.venv\\Scripts\\Activate.ps1
```
- macOS 또는 Linux에서 다음을 실행합니다:
```
source ./.venv/bin/activate
```
> 💁 이러한 명령은 가상 환경을 만들기 위해 명령을 실행한 동일한 위치에서 실행해야 합니다. .venv 폴더를 탐색할 필요가 없으며, 항상 activate 명령과 아무 명령을 실행하여 패키지를 설치하거나 가상 환경을 만들 때 있떤 폴더에서 코드를 실행해야 합니다.
4. 가상 환경이 활성화 되면, 기본 `python` 명령은 가상 환경을 만드는 데 사용된 Python 버전을 실행합니다. 다음을 실행하여 버전을 가져옵니다:
```
python --version
```
출력은 다음과 유사합니다:
```
(.venv) ➜ nightlight-server python --version
Python 3.9.1
```
> 💁 Python 버전은 다를 수 있습니다 - 버전 3.6 이상이면 좋습니다. 그렇지 않은 경우, 이 폴더를 삭제하고, 최신 버전의 Python을 설치한 후 다시 시도하십시오.
5. 다음 영령을 실행하여 널리 사용되는 MQTT 라이브러리인 [Paho-MQTT](https://pypi.org/project/paho-mqtt/)용 pip 패키지를 설치하십시오.
```
pip install paho-mqtt
```
이 pip 패키지는 가상 환경에만 설치되며 외부에서는 사용할 수 없습니다.
### 서버 코드 작성
이제 서버 코드를 Python으로 작성할 수 있습니다.
### 작업 - 서버 코드 작성
서버 코드를 작성합니다.
1. 터미널 또는 명령줄에서, 가상 환경 내에서 다음을 실행하여 `app.py`라는 Python 파일을 만듭니다:
- Windows에서 다음을 실행합니다:
```
type nul > app.py
```
- macOS 또는 Linux에서 다음을 실행합니다:
```
touch app.py
```
2. VS Code에서 현재 폴더를 엽니다:
```
code .
```
3. VS Code가 시작되면 Python 가상 환경이 활성화됩니다. 이것은 하단 상태 표시줄에 보고됩니다.:
![../../../images/vscode-virtual-env.png](../../../../images/vscode-virtual-env.png)
4. VS Code가 시작될 때 VS Code 터미널이 이미 실행 중이면 가상 환경이 활성화되지 않습니다. 가장 쉬운 방법은 **활성 터미널 인스턴스 종료** 버튼을 사용하여 터미널을 종료하는 것입니다.:
![../../../images/vscode-kill-terminal.png](../../../../images/vscode-kill-terminal.png)
5. \*Terminal -> New Terminal을 선택하거나, `` CTRL+` ``를 눌러 새 VS Code 터미널을 시작합니다. 새 터미널은 가상 환경을 로드하고 이를 활성화하라는 호출이 터미널에 표시됩니다. 가상 환경의 이름(`.venv`)도 프롬프트에 표시됩니다:
```
➜ nightlight-server source .venv/bin/activate
(.venv) ➜ nightlight
```
6. VS Code 탐색기에서 `app.py`파일을 열고 다음 코드를 추가합니다:
```
import json
import time
import paho.mqtt.client as mqtt
id = '<ID>'
client_telemetry_topic = id + '/telemetry'
client_name = id + 'nightlight_server'
mqtt_client = mqtt.Client(client_name)
mqtt_client.connect('test.mosquitto.org')
mqtt_client.loop_start()
def handle_telemetry(client, userdata, message):
payload = json.loads(message.payload.decode())
print("Message received:", payload)
mqtt_client.subscribe(client_telemetry_topic)
mqtt_client.on_message = handle_telemetry
while True:
time.sleep(2)
```
6행에서 `<ID>`를 기기 코드를 생성할 때 사용한 고유 ID로 바꿉니다.
⚠️ 이것은 **반드시** 장치에서 사용한 것과 동일한 ID 여야 합니다, 그렇지 않으면 서버 코드가 올바른 주제를 구독하거나 게시하지 않습니다.
이 코드는 고유한 이름으로 MQTT 클라이언트를 생성하고 _[test.mosquitto.org](http://test.mosquitto.org/)_ 브로커에 연결합니다. 그런 다음 구독된 주제에 대한 메시지를 수신하는 백그라운드 스레드에서 실행되는 처리 루프를 시작합니다.
그런 다음 클라이언트는 원격 분석 주제에 대한 메시지를 구독하고, 메시지가 수신될 때 호출되는 함수를 정의합니다. 원격 측정 메시지가 수신되면, `handle_telemetry` 함수가 호출되어, 수신된 메시지를 콘솔에 인쇄합니다.
마지막으로 무한 루프는 응용 프로그램이 계속 실행되도록 합니다. MQTT 클라이언트는 백그라운드 스레드에서 메시지를 수신하고 있으며 기본 애플리케이션이 실행 중인 동안 항상 실행됩니다.
7. VS Code 터미널에서, 다음을 실행하여 Python 앱을 실행합니다:
```
python app.py
```
앱이 IoT 장치의 메시지 수신을 시작합니다.
8. 장치가 실행 중이고 원격 분석 메시지를 보내고 있는지 확인하십시오. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지가 터미널에 인쇄됩니다.
```
(.venv) ➜ nightlight-server python app.py
Message received: {'light': 0}
Message received: {'light': 400}
```
nightlight-server 가상 환경의 app.py 파일이 전송되는 메시지를 수신하려면 nightlight 가상 환경의 app.py 파일이 실행 중이어야 합니다.
> 💁 이 코드는 [code-server/server](code-server/server) 폴더에서 찾을 수 있습니다.
### 얼마나 자주 원격 분석을 보내야 합니까?
원격 분석에서 중요한 고려 사항 중 하나는 얼마나 자주 데이터를 측정하고 보내는가? 입니다. 대답은 - 상황에 따라 다르다 입니다. 자주 측정하면 측정 변화에 더 빠르게 대응할 수 있지만, 더 많은 전력, 더 많은 대역폭을 사용하고 더 많은 데이터를 생성하고 처리할 더 많은 클라우드 리소스가 필요합니다. 충분히 자주 측정해야 하지만 너무 자주 측정해서는 안 됩니다.
온도 조절기의 경우, 온도가 자주 변하지 않기 때문에 몇 분마다 측정하는 것으로 충분합니다. If you only measure once a day then you could end up heating your house for nighttime temperatures in the middle of a sunny day, whereas if you measure every second you will have thousands of unnecessarily duplicated temperature measurements that will eat into the users' Internet speed and bandwidth (a problem for people with limited bandwidth plans), use more power which can be a problem for battery powered devices like remote sensors, and increase the cost of the providers cloud computing resources processing and storing them.
공장에서 기계가 고장나면 치명적인 피해를 입히고 수백만 달러의 수익 손실을 초래할 수 있는 기계 주변의 데이터를 모니터링하는 경우, 초당 여러 번 측정해야 할 수 있습니다. 기계가 고장나기 전에 중지하고 수정해야 함을 나타내는 원격 측정을 놓치는 것보다 대역폭을 낭비하는 것이 좋습니다.
> 💁 이 상황에서는, 인터넷에 대한 의존도를 줄이기 위해 원격 분석을 먼저 처리하는 에지 장치를 고려할 수 있습니다.
### 연결 끊김
인터넷 연결은 불안정할 수 있으며 일반적으로 중단됩니다. 이러한 상황에서 IoT 장치는 무엇을 해야 합니까 - 데이터가 손실되어야 합니까, 아니면 연결이 복원될 때까지 저장해야 합니까? 다시 말하지만, 대답은 상황에 따라 다릅니다.
온도 조절기의 경우 새 온도 측정이 수행되자마자 데이터가 손실될 수 있습니다. 난방 시스템은 20분 전에 온도가 19°C인 경우 20.5°C였던 것을 신경 쓰지 않습니다. 난방을 켜야 하는지 여부를 결정하는 것은 지금 온도입니다.
기계류의 경우 특히 추세를 찾는 데 사용되는 경우 데이터를 유지해야 할 수 있습니다. 정의된 기간(예들 들어, 지난 1시간)의 데이터를 살펴보고 비정상적인 데이터를 찾아냄으로써 데이터 스트림의 이상을 감지할 수 있는 기계 학습 모델이 있습니다. 이것은 종종 예측 유지보수에 사용되며, 곧 고장날 수 있다는 표시를 찾아 그 전에 수리하거나 교체할 수 있습니다. 시스템에 대한 모든 원격 분석을 전송하여 이상 감지를 위해 처리할 수 있기를 원할 수 있으므로, IoT 장치가 다시 연결되면 인터넷 중단 중에 생성된 모든 원격 분석이 전송됩니다.
IoT 장치 설계자는 인터넷 중단 또는 위치로 인한 신호 손실 중에 IoT 장치를 사용할 수 있는지도 고려해야 합니다. 스마트 온도 조절기는 정전으로 인해 클라우드에 원격 측정을 보낼 수 없는 경우 난방을 제어하기 위해 몇 가지 제한된 결정을 내릴 수 있어야 합니다.
![../../../images/bricked-car.png](../../../../images/bricked-car.png)
MQTT가 연결 손실을 처리하려면, 장치 및 서버 코드가 필요한 경우 메시지 전달을 보장해야 합니다. 예를 들어 전송된 모든 메시지가 응답 주제에 대한 추가 메시지로 응답되도록 요구하고, 그렇지 않은 경우 나중에 재생할 수 있도록 수동으로 대기열에 추가됩니다.
## 명령
명령은 클라우드에서 장치로 보내는 메시지로, 작업을 수행하도록 지시합니다. 대부분의 경우 이것은 액츄에이터를 통해 일종의 출력을 제공하는 것과 관련이 있지만, 재부팅하거나 추가 원격 측정을 수집하여 명령에 대한 응답으로 반환하는 것과 같은 장치 자체에 대한 명령일 수 있습니다.
![An Internet connected thermostat receiving a command to turn on the heating](../../../../images/commands.png)
온도 조절기는 클라우드에서 난방을 켜라는 명령을 받을 수 있습니다. 모든 센서의 원격 측정 데이터를 기반으로 클라우드 서비스가 난방을 켜야 한다고 결정한 경우 관련 명령을 보냅니다.
### MQTT 브로커에 명령 보내기
인터넷 제어 야간 조명의 다음 단계는 서버 코드가 감지하는 조명 수준에 따라 조명을 제어하기 위해 IoT 장치에 명령을 다시 보내는 것입니다
1. VS Code에서 서버 코드 열기
2. `client_telemetry_topic` 명령을 보낼 주제를 정의하는 선언 뒤에 다음 줄을 추가힙니다:
```
server_command_topic = id + '/commands'
```
3. `handle_telemetry` 함수 끝에 다음 코드를 추가합니다:
```
command = { 'led_on' : payload['light'] < 300 }
print("Sending message:", command)
client.publish(server_command_topic, json.dumps(command))
```
이것은 명령 주제에 조명이 300 미만인지 여부에 따라 `led_on` 값이 ture나 false로 설정되도록 JSON 메시지를 보냅니다. 조명이 300보다 작으면, LED를 켜도록 장치에 지시하기 위해 true 가 전송됩니다.
4. 이전과 같이 코드를 실행
5. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지와 전송 중인 명령이 터미널에 기록됩니다.:
```
(.venv) ➜ nightlight-server python app.py
Message received: {'light': 0}
Sending message: {'led_on': True}
Message received: {'light': 400}
Sending message: {'led_on': False}
```
> 💁 원격 측정과 명령은 각각 단일 주제에 대해 전송됩니다. 여러 장치의 원격 분석은 동일한 원격 분석 항목에 표시되고 여러 장치에 대한 명령은 동일한 명령 항목에 나타남을 의미합니다. 특정 장치에 명령을 보내려면, `/commands/device1`, `/commands/device2`와 같은 고유한 장치 id같은 여러 주제를 사용할 수 있습니다. 그런 식으로 장치는 해당 장치에 대한 메시지를 수신할 수 있습니다.
> 💁 이 코드는 [code-commands/server](code-commands/server) 폴더에서 찾을 수 있습니다.
### IoT 장치에서 명령 처리
이제 서버에서 명령이 전송되므로, IoT 장치에 코드를 추가하여 명령을 처리하고 LED를 제어할 수 있습니다.
MQTT 브로커의 명령을 수신하려면 아래 단계를 따르십시오:
- [아두이노 - Wio 터미널](notion://www.notion.so/wio-terminal-commands.md)
- [싱글 보드 컴퓨터 - Raspberry Pi/가상 IoT 장치](notion://www.notion.so/single-board-computer-commands.md)
이 코드가 작성되고 실행되면 조명 수준을 변경하는 실험을 하십시오. 서버 및 장치의 출력을 관찰하고 조명 수준을 변경할 때 LED를 관찰합니다.
### 연결 끊김
오프라인인 IoT 장치에 명령을 보내야 하는 경우 클라우드 서비스는 어떻게 해야 합니까? 다시 말하지만, 대답은 상황에 따라 다릅니다.
최신 명령이 이전 명령보다 우선하면 이전 명령은 무시할 수 있습니다. 클라우드 서비스가 난방을 켜라는 명령을 보낸 다음, 난방을 끄라는 명령을 보내면, 온 명령을 무시하고 재전송하지 않을 수 있습니다.
만약 명령을 순서대로 처리해야 하는 경우, 예를 들어 로봇 팔을 위로 이동한 다음 그래버를 닫는 경우, 연결이 복원되면 순서대로 전송해야 합니다.
✅ 장치 또는 서버 코드가 필요한 경우 MQTT를 통해 명령이 항상 순서대로 전송되고 처리되도록 하려면 어떻게 해야 합니까?
---
## 🚀 도전
지난 세 수업의 과제는 집, 학교 또는 직장에 있는 IoT 장치를 최대한 많이 나열하고 마이크로컨트롤러 또는 단일 보드 컴퓨터 또는 이 둘을 혼합하여 구축되었는지 여부를 결정하고 어떤 센서와 액추에이터를 사용하고 있는지 생각해 보는 것이었습니다.
이러한 장치의 경우, 어떤 메시지를 보내거나 받을 수 있는지 생각해 보십시오. 어떤 원격 측정을 보내나요? 어떤 메시지나 명령을 받을 수 있습니까? 이것이 안전하다고 생각합니까?
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/8)
## 복습 & 독학
[MQTT Wikipedia page](https://wikipedia.org/wiki/MQTT)에서 MQTT에 대해 자세히 알아보세요.
[Mosquitto](https://www.mosquitto.org/) 를 사용하여 MQTT 브로커를 직접 실행하고 IoT 장치와 서버 코드를 연결해보세요.
> 💁 팁 - 기본적으로 Mosquitto는 익명 연결(즉, 사용자 이름과 암호 없이 연결)을 허용하지 않으며, 실행중인 컴퓨터 외부에서 연결을 허용하지 않습니다.
> [`mosquitto.conf` 구성 파일](https://www.mosquitto.org/man/mosquitto-conf-5.html)로 이 문제를 해결할 수 있습니다:
>
> ```
> listener 1883 0.0.0.0
> allow_anonymous true
>
> ```
## 과제
[MQTT를 다른 통신 프로토콜과 비교 및 대조](assignment.ko.md)

@ -0,0 +1,14 @@
# MQTT를 다른 통신 프로토콜과 비교, 대조하기
## 지침
이 강의에서는 MQTT 통신 프로토콜을 다루었습니다. 다른 프로토콜에는 AMQP 와 HTTP/HTTPS 가 있습니다.
이 두가지를 모두 조사하고 MQTT와 비교/대조하십시오. 연결이 끊긴 경우 전력 사용량, 보안, 메시지 지속성애 대해 생각해보세요.
## 평가 기준
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
| ----------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| AMQP 와 MQTT 비교 | AMQP와 MQTT를 비교 및 ​​대조할 수 있으며 전원, 보안, 메시지 지속성을 다룹니다. | AMQP와 MQTT를 부분적으로 비교하고 대조할 수 있으며 전력, 보안, 메시지 지속성 중 두가지를 다룹니다. | AMQP와 MQTT를 부분적으로 비교 및 ​​대조할 수 있으며 전원, 보안, 메시지 지속성 중 하나를 다룹니다. |
| HTTP/HTTPS 와 MQTT 비교 | HTTP/HTTPS와 MQTT를 비교 및 ​​대조할 수 있으며 전원, 보안, 메시지 지속성을 다룹니다. | HTTP/HTTPS와 MQTT를 부분적으로 비교하고 대조할 수 있으며 기능, 보안, 메시지 지속성 중 두가지를 다룹니다. | HTTP/HTTPS를 MQTT와 부분적으로 비교 및 ​​대조할 수 있으며 전원, 보안, 메시지 지속성 중 하나를 다룹니다. |

@ -0,0 +1,53 @@
# 인터넷을 통해 야간 조명 제어하기 - 가상 IoT 하드웨어 및 Raspberry Pi
강의에서 MQTT 브로커에서 Raspberry Pi 또는 가상 IoT 장치로 전송된 명령을 실행하게 됩니다.
## 명령 실행하기
다음 단계는 MQTT 브로커에서 보낸 명령을 실행하고 이에 응답하는 것입니다.
### 작업
명령을 실행합니다.
1. VS Code에서 야간 조명 프로젝트를 엽니다.
2. 가상 IoT 장치를 사용하는 경우, 터미널이 가상 환경을 실행 중인지 확인하십시오. Raspberry Pi 를 사용하는 경우 가상 환경을 사용하지 않습니다.
3. `client_telemetry_topic`의 정위 뒤에 다음 코드를 추가합니다.:
```python
server_command_topic = id + '/commands'
```
`server_command_topic` 는 장치가 LED 명령을 수신하기 위해 실행될 MQTT 주제입니다.
4. main loop 바로 위의 `mqtt_client.loop_start()` 라인 뒤에 다음 코드를 추가합니다:
```python
def handle_command(client, userdata, message):
payload = json.loads(message.payload.decode())
print("Message received:", payload)
if payload['led_on']:
led.on()
else:
led.off()
mqtt_client.subscribe(server_command_topic)
mqtt_client.on_message = handle_command
```
이 코드는 `handle_command` 함수를 정의합니다, 이는 메시지를 JSON 문서로 읽고 `led_on` 속성 값을 찾는 함수입니다. `True` 로 설정되면 LED가 켜지고, 그렇지 않다면 LED는 꺼집니다.
MQTT 클라이언트는 서버가 메시지를 보낼 주제를 실행하고 메시지가 수신될 때 `handle_command` 함수가 호출되도록 설정합니다.
> 💁 `on_message` 핸들러는 실행된 모든 주제에 대해 호출됩니다. 나중에 여러 주제를 수신하는 코드를 작성하면, 핸들러 함수를 거쳐 보내진 `message` 객체의 주제를 얻을 수 있습니다.
5. 과제의 이전 부분과 동일한 방식으로 코드를 실행합니다. 가상 IoT 장치를 사용하는 경우, CounterFit 앱이 실행중이고 LED가 올바른 핀에 생성되었는지 확인하십시오.
6. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지와 전송 중인 명령이 터미널에 기록됩니다. LED도 조명 수준에 따라 켜지고 꺼질 것입니다.
> 💁 이 코드는 [code-commands/virtual-device](../code-commands/virtual-device) 폴더 또는 [code-commands/pi](../code-commands/pi) 폴더에서 찾을 수 있습니다.
😀 장치가 MQTT 브로커의 명령에 응답하도록 성공적으로 코딩했습니다.

@ -0,0 +1,205 @@
# 위치 추적
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-11.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치노트. 사진을 클릭하여 더 크게 보세요
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/21)
## 도입
농부로부터 소비자에게 음식을 전달하는 주요 과정은 트럭, 선박, 비행기 또는 기타 상업 운송 차량에 농산물 상자를 적재하고, 음식을 고객에게 직접 전달하거나 중앙 허브 또는 창고로 가공하는 것을 포함합니다. 농장에서 소비자에 이르는 전체 엔드 투 엔드 프로세스는 *supply-chain*이라는 프로세스의 일부입니다. 아래 영상은 애리조나 주립 대학의 W.P. Carey 경영대학원에서 공급망의 아이디어와 공급망이 어떻게 관리되는지에 대하여 더 자세히 설명하는 영상입니다.
[![What is Supply Chain Management? A video from Arizona State University's W. P. Carey School of Business](https://img.youtube.com/vi/Mi1QBxVjZAw/0.jpg)](https://www.youtube.com/watch?v=Mi1QBxVjZAw)
> 🎥 이미지를 클릭하여 영상을 시청하세요.
IoT 장치를 추가하면 공급망을 획기적으로 개선하여 물품들이 있는 곳을 관리하고, 운송 및 물품 처리를 더 잘 계획하며 문제에 더 빨리 대응할 수 있습니다.
트럭과 같은 일련의 차량을 관리할 때는 주어진 시간에 각 차량이 어디에 있는 지 아는 것이 도움이 됩니다. 차량에는 현재 차량의 위치를 IoT 시스템으로 전송하는 GPS 센서가 장착되어 있어 소유자가 위치를 정확히 파악하고, 선택한 경로를 확인하고, 목적지에 언제 도착할지 알 수 있습니다. 대부분의 차량은 WiFi 서비스 범위 밖에서 작동하므로 이러한 종류의 데이터를 전송하기 위해 셀룰러 네트워크를 사용합니다. 때때로 GPS 센서는 전자 로그 북과 같은 더 복잡한 IoT 장치에 내장됩니다. 이 장치들은 운전자들이 근무 시간에 관한 현지 법을 준수하기 위해 트럭이 운송된 기간 또한 추적합니다.
이번 강의에서는 GPS(Global Positioning System)센서를 사용하여 차량 위치를 추적하는 방법을 배웁니다.
이번 강의에서 다룰 내용은 다음과 같습니다:
* [차량과 연결](#차량과-연결)
* [지리공간 좌표](#지리공간-좌표)
* [Global Positioning Systems (GPS)](#global-positioning-systems-gps)
* [GPS 센서 데이터 읽기](#gps-센서-데이터-읽기)
* [NMEA GPS 데이터](#nmea-gps-데이터)
* [GPS 센서 데이터 디코딩](#gps-센서-데이터-디코딩)
## 차량과 연결
IoT는 *연결된 차량*의 함대를 만들어 상품 운송 방식을 변화시키고 있습니다. 이러한 차량은 중앙 IT 시스템에 연결되어 위치 정보 및 기타 센서 데이터를 보고합니다. 일련의 연결 차량을 보유하는 것은 다음과 같은 다양한 이점을 제공합니다.
* 위치 추적 - 언제든지 차량의 위치를 정확히 파악할 수 있으므로 다음과 같은 이점이 있습니다.
* 차량이 목적지에 도착하기 직전에 미리 알람을 받아 직원이 물픔을 내릴 준비를 할 수 있습니다.
* 도난 차량의 위치 파악이 가능합니다.
* 위치 및 경로 데이터를 교통 문제와 결합하여 운송 중간에 차량의 경로를 변경할 수 있습니다.
* 세금을 준수합니다. 일부 국가에서는 공공 도로에서 주행하는 차량에 주행 거리 만큼의 요금을 부과합니다. ([New Zealand's RUC](https://www.nzta.govt.nz/vehicles/licensing-rego/road-user-charges/)), 따라서 차량이 공공 도로에서 주행하는 시간과 개인 도로에서 주행하는 시간을 파악하면 세금을 더 쉽게 계산할 수 있습니다.
* 고장이 발생할 경우 유지보수 인력을 어디에 파견해야 하는지 파악할 수 있습니다.
* 운전자 원격 측정 - 운전자가 제한 속도를 준수하고 적절한 속도로 코너링하며 조기에 효율적으로 제동을 걸고 안전하게 주행할 수 있도록 보장합니다. 연결된 차향에는 사고를 기록하는 카메라가 있을 수도 있습니다. 이는 좋은 운전자들에게 할인된 요금을 주는 보험과 연결될 수 있습니다.
* 운전자 시간 준수 - 운전자가 엔진을 켜고 끄는 시간을 기준으로 확인하여 법적으로 허용된 시간 동안만 운전하도록 보장합니다.
이러한 이점들은 결합될 수 있습니다 - 예를 들어, 운전자가 허용된 운전 시간 내에 목적지에 도착할 수 없는 경우, 위치 추적과 결합하여 운전자를 재 경로화 할 수 있습니다. 이는 온도 제어 트럭의 온도 데이터는 현재 경로로 인해 차량이 온도 유지를 할 수 없는 경우 차량을 재 구매 할 수 있도록 합니다.
> 🎓 물품 유통은 농장에서 슈퍼마켓으로 하나 이상의 창고를 통해 상품을 한 장소에서 다른 장소로 운송하는 과정입니다. 한 농부가 토마토 상자를 트럭에 싣고 중앙 창고로 배달한 다음, 슈퍼마켓으로 배달되는 트럭에는 농부의 토마토와 다른 종류의 농산물들이 혼합되어 있습니다..
차량 추적의 핵심 구성 요소는 GPS입니다. GPS는 지구상 어디에서나 위치를 정확히 파악할 수 있는 센서입니다. 이 강의에서는 GPS 센서를 사용하는 방법부터 시작하여 지구의 위치를 정의하는 방법에 대해 알아봅니다.
## 지리공간 좌표
지리공간 좌표는 지구 표면의 점을 정의하는 데 사용되며, 컴퓨터 화면의 픽셀을 그리거나 십자 스티치로 꿰매는 데 좌표를 사용할 수 있는 방법과 유사합니다. 단일 점의 경우 좌표 쌍이 있습니다. 예를 들어, 미국 워싱턴주 레드몬드에 있는 마이크로소프트 캠퍼스는 47.6423109, -122.1390293에 위치해 있습니다.
### 위도와 경도
지구는 3차원 원인 구입니다. 이 때문에 점은 원의 기하학적 구조와 마찬가지로 360도로 나누어 정의된다. 위도는 남북의 의치를 측정하고 경도는 동서의 위치를 측정한다.
> 💁 아무도 원이 360도로 나뉘는 원래의 이유를 알지 못합니다. 위키피디아의 [degree (angle)](https://wikipedia.org/wiki/Degree_(angle))페이지에서 가능성 있는 몇가지 이유들을 다룹니다.
![Lines of latitude from 90° at the North Pole, 45° halfway between the North Pole and the equator, 0° at the equator, -45° halfway between the equator and the South Pole, and -90° at the South Pole](../../../../images/latitude-lines.png)
위도는 북반구와 남반구를 각각 90°씩 나누며 지구를 돌고 적도와 평행하게 달리는 선을 이용해 측정합니다. 적도는 0도, 북극은 90도, 남극은 -90도, 남극은 -90도입니다.
경도는 동서로 측정된 위치로 측정됩니다. 경도의 0°원점은 *원초 자오선*이라고 불리며, 1884년에 [영국 그리니치에 있는 영국 왕립 천문대](https://wikipedia.org/wiki/Royal_Observatory,_Greenwich))를 통과하는 북극에서 남극점까지의 선으로 정의되었습니다.
![Lines of longitude that go from -180° to the west of the Prime Meridian, to 0° on the Prime Meridian, to 180° east of the Prime Meridian](../../../../images/longitude-meridians.png)
> 🎓 자오선은 북극점에서 남극점까지 반원을 이루는 가상의 직선이다.
점의 경도를 측정하려면 본초 자오선에서 해당 점을 통과하는 자오선까지의 적도 주위의 좌표를 측정합니다. 경도는 본초 자오선에서 서경 180도에서 동경 180도까지이다. 180°와 -180°는 같은 점, 즉 반경 또는 180도를 가리킵니다. 이것은 본초 자오선에서 지구 반대편에 있는 자오선이다.
> 💁 반자오선은 국제일자선과 거의 같은 위치에 있는 것과 혼동해서는 안 됩니다. 그러나 국제일자선은 직선이 아니며 지정학적 경계에 맞게 다향합니다.
✅ 조사 해 봅시다 : 현재 위치의 위도와 경도를 찾으십시오.
### Degree, 분 및 초 VS 십진수 Degree
전통적으로 위도와 경도의 측정은 시간과 거리를 최초로 측정하고 기록한 고대 바빌로니아인들이 사용했던 숫자 체계인 Base-60을 사용하여 수행되었습니다. 여러분은 아마 시간을 60분으로, 분을 60초로 나누는 것을 깨닫지도 못한 채 매일 60진법을 사용할 것입니다.
경도와 위도는 Degree, 분, 초 단위로 측정되며 1분은 Degree의 1/60, 1초는 1/60분이다.
예를 들어, 적도에서:
* 위도 1°는 **111.3km**입니다.
* 위도의 1분은 111.3/60 = **1.855km**
* 위도의 1초는 1.855/60 = **0.031km**
1분이라는 표현은 하나의 인용구이고, 1초라는 표현은 이중 인용구입니다. 예를 들어, 2도, 17분, 43초는 2°17'43"로 기록됩니다. 초의 일부는 소수로 주어지는데, 예를 들어 0.0초는 0°0'0.5"입니다.
컴퓨터는 Base-60에서 작동하지 않으므로 대부분의 컴퓨터 시스템에서 GPS 데이터를 사용할 때 이러한 좌표는 십진법으로 제공됩니다. 예를 들어, 2°17'43"은 2.295277입니다. Degree 기호는 일반적으로 생략됩니다.
점에 대한 좌표는 항상 "위도, 경도"로 제공되므로 47.6423109,-122.117198의 이전 마이크로소프트 캠퍼스의 예는 다음과 같습니다.
* 위도 47.6423109(적도에서 북쪽으로 47.6423109도)
* 경도 -122.1390293(원초 자오선에서 서쪽으로 122.1390293도)
![The Microsoft Campus at 47.6423109,-122.117198](../../../../images/microsoft-gps-location-world.png)
## Global Positioning Systems (GPS)
GPS 시스템은 사용자의 위치를 찾기 위해 지구 주위를 도는 여러 위성을 사용합니다. 여러분은 아마도 휴대폰의 지도 앱(예: Apple Maps 또는 Google Maps)에서 위치를 찾거나 Uber 또는 Lyft와 같은 놀이기구 호출 앱에서 또는 자동차에서 위성 내비게이션(sat-nav)을 사용할 때 위치를 확인하기 위해 GPS 시스템을 사용했을 것입니다.
> 🎓 '위성항법'의 위성은 GPS 위성입니다!
GPS 시스템은 각 위성의 현재 위치와 정확한 타임스탬프로 신호를 보내는 다수의 위성을 가지고 작동합니다. 이러한 신호는 전파를 통해 전송되며 GPS 센서의 안테나에 의해 감지됩니다. GPS 센서가 이러한 신호를 감지하고 현재 시간을 사용하여 신호가 위성에서 센서에 도달하는 데 걸린 시간을 측정합니다. 전파의 속도가 일정하기 때문에 GPS 센서는 전송된 타임스탬프를 사용하여 센서가 위성으로부터 얼마나 떨어져 있는지 알아낼 수 있습니다. 적어도 3개의 위성으로부터 온 데이터를 전송된 위치와 결합함으로써, GPS 센서는 지구상의 위치를 정확히 찾아낼 수 있다.
> 💁 GPS 센서는 전파를 감지하기 위해 안테나가 필요합니다. 트럭과 자동차에 내장된 안테나는 일반적으로 앞유리나 지붕에 신호를 받을 수 있도록 배치되어 있습니다. 스마트폰이나 IoT 기기와 같은 별도의 GPS 시스템을 사용하는 경우, GPS 시스템이나 전화기에 내장된 안테나가 앞쪽에 장착되는 등 하늘을 바라 볼 수 있도록 해야 합니다.
![By knowing the distance from the sensor to multiple satellites, the location be calculated](../../../../images/gps-satellites.png)
GPS 위성은 센서 위 고정점이 아닌 지구를 돌고 있어 위치정보에는 위도와 경도 뿐만 아니라 해수면 이상 고도도 포함됩니다.
과거 GPS는 미군이 시행하는 정확도에 한계가 있어, 정확도가 5m 안팎으로 제한되었습니다. 이 제한은 2000년에 사라지고 30cm의 정확도를 허용했습니다. 신호 간섭으로 인해 이 정확도를 얻는 것이 항상 가능한 것은 아닙니다.
✅ 스마트폰이 있으면 지도 앱을 실행하여 위치가 얼마나 정확한지 확인해보세요. 전화기가 더 정확한 위치를 얻기 위해 여러 위성을 감지하는 데 짧은 시간이 걸릴 수 있습니다.
> 💁 이 위성들은 믿을 수 없을 정도로 정확한 원자 시계를 포함하고 있지만, 그것들은 지구의 원자 시계에 비해 하루에 38마이크로초(0.0000038초)씩 표류하는데, 이는 아인슈타인의 특수 상대성 이론과 일반 상대성 이론에 의해 예측된 속도가 증가함에 따라 시간이 느려지기 때문입니다. - 위성들은 지구의 자전보다 더 빨리 이동합니다. 이 드리프트는 특수 상대성 이론과 일반 상대성 이론의 예측을 증명하는 데 사용되어 왔으며 GPS 시스템의 설계에서 조정되어야합니다. 문자 그대로 GPS 위성에서는 시간이 더 느리게 흐릅니다.
GPS 시스템은 미국, 러시아, 일본, 인도, 유럽연합, 중국을 포함한 많은 국가와 정치 연합에 의해 개발되고 배치되었습니다. 최신 GPS 센서는 이러한 시스템 대부분에 연결하여 더 빠르고 정확한 수정을 할 수 있습니다.
> 🎓 각 비군사적 사역에서의 위성그룹을 constellations라고 부릅니다.
>
## GPS 센서 데이터 읽기
대부분의 GPS 센서는 UART를 통해 GPS 데이터를 전송합니다.
> ⚠️ UART는[project 2, lesson 2](../../../../2-farm/lessons/2-detect-soil-moisture/README.md#universal-asynchronous-receiver-transmitter-uart)에서 다루었습니다. 필요한 경우 해당 강의를 참조하세요.
IoT 장치의 GPS 센서를 사용하여 GPS 데이터를 가져올 수 있습니다.
### 작업 - GPS 센서를 연결하고 GPS 데이터를 읽어봅시다.
IoT 장치를 사용하여 GPS 데이터를 읽으려면 관련 가이드를 참조하십시오. :
* [Arduino - Wio Terminal](../wio-terminal-gps-sensor.md)
* [Single-board computer - Raspberry Pi](../pi-gps-sensor.md)
* [Single-board computer - Virtual device](../virtual-device-gps-sensor.md)
## NMEA GPS 데이터
당신이 코드를 실행했을 때, 당신은 출력에서 횡설수설하는 것처럼 보일 수 있는 것을 보았을 것입니다. 이것은 사실 표준 GPS 데이터이고, 모두 의미가 있습니다.
GPS 센서는 NMEA 0183 표준을 사용하여 NMEA 메시지로 데이터를 출력합니다. NMEA는 해양 전자 간 통신 표준을 설정하는 미국의 무역 조직인 [National Marine Electronics Association](https://www.nmea.org)의 약자입니다.
> 💁 이 표준은 독점적이며 최소 미화 2,000 달러에 판매되지만, 이에 대한 충분한 정보는 대부분의 표준이 리버스 엔지니어링되었으며 오픈 소스 및 기타 비상업 코드에서 사용될 수 있습니다.
이 메시지는 텍스트 기반입니다. 각 메시지는 `$` 문자로 시작하는 *문장*으로 구성되며, 그 다음으로 메시지의 소스를 나타내는 2개의 문자(예: 미국 GPS 시스템의 경우 GP, 러시아 GPS 시스템의 경우 GN), 메시지 유형을 나타내는 3개의 문자로 구성됩니다. 메시지의 나머지 부분은 쉼표로 구분된 필드로, 새 줄 문자로 끝납니다.
수신할 수 있는 메시지 유형은 다음과 같습니다.:
| 유형 | 설명 |
| ---- | ----------- |
| GGA | GPS 센서의 위도, 경도, 고도를 포함한 GPS 수정 데이터와 이 수정을 계산하기 위해 볼 수 있는 위성의 수. |
| ZDA | 현지 시간대를 포함한 현재 날짜 및 시간 |
| GSV | 보기에 있는 위성의 세부 정보 - GPS 센서가 신호를 감지할 수 있는 위성으로 정의됩니다. |
> 💁 GPS 데이터에는 타임스탬프가 포함되어 있으므로 IoT 장치는 NTP 서버나 내부 실시간 시계에 의존하지 않고 GPS 센서에서 필요한 경우 시간을 얻을 수 있습니다.
GGA 메시지는 방향을 나타내는 단일 문자와 함께 `(dd)dmm.mmm` 형식을 사용하는 현재 위치를 포함합니다. 형식의 `d`는 도, `m`은 분, 초는 분 단위입니다. 예를 들어, 2°17'43"은 217.7166667 - 2도, 17.7166667분입니다.
The direction character can be `N` or `S` for latitude to indicate north or south, and `E` or `W` for longitude to indicate east or west. For example, a latitude of 2°17'43" would have a direction character of `N`, -2°17'43" would have a direction character of `S`.
방향 문자는 북쪽이나 남쪽을 나타내는 위도의 경우 `N`이나 `S`, 동쪽이나 서쪽을 나타내는 경도의 경우 `E``W`가 될 수 있습니다. 예를 들어, 2°17'43"의 위도는 `N`의 방향 문자를 가지며, -2°17'43"의 방향 문자는 `S`의 방향 문자를 갖는다.
예시 NMEA 문장 `$GNGGA, 020604.001, 4738.538654, N, 12208.341758, W, 1,3,164.7, M, -17.1, M, *67`
* 위도 부분은 `4738.538654,N`으로 소수점에서 47.6423109로 변환된다. `4738.538654`는 47.6423109, 방향은 `N`(북)이므로 양위도이다.
* 경도 부분은 -122.1390293(10진수)으로 환산한 `12208.341758,W`이다. `12208.341758`은 122.1390293°이고 방향은 `W`(서쪽)이므로 음경이다.
## GPS 센서 데이터 디코딩
원시 NMEA 데이터를 사용하는 것보다 더 유용한 형식으로 디코딩하는 것이 좋습니다. 원시 NMEA 메시지에서 유용한 데이터를 추출하는 데 사용할 수 있는 여러 오픈 소스 라이브러리가 있습니다.
### 작업 - GPS 센서 데이터를 디코딩 해 봅시다
IoT 장치를 사용하여 관련 가이드를 통해 GPS 센서 데이터를 디코딩합니다.:
* [Arduino - Wio Terminal](../wio-terminal-gps-decode.md)
* [Single-board computer - Raspberry Pi/Virtual IoT device](../single-board-computer-gps-decode.md)
---
## 🚀 도전
여러분만의 NMEA 디코더를 쓰세요! NMEA 문장을 해독하기 위해 타사 라이브러리에 의존하는 대신, 여러분은 NMEA 문장에서 위도와 경도를 추출하기 위해 여러분만의 디코더를 작성할 수 있을까요?
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/22)
## 복습 및 독학
* 지리공간 좌표에 대한 자세한 내용은 [Wikipedia의 지리 좌표 시스템 페이지](https://wikipedia.org/wiki/Geographic_coordinate_system))를 참조하세요.
* [위키피디아의 프라임 자오선 페이지](https://wikipedia.org/wiki/Prime_meridian#Prime_meridian_on_other_planetary_bodies)에서 지구 외 다른 천체의 프라임 자오선에 대해 자세히 읽어보세요.
* EU, 일본, 러시아, 인도 및 미국과 같은 다양한 세계 정부 및 정치 연합의 다양한 GPS 시스템을 찾아보세요.
## 과제
[다른 GPS 데이터 조사](../assignment.md)

@ -0,0 +1,352 @@
# 위치 데이터 시각화
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-13.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 더 큰 버전을 보려면 이미지를 클릭하십시오.
이 비디오는 이번 단원에서 다룰 Azure Maps의 IoT에 대한 개요를 제공합니다.
[![Azure Maps - The Microsoft Azure Enterprise Location Platform](https://img.youtube.com/vi/P5i2GFTtb2s/0.jpg)](https://www.youtube.com/watch?v=P5i2GFTtb2s)
> 🎥 위 이미지를 클릭하면 영상을 볼 수 있습니다.
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/25)
## 개요
지난 수업에서는 서버리스 코드를 사용하여 스토리지 컨테이너의 클라우드에 저장하기 위해 센서에서 GPS 데이터를 가져오는 방법을 배웠습니다. 이제 Azure 맵에서 해당 지점을 시각화하는 방법을 알아봅니다. 웹 페이지에서 지도를 만드는 방법, GeoJSON 데이터 형식 및 이를 사용하여 지도에 캡처된 모든 GPS 지점을 표시하는 방법을 배우게 됩니다.
이 강의에서는 다음을 다룰 것입니다:
- [데이터 시각화란](#what-is-data-visualization)
- [지도 서비스](#map-services)
- [Azure Maps 리소스 만들기](#create-an-azure-maps-resource)
- [웹 페이지에 지도 표시](#show-a-map-on-a-web-page)
- [GeoJSON 형식](#the-geojson-format)
- [GeoJSON을 사용하여 지도에 GPS 데이터 표시하기](#plot-gps-data-on-a-map-using-geojson)
> 💁 This lesson will involve a small amount of HTML and JavaScript. If you would like to learn more about web development using HTML and JavaScript, check out [Web development for beginners](https://github.com/microsoft/Web-Dev-For-Beginners).
## 데이터 시각화란
데이터 시각화는 이름에서 알 수 있듯이 사람이 이해하기 쉽게 데이터를 시각화하는 것입니다. 일반적으로 차트 및 그래프와 연관되지만 사람이 데이터를 더 잘 이해할 수 있을 뿐만 아니라 의사 결정을 내리는 데 도움이 되도록 데이터를 그림으로 나타내는 모든 방법을 말합니다.
간단한 예를 들자면, 지난 농장 프로젝트에서 토양 수분 설정을 캡처했습니다. 2021년 6월 1일에 매시간 수집된 토양 수분 데이터 테이블은 다음과 같을 수 있습니다:
| Date | Reading |
| ---------------- | ------: |
| 01/06/2021 00:00 | 257 |
| 01/06/2021 01:00 | 268 |
| 01/06/2021 02:00 | 295 |
| 01/06/2021 03:00 | 305 |
| 01/06/2021 04:00 | 325 |
| 01/06/2021 05:00 | 359 |
| 01/06/2021 06:00 | 398 |
| 01/06/2021 07:00 | 410 |
| 01/06/2021 08:00 | 429 |
| 01/06/2021 09:00 | 451 |
| 01/06/2021 10:00 | 460 |
| 01/06/2021 11:00 | 452 |
| 01/06/2021 12:00 | 420 |
| 01/06/2021 13:00 | 408 |
| 01/06/2021 14:00 | 431 |
| 01/06/2021 15:00 | 462 |
| 01/06/2021 16:00 | 432 |
| 01/06/2021 17:00 | 402 |
| 01/06/2021 18:00 | 387 |
| 01/06/2021 19:00 | 360 |
| 01/06/2021 20:00 | 358 |
| 01/06/2021 21:00 | 354 |
| 01/06/2021 22:00 | 356 |
| 01/06/2021 23:00 | 362 |
인간으로서, 데이터를 이해하는 것은 어려울 수 있습니다. 이는 아무런 의미가 없는 숫자입니다. 이 데이터를 시각화하는 첫 번째 단계는 꺾은선형 차트에 그리는 것입니다:
![A line chart of the above data](../../../../images/chart-soil-moisture.png)
이는 언제 자동 급수 시스템이 토양 수분 판독값 450을 읽어 켜지는지를 나타내는 선을 추가하여 더욱 발전시킬 수 있습니다.
![A line chart of soil moisture with a line at 450](../../../../images/chart-soil-moisture-relay.png)
이 차트는 토양 수분 수준 뿐만 아니라, 급수 시스템이 켜진 지점을 매우 빠르게 보여줍니다.
차트는 데이터를 시작화하는 유일한 도구가 아닙니다. 날씨를 추적하는 IoT 장치에는 흐린 날의 구름 기호, 비 오는 날의 비구름 등과 같은 기호를 사용하여 날씨 상태를 시각화하는 웹 앱, 또는 모바일 앱이 있을 수 있습니다. 데이터를 시각화하는 방법은 무수히 많고, 진지하고 재미있는 경우도 많습니다.
✅ 시각화된 데이터를 보는 방법에 대해 생각해 보십시오. 어떤 방법이 가장 명확했고 가장 빠르게 결정을 내릴 수 있었습니까?
최고의 시각화를 통해 사람과 사람이 빠르게 의사 결정을 내릴 수 있습니다. 예를 들어 산업용 기계의 모든 판독값을 표시하는 게이지를 갖는 것은 처리하기 어렵지만 무언가 잘못되었을 때 빨간색 표시등이 깜박이면 사람이 결정을 내릴 수 있습니다. 때때로 최고의 시각화는 번쩍이는 불빛입니다!
GPS 데이터로 작업할 때 가장 명확한 시각화는 지도에 데이터를 표시하는 것입니다. 예를 들어 배달 트럭을 표시하는 지도는 가공 공장의 작업자가 트럭이 도착하는 시기를 확인하는 데 도움이 될 수 있습니다. 이 지도가 현재 위치에 있는 트럭 사진보다 더 많은 것을 보여주고 트럭의 내용물에 대한 아이디어를 제공한다면 공장의 작업자는 그에 따라 계획을 세울 수 있습니다. 만약 근처에 냉장 트럭이 보이면 냉장고에 공간을 마련해야 한다는 것을 압니다.
## 지도 서비스
지도로 작업하는 것은 흥미롭습니다. Bing Maps, Leaflet, Open Street Maps 및 Google Maps와 같이 선택할 수 있는 것이 많습니다. 이 단원에서는 [Azure Maps](https://azure.microsoft.com/services/azure-maps/?WT.mc_id=academic-17441-jabenn) 과 GPS 데이터를 표시하는 방법에 대해 알아봅니다.
![The Azure Maps logo](../../../../images/azure-maps-logo.png)
Azure Maps는 "새로운 매핑 데이터를 사용하여 웹 및 모바일 애플리케이션에 지리학적 환경을 제공하는 지리 공간 서비스 및 SDK 모음"입니다. 개발자에게는 권장 교통 경로 제공, 교통 사고에 대한 정보 제공, 실내 내비게이션, 검색 기능, 고도 정보, 날씨 서비스 등을 수행할 수 있는 아름다운 대화형 지도를 만드는 도구가 제공됩니다.
✅ [매핑 코드 샘플](https://docs.microsoft.com/samples/browse?WT.mc_id=academic-17441-jabenn&products=azure-maps) 로 실험하기
지도의 빈 캔버스, 타일, 위성 이미지, 도로가 중첩된 위성 이미지, 다양한 유형의 회색조 지도, 고도를 표시하는 음영 부조가 있는 지도, 야경 지도, 고대비 지도로 지도를 표시할 수 있습니다. [Azure Event Grid](https://azure.microsoft.com/services/event-grid/?WT.mc_id=academic-17441-jabenn)와 통합하여 지도에서 실시간 업데이트를 받을 수 있습니다. 핀치, 드래그와 클릭과 같은 이벤트에 지도가 반응할 수 있도록 다양한 제어를 활성화하여 지도의 동작과 모양을 제어할 수 있습니다. 지도의 모양을 제어하기 위해 버블, 라인, 다각형, 히트 맵 등을 포함하는 레이어를 추가할 수 있습니다. 구현하는 지도의 스타일은 선택한 SDK에 따라 다릅니다.
[REST API](https://docs.microsoft.com/javascript/api/azure-maps-rest/?WT.mc_id=academic-17441-jabenn&view=azure-maps-typescript-latest), [Web SDK](https://docs.microsoft.com/azure/azure-maps/how-to-use-map-control?WT.mc_id=academic-17441-jabenn), 모바일 어플리케이션을 만드는 경우, [Android SDK](https://docs.microsoft.com/azure/azure-maps/how-to-use-android-map-control-library?WT.mc_id=academic-17441-jabenn&pivots=programming-language-java-android)를 활용하여 Azure Maps API에 접근할 수 있습니다.
이 수업에서는 웹 SDK를 사용하여 지도와 센서의 GPS 위치 경로를 표시합니다.
## Azure Maps 리소스 만들기
첫 번째 단계는 Azure Maps 계정을 만드는 것입니다.
### 작업 - Azure Maps 리소스 만들기
1. 터미널 또는 명령 프롬프트에서 다음 명령을 실행하여 `gps-sensor` 리소스 그룹에 Azure Maps 리소스를 만듭니다:
```sh
az maps account create --name gps-sensor \
--resource-group gps-sensor \
--accept-tos \
--sku S1
```
이렇게 하면 Azure Maps의 `gps-sensor`라는 리소스가 생성됩니다. 사용 중인 계층은 `S1`으로, 다양한 기능을 포함하는 유료 계층이지만, 무료로 제공되는 부분이 있습니다.
> 💁 Azure Maps의 사용 비용을 보려면, [Azure Maps 가격 페이지](https://azure.microsoft.com/pricing/details/azure-maps/?WT.mc_id=academic-17441-jabenn)를 확인하세요.
2. 지도 리소스에 대한 API 키가 필요합니다. 이 키를 얻으려면 다음 명령어를 사용하십시오:
```sh
az maps account keys list --name gps-sensor \
--resource-group gps-sensor \
--output table
```
`PrimaryKey` 값을 복사해 놓으십시오.
## 웹 페이지에 지도 표시
이제 웹 페이지에 지도를 표시하는 다음 단계를 수행할 수 있습니다. 작은 웹 앱에 대해 하나의 `html` 파일만 사용 합니다. 상품이나 팀 환경에서는 웹 앱에 움직이는 부분이 더 많을 가능성이 높습니다!
### 작업 - 웹 페이지에 지도 표시
1. 로컬 컴퓨터의 폴더에 index.html 이라는 파일을 만듭니다. 지도를 보관할 HTML 마크업을 추가합니다:
```html
<html>
<head>
<style>
#myMap {
width: 100%;
height: 100%;
}
</style>
</head>
<body onload="init()">
<div id="myMap"></div>
</body>
</html>
```
지도에 `myMap` `div` 가 로드됩니다. 몇 가지 스타일을 사용하면 페이지의 너비와 높이를 확장할 수 있습니다.
> 🎓 `div` 는 웹 페이지의 섹션으로, 이름과 스타일을 지정할 수 있습니다.
2. 여는 `<head>` 태그 아래에, 외부 스타일시트를 추가하여 지도 표시를 제어하고, 웹 SDK의 외부 스크립트를 추가하여 해당 동작을 관리합니다:
```html
<link
rel="stylesheet"
href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"
type="text/css"
/>
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
```
이 스타일시트에는 지도 모양에 대한 설정이 포함되어 있으며, 스크립트 파일에는 지도를 로드하는 코드가 포함되어 있습니다. 이 코드를 추가하는 것은 C++ 헤더 파일을 포함하거나 Python 모듈을 가져오는 것과 유사합니다.
3. 해당 스크립트 아래에, 스크립트 블록을 추가하여 지도를 시작합니다.
```javascript
<script type='text/javascript'>
function init() {
var map = new atlas.Map('myMap', {
center: [-122.26473, 47.73444],
zoom: 12,
authOptions: {
authType: "subscriptionKey",
subscriptionKey: "<subscription_key>",
}
});
}
</script>
```
`<subscription_key>` 를 Azure Maps 계정의 API 키로 변경합니다.
웹 브라우저에서 `index.html` 페이지를 열면, 지도가 로드되고, 시애틀 지역에 초점이 맞춰져 있어야 합니다.
![A map showing Seattle, a city in Washington State, USA](../../../../images/map-image.png)
✅ 확대/축소 및 중심 매개변수를 변경하여 지도 표시를 실험합니다. 데이터의 위도와 경도에 해당하는 다른 좌표를 추가하여 지도의 중심을 다시 맞출 수 있습니다.
> 💁 웹 앱을 로컬에서 사용하는 더 좋은 방법은 [http-server](https://www.npmjs.com/package/http-server)를 설치하는 것입니다. 이 도구를 사용하려면 [node.js](https://nodejs.org/) 와 [npm](https://www.npmjs.com/) 이 설치되어 있어야 합니다. 도구들이 설치되면 `index.html``http-server` 타입의 위치를 다룰 수 있습니다. 웹 앱은 로컬 웹 서버 [http://127.0.0.1:8080/](http://127.0.0.1:8080/)에서 열립니다.
## GeoJSON 형식
이제 지도가 표시되는 웹 앱이 준비되었으므로, 저장소 계정에서 GPS 데이터를 추출하여 지도 위에 있는 마커를 레이어에 표시해야 합니다. 그 전에 Azure Maps에 필요한 [GeoJSON](https://wikipedia.org/wiki/GeoJSON) 형식을 살펴보겠습니다.
[GeoJSON](https://geojson.org/)은 지리적 특정 데이터를 처리하도록 설계된 특수 형식의 개방형 표준 JSON 사양입니다. GeoJSON 파일을 디버깅하는 데 유용한 도구인 [geojson.io](https://geojson.io) 를 사용하여 샘플 데이터를 테스트하여 이에 대해 알아볼 수 있습니다 .
샘플 GeoJSON 데이터는 다음과 같습니다:
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-2.10237979888916, 57.164918677004714]
}
}
]
}
```
특히 흥미로운 점은 데이터가 `FeatureCollection`내에 `Feature`로 중첩되는 방식입니다. 해당 객체 내에서 위도와 경도를 나타내는 `coordinates`가 있는 `geometry`를 찾을 수 있습니다.
✅ When building your geoJSON, pay attention to the order of `latitude` and `longitude` in the object, or your points will not appear where they should! GeoJSON expects data in the order `lon,lat` for points, not `lat,lon`.
`Geometry` 는 단일 점 또는 다각형과 같은 다른 유형을 가질 수 있습니다. 이 예시에서는 경도와 위도라는 두 좌표가 지정된 점입니다.
✅ Azure Maps는 표준 GeoJSON 과 원 및 기타 기하 도형을 그리는 기능을 비롯한 몇 가지 [향상된 기능들](https://docs.microsoft.com/azure/azure-maps/extend-geojson?WT.mc_id=academic-17441-jabenn) 을 제공합니다.
## GeoJSON을 사용하여 지도에 GPS 데이터 표시하기
이제 이전 학습에서 빌드한 저장소에서 데이터를 사용할 준비가 되었습니다. 참고로 Blob 저장소에 여러 파일로 저장되므로 Azure Maps에서 데이터를 사용할 수 있도록 파일을 검색하고 구문 분석해야 합니다.
### 작업 - 웹 페이지에서 액세스할 스토리지 구성
데이터를 가져오기 위해 저장소를 호출하면 브라우저 콘솔에서 발생하는 오류를 보고 놀랄 수 있습니다. 외부 웹 앱이 해당 데이터를 읽을 수 있도록 하려면 이 저장소에서 [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) 에 대한 권한을 설정해야 하기 때문입니다.
> 🎓 CORS는 "Cross-Origin Resource Sharing"을 나타내며 일반적으로 보안상의 이유로 Azure에서 명시적으로 설정해야 합니다. 이는 데이터에 접근할 수 있는 것으로 예상하지 않는 사이트를 중지합니다.
1. 다음 명령을 실행하여 CORS를 활성화합니다:
```sh
az storage cors add --methods GET \
--origins "*" \
--services b \
--account-name <storage_name> \
--account-key <key1>
```
`<storage_name>` 을 저장소 계정의 이름으로 바꿉니다. `<key1>` 를 저장소 계정의 키로 바꿉니다.
이 명령은 모든 웹 사이트(와일드 카드 `*` 는 모두를 의미)가 저장소 계정에서 _GET_ 요청, 즉 데이터 가져오기를 수행할 수 있습니다. `--services b` 는 blobs에만 이 설정을 적용한다는 의미입니다.
### 작업 - 저장소에서 GPS 데이터 로드
1. `init` 함수의 전체 내용을 다음 코드로 변경합니다:
```javascript
fetch(
"https://<storage_name>.blob.core.windows.net/gps-data/?restype=container&comp=list"
)
.then((response) => response.text())
.then((str) => new window.DOMParser().parseFromString(str, "text/xml"))
.then((xml) => {
let blobList = Array.from(xml.querySelectorAll("Url"));
blobList.forEach(async (blobUrl) => {
loadJSON(blobUrl.innerHTML);
});
})
.then((response) => {
map = new atlas.Map("myMap", {
center: [-122.26473, 47.73444],
zoom: 14,
authOptions: {
authType: "subscriptionKey",
subscriptionKey: "<subscription_key>",
},
});
map.events.add("ready", function () {
var source = new atlas.source.DataSource();
map.sources.add(source);
map.layers.add(new atlas.layer.BubbleLayer(source));
source.add(features);
});
});
```
`<storage_name>` 를 저장소 계정의 이름으로 변경합니다. `<subscription_key>` Azure Maps 계정의 API 키로 변경합니다.
여기에서 여러 가지 일이 발생합니다. 먼저 코드는 저장소 계정 이름을 사용하여 빌드된 URL 엔드포인트를 사용하여 Blob 컨테이너에서 GPS 데이터를 가져옵니다. 이 URL은 리소스 유형이 컨테이너(`restype=container`) 임을 나타내는 `gps-data`에서 검색하고 모든 Blob 에 대한 정보를 나열합니다. 이 목록은 Blob 자체를 반환하지 않지만 Blob 데이터를 로드하는 데 사용할 수 있는 각 Blob에 대한 URL을 반환합니다.
> 💁 You can put this URL into your browser to see details of all the blobs in your container. Each item will have a `Url` property that you can also load in your browser to see the contents of the blob. 이 URL을 브라우저에 넣으면 컨테이너의 모든 Blob에 대한 세부 정보를 볼 수 있습니다. 각 항목은 Blob의 콘텐츠를 보기 위해 브라우저에 로드할 수 있는 `Url` 속성이 있습니다.
이 코드는 다음에 생성될 `loadJSON` 함수를 호출하여 각 Blob을 로드합니다. 그런 다음 지도 제어를 만들고 `ready` 이벤트에 코드를 추가합니다. 이 이벤트는 웹 페이지에 지도가 표시될 때 호출됩니다.
ready 이벤트는 나중에 채워질 GeoJSON 데이터를 포함하는 컨테이너인 Azure Maps 데이터 원본을 만듭니다. 그런 다음 이 데이터 원본을 사용하여 GeoJSON의 각 지점 중심에 있는 지도의 원 집합인 버블 레이어를 만듭니다.
2. `init` 함수 아래의 스크립트 블록에 `loadJSON` 함수를 추가합니다:
```javascript
var map, features;
function loadJSON(file) {
var xhr = new XMLHttpRequest();
features = [];
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
gps = JSON.parse(xhr.responseText);
features.push(
new atlas.data.Feature(
new atlas.data.Point([
parseFloat(gps.gps.lon),
parseFloat(gps.gps.lat),
])
)
);
}
}
};
xhr.open("GET", file, true);
xhr.send();
}
```
이 함수는 패치(fetch) 루틴에 의해 호출되어 JSON 데이터를 구문 분석하고 경도 및 위도 좌표를 geoJSON으로 읽도록 변환됩니다. 파싱되면 데이터는 geoJSON의 `Feature`의 일부로 설정됩니다. 지도가 초기화되고 경로 주위에 작은 점들이 나타납니다.
3. 브라우저에서 HTML 페이지를 로드합니다. 지도를 로드한 다음 저장소에서 모든 GPS 데이터를 로드하고 지도에 표시합니다.
![A map of Saint Edward State Park near Seattle, with circles showing a path around the edge of the park](../../../../images/map-path.png)
> 💁 이 코드는 [코드](.././code) 폴더에서 찾을 수 있습니다.
---
## 🚀 도전
정적 데이터를 지도에 마커로 표시할 수 있다는 점이 좋습니다. 이 웹 앱에 타임스탬프가 있는 json 파일을 사용하여 애니메이션을 추가하고 시간 경과에 따른 마커의 경로를 표시할 수 있습니까? 여기 지동 애니메이션을 사용한 [일부 샘플들](https://azuremapscodesamples.azurewebsites.net/) 이 있습니다.
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/26)
## 복습 및 독학
Azure Maps은 특히 IoT 장치 작업에 유용합니다.
- [Microsoft 문서의 Azure Maps 문서](https://docs.microsoft.com/azure/azure-maps/tutorial-iot-hub-maps?WT.mc_id=academic-17441-jabenn)에서 일부 용도를 조사합니다.
- [Microsoft의 Azure Maps의 자기 학습 모듈을 사용하여 첫번째 경로 찾기 앱 만들기](https://docs.microsoft.com/learn/modules/create-your-first-app-with-azure-maps/?WT.mc_id=academic-17441-jabenn)를 통해 지도 작성 및 웨이포인트에 대한 지식을 심화하세요.
## 과제
[앱 배포](assignment.md)

@ -0,0 +1,223 @@
# IoT 장치로 음성 인식
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-21.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 크게 보려면 클릭하세요.
이 비디오는 이 수업에서 다룰 주제인 Azure 음성 서비스에 대한 개요를 제공합니다.
[![Microsoft Azure YouTube 채널의 Cognitive Services Speech 리소스를 사용하는 방법](https://img.youtube.com/vi/iW0Fw0l3mrA/0.jpg)](https://www.youtube.com/watch?v=iW0Fw0l3mrA)
> 🎥 상단의 이미지를 클릭하여 비디오를 시청합니다.
## 수업 전 퀴즈
[수업 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/41)
## 개요
'Alexa, 타이머 12분으로 맞춰줘'
'Alexa, 타이머 상태'
'Alexa, 스팀 브로콜리 타이머 8분으로 맞춰줘'
스마트 기기는 점점 더 보편화되고 있습니다. HomePods이나 Echos, Google Homes와 같은 스마트 스피커 뿐만 아니라 휴대폰, 워치, 조명 장치 및 온도 조절 장치에도 내장되어 있습니다.
> 💁 저희집에는 최소 19개의 음성 비서가 있는 장치가 있습니다. 제가 아는 것만 해도요!
음성 제어는 움직임이 제한된 사람들이 장치와 상호 작용할 수 있도록 하여 접근성을 높입니다. 팔이 없이 태어난 선천적인 장애든, 팔이 부러지는 것과 같이 일시적인 장애든, 장바구니나 아이의 손을 잡느라 손이 없든간에 우리의 손이 아닌 목소리로 집을 제어할 수 있다는 것은 접근의 세상을 열어줍니다. 아이의 변화와 다루기 힘든 유아를 돌보는 동안 'Hey Siri, 차고 문 좀 닫아줘'를 외치는 것은 작지만 효과적인 삶의 개선이 될 수 있습니다.
음성 비서의 가장 인기있는 용도 중 하나는 특히나 주방 타이머 같은 타이머 설정입니다. 목소리만으로 여러개의 타이머를 설정할 수 있는 것은 주방에서 큰 도움이 됩니다. 물리적 타이머를 설정하기 위해 반죽을 하거나 수프를 저어주거나 만두 속을 채우던 손을 멈출 필요가 없습니다.
이 수업에서는 음성 인식을 IoT에 구축하는 것을 배웁니다. 센서로서의 마이크, IoT 기기에 부착된 마이크로 부터 어떻게 오디오를 캡처하는지, 어떻게 인공지능이 들은 내용을 텍스트로 변환하는 지에 대해 배웁니다. 이 프로젝트의 나머지 부분에서 여러 언어로 음성을 사용하여 타이머를 설정할 수 있는 스마트 주방 타이머를 구축할 예정입니다.
이 강의에서 다룰 내용은 다음과 같습니다:
- [마이크](#마이크)
- [IoT 장치에서 오디오 캡처하기](#iot-장치에서-오디오-캡처하기)
- [음성에서 텍스트로](#음성에서-텍스트로)
- [음성을 텍스트로 변환](#음성을-텍스트로-변환)
## 마이크
마이크는 음파를 전기 신호로 변환하는 아날로그 센서입니다. 공기 중의 진동은 마이크의 구성 요소들을 아주 작은 양으로 움직이게 하고, 이것들은 전기 신호에 작은 변화를 일으킵니다. 이후 이러한 변화는 증폭되어 전기적 출력을 생성합니다.
### 마이크 유형
마이크는 다양한 종류가 있습니다.
* Dynamic - Dynamic 마이크에는 자석이 부착되어있어, 와이어 코일을 통해 움직이며 전류를 생성하는 움직이는 다이어프램이 있습니다. 이것은 대개 전류를 사용하여 와이어 코일에 있는 자석을 움직이는 확성기와는 반대로, 진동판을 움직여 소리를 생성합니다. 즉, 이것은 스피커가 Dynamic 마이크로 사용할 수 있고, dynamic 마이크를 스피커로 사용할 수 있음을 의미합니다. 사용자가 듣거나 말하는 intercom 같은 장치에서 스피커와 마이크의 역할을 동시에 수행할 수 있는 장치는 없습니다.
Dynamic 마이크는 작동하는데 전력이 필요하지 않으며, 전기 신호는 전적으로 마이크에서 생성됩니다.,
![Patti Smith singing into a Shure SM58 (dynamic cardioid type) microphone](../../../../images/dynamic-mic.jpg)
* Ribbon -Ribbon 마이크는 다이어프램 대신 금속 리본이 있다는 점을 제외하면 Dynamic 마이크와 유사합니다. 이 리본은 자기장에서 이동하며 전류를 생성합니다. Dynamic 마이크와 마찬가지로 리본 마이크는 전원이 필요하지 않습니다.
![Edmund Lowe, American actor, standing at radio microphone (labeled for (NBC) Blue Network), holding script, 1942](../../../../images/ribbon-mic.jpg)
* Condenser - Condenser 마이크는 얇은 금속 다이어프램과 고정 금속 백플레이트를 가지고 있습니다. 이 두 가지 모두에 전기가 적용되며 다이어프램이 진동함에 따라 플레이트 사이의 정전기가 변화하여 신호가 생성됩니다. 콘덴서 마이크가 작동하려면 *팬텀 전원*이 필요합니다.
![C451B small-diaphragm condenser microphone by AKG Acoustics](../../../../images/condenser-mic.jpg)
* MEMS - 마이크로 전기 기계 시스템 마이크, 또는 MEMS는 작은 칩에 있는 마이크입니다. 이들은 압력 감지 다이어프램을 실리콘 칩에 새기고, 콘덴서 마이크와 유사하게 작동합니다. 이 마이크들은 아주 작고 회로에 사용 될 수 있습니다.
![A MEMS microphone on a circuit board](../../../../images/mems-microphone.png)
위 이미지에서 **LEFT**라고 표시된 칩은 MEMS 마이크이며 폭이 1mm 미만인 작은 다디어프램이 있습니다.
✅ 생각 해 봅시다 : 컴퓨터, 전화기, 헤드셋 또는 다른 전자기기에서는 어떠한 마이크를 가지고 있는지 조사 해 봅시다.
### Digital audio
오디오는 매우 미세한 정보를 전달하는 아날로그 신호입니다. 이 신호를 디지털로 변환하려면 오디오를 초당 수천번 샘플링 해야합니다.
> 🎓 샘플링이란 오디오 신호를 해당 지점의 신호를 나타내는 디지컬 값으로 변환하는 것 입니다.
![A line chart showing a signal, with discrete points at fixed intervals](../../../../images/sampling.png)
디지털 오디오는 펄스 코드 변조(Pulse Code Modulation, PCM)를 사용하여 샘플링 됩니다. PCM은 신호의 전압을 읽고 정의된 크기를 사용하여 해당 전압에 가장 가까운 이산 값을 선택하는 작업을 포함합니다.
> 💁 PCM은 펄스 폭 변조의 센서 버전 혹은 PWM(PWM은 [lesson 3 of the getting started project](../../../../1-getting-started/lessons/3-sensors-and-actuators/README.md#pulse-width-modulation)에서 다룬 적 있습니다.). PCM은 아날로그 신호를 디지털 신호로 변환하고 PWM은 디지털 신호를 아날로그로 변환합니다.
예를 들어 대부분의 스트리밍 음악 서비스는 16비트 혹은 24비트 오디오를 제공합니다. 즉, 전압을 16비트 정수 또는 24비트 정수로 변환합니다. 16비트 오디오는 -32,768에서 32,767 사이의 숫자로 변환되고, 24비트는 -8,388,608에서 8,388,607 사이의 범위에 있습니다. 비트 수가 많을수록 샘플링 된 결과는 우리가 실제로 귀로 듣는 것과 유사해집니다.
> 💁 종종 LoFi라고 하는 하드 8비트 오디오를 사용할 때가 있습니다. 이것은 8비트만 사용하는 오디오 샘플링으로 범위는 -128에서 127까지입니다. 최초의 컴퓨터 오디오는 하드웨어의 한계로 인해 8비트로 제한되었기 때문에 이것은 레트로 게임에서 자주 볼 수 있습니다.
이러한 샘플은 KHz(초당 수천 개의 판독치) 단위로 잘 정의된 샘플 속도를 사용하여 초당 수천 번 수집됩니다. 스트리밍 음악 서비스는 대부분의 오디오에 48KHz를 사용하지만, 일부 `무손실` 오디오는 최대 96KHz 또는 심지어 192KHz를 사용합니다. 샘플링 속도가 높을수록 오디가 원본에 가깝습니다. 인간이 48KHz 이상의 차이를 구별할 수 있는지에 대한 논란이 있습니다.
✅ 생각 해 봅시다 : 스트리밍 음악 서비스를 사용한다면, 어떤 샘플링 정도와 크기를 사용하나요? CD를 사용할 경우 CD 오디오의 샘플링 비율과 크기는 어떻게 될까요?
오디오 데이터에는 여러가지 다른 형식이 있습니다. 음질을 잃지 않고 작게 만들기 위해서 만들어진 mp3 오디오 데이터에 대하여 들어본 적이 있을 것 입니다. 압축되지 않은 오디오는 종종 WAV 파일로 저장됩니다. 이 파일은 44 바이트릐 헤더 정보와 원시 오디오 데이터를 포합합니다. 헤더에는 샘플링 속도(예: 16KHz의 경우 16000), 샘플링 크기(16비트의 경우 16) 및 채널 수와 같은 정보가 포함됩니다. WAV 파일의 헤더 뒤에 원시 오디오 데이터가 포함됩니다.
> 🎓 채널은 오디오를 구성하는 다양한 오디오 스트림 수를 나타냅니다. 예를 들어, 좌우 구분이 되는 스테레오 오디오의 경우 2개의 채널이 있습니다. 홈 시어터 시스템의 7.1 서라운드 사운드의 경우 8입니다.
### 오디오
오디오 데이터는 상대적으로 큰 값을 가집니다. 압축되지 않은 16비트 오디오를 16KHz(스피치 대 텍스트 모델에서 사용하기에 충분한 속도)로 캡처하려면 오디오의 초당 32KB의 데이터가 필요합니다.
* 16비트는 샘플당 2바이트(1바이트는 8비트)를 의미합니다.
* 16KHz는 초당 16,000개의 샘플입니다.
* 16,000 x 2바이트 = 32,000 bytes/sec.
적은 양의 데이터처럼 느껴질 수 있지만 메모리가 제한된 마이크로 컨트롤러를 사용하는 경우 데이터가 훨씬 더 많게 느껴질 수 있습니다. 예를 들어, Wio Terminal은 192KB의 메모리를 가지고 있으며 프로그램 코드와 변수를 저장해야 합니다. 프로그램 코드의 길이가 짧더라도 5초 이상의 오디오를 캡쳐할 수 없습니다.
마이크로컨트롤러는 SD 카드나 플래시 메모리와 같은 추가 저장소에 액세스할 수 있습니다. 오디오를 캡처하는 IoT 장치를 구축할 때는 추가 저장소가 있어야 할 뿐만 아니라 코드가 마이크에서 캡처한 오디오를 해당 저장소에 직접 기록하고 클라우드로 전송할 때 저장소에서 웹 요청으로 스트리밍해야 합니다. 이렇게 하면 한 번에 전체적인 오디오 데이터 블록을 메모리에 저장하여 메모리 lack을 방지할 수 있습니다.
## IoT 장치에서 오디오 캡처하기
IoT 장치를 마이크와 연결하여 텍스트로 변환할 준비가 된 오디오를 캡처할 수 있습니다. 스피커를 연결하여 오디오 출력을 할 수도 있습니다. 이후 강의에서 이것은 오디오 피드백을 제공하는 데 사용되지만 마이크를 테스트 하기 위해 지금 스피커를 설정하는 것이 유용합니다.
### 작업 - 마이크 및 스피커 구성
관련 가이드를 통해 IoT 장치용 마이크와 스피커를 구성하십시오:
- [Arduino - Wio Terminal](../wio-terminal-microphone.md)
- [단일 보드 컴퓨터 - Raspberry Pi](pi-microphone.ko.md)
- [단일 보드 컴퓨터 - 가상 장치](../virtual-device-microphone.md)
### 작업 - 오디오 캡처
관련 가이드를 통해 IoT 장치에서 오디오를 캡처합니다:
- [Arduino - Wio Terminal](../wio-terminal-audio.md)
- [단일 보드 컴퓨터 - Raspberry Pi](pi-audio.ko.md)
- [단일 보드 컴퓨터 - 가상 장치](../virtual-device-audio.md)
## 음성에서 텍스트로
음성을 텍스트로 변환하거나 음성 인식은 AI를 사용하여 오디오 신호의 단어를 텍스트로 변환하는 것을 포함합니다.
### 음성 인식 모델
음성을 텍스트로 변환하기 위해, 오디오 신호의 샘플은 함께 그룹화되어 Recurrent Neural network (RNN) 기반의 기계 학습 모델에 제공됩니다. 이는 이전 데이터를 사용하여 수신 데이터에 대한 결정을 내릴 수 있는 일종의 기계 학습 모델입니다. 예를 들어, RNN은 오디오 샘플의 한 블록을 'Hel'로 감지할 수 있고, 다른 블록을 수신하면 이를 'lo' 라고 생각할 수 있습니다. 이를 이전 블록과 결합하여 'Hello' 가 올바른 단어임을 찾고, 결과 값으로 선택합니다.
ML 모델은 항상 동일한 크기의 데이터를 받아들입니다. 이전 단원에서 구축한 이미지 분류기는 이미지의 크기를 고정된 크기로 조정하고 처리합니다. 이는 음성 모델도 마찬가지로, 오디오 청크 사이즈를 조정해야 합니다. 음성 모델은 'Hi'과 'Highway', 또는 'flock'과 'floccinaucinihilipilification' 을 구별할 수 있도록, 여러 예측의 출력을 결합하여 답을 얻을 수 있어야 합니다.
또한 음성 모델은 문단을 이해할 수 있을 정도로 발전했으며, 더 많은 소리를 처리해 감지한 언어를 수정할 수 있습니다. 예를 들어, "I went to the shops to get two bananas and an apple too" 라고 말한다면, 동음의이어 to, two, too 를 사용할 수 있습니다. 음성 모델은 문단을 이해하고 절절한 철자를 사용할 수 있습니다.
> 💁 일부 음성 서비스들은 공장과 같은 시끄러운 환경이나, 화학 이름과 같이 특정 산업에서 사용되는 단어들을 맞춤화하여 더 잘 작동하도록 할 수 있습니다. 이러한 맞춤화는 주어진 샘플 오디오와 사본, 전이 학습으로 훈련됩니다. 이전 시간에 몇 개의 이미지를 사용하여 이미지 분류기를 휸련한 것과 동일한 방법입니다.
### 개인 정보
소비자 IoT 장치에서 음성을 텍스트로 변환할때, 개인 정보는 매우 중요합니다. 이러한 장치는 지속적으로 오디오를 듣습니다. 소비자는 말하는 모든 내용이 클라우드로 전송되어 텍스트로 변환되는 것을 원하지 않을 수 있습니다. 이것은 많은 인터넷 대역폭을 사용할 뿐만 아니라, 개인 정보에 중요한 영향을 미칩니다. 특히 일부 스마트 장치 제조업체가 [모델을 개선하는데 도움이 되도록, 생성된 텍스트에 대해 사람이 검증](https://www.theverge.com/2019/4/10/18305378/amazon-alexa-ai-voice-assistant-annotation-listen-private-recordings)할 오디오를 선택할 때 큰 영향을 미칩니다.
당신은 스마트 장치가 집이나, 사적인 회의, 또는 친밀한 사이에서의 대화가 아니라, 장치를 이용할 때만 클라우드에 오디오를 전송해 처리해주기를 원할 것입니다. 이를 위해, 대부분의 스마트 장치가 이용하는 방식은 호출 명령어입니다. "Alexa", "Hey Siri", 또는 "OK Google"과 같은 _호출 명령어_ 를 사용해 장치를 "깨워" 사용자가 말하는 것을 듣게 합니다. 장치는 음성 중단을 감지하여 대화가 마쳤음을 알아챕니다.
> 🎓 호출 명령어는 _키워드 발견_ 또는 _키워드 인식_ 이라고도 합니다.
이러한 호출 명령어는 클라우드가 아닌 장치에서 감지됩니다. 이러한 스마트 장치들은 작은 AI 모델이 있으며, 이는 장치를 깨우는 작업을 수행합니다. 호출 명령어가 감지되면 인식을 위해 오디오를 클라우드로 스트리밍하기 시작합니다. 이 모델은 매우 전문화되어 있으며 호출 명령어를 듣기만 하면 됩니다.
> 💁 일부 기술 회사는 장치에 더 많은 개인 정보를 추가하고 장치에서 음성을 텍스트로 변환하는 작업을 수행하고 있습니다. Apple은 장치에서 음성을 텍스트로 변환하는 기능을 지원하고, 클라우드를 사용하지 않고 많은 요쳥을 처리할 수 있는 기능에 대한 2021년 iOS 와 macOS 업데이트 일부를 발표했습니다. 이는 기기에 ML 모델을 실행할 수 있는 강력한 프로세서가 있기에 가능했습니다.
✅ 클라우드로 오디오를 전송하고 저장하는 것의 개인 정보 보호 및 윤리적 의미는 무엇이라고 생각합니까? 오디오를 저장해야 한다면 어떻게 해야 합니까? 법 집행을 위해 녹음을 사용하는 것이 사생활 침해에 대한 좋은 절충안이라고 생각힙니까?
호출 명령어 감지는 주로 TinyMl이라는 기술을 사용합니다. 이는 ML 모델을 마이크로컨트롤러에서 실행할 수 있도록 변환한 것입니다. 이 모델은 크기가 매우 작고, 실행하는데 매우 적은 전력을 소모합니다.
호출 명령어 모델을 사용과 복잡한 훈련을 피하기 위해, 이 단원에서 구축하는 스마트 타이머는 버튼을 사용하여 음성 인식을 시작합니다.
> 💁 Wio Terminal 또는 Raspberry Pi 에서 wake world 감지 모델을 생성하려는 경우, 다음 문서 [Edge Impulse로 음성에 대한 응답](https://docs.edgeimpulse.com/docs/responding-to-your-voice)를 확인하십시오. 컴퓨터를 사용하여 작업을 수행하려는 경우, [Microsoft의 사용자 지정 키워드와 시작하기 문서](https://docs.microsoft.com/azure/cognitive-services/speech-service/keyword-recognition-overview?WT.mc_id=academic-17441-jabenn)에서 시도할 수 있습니다.
## 음성을 텍스트로 변환
![Speech services logo](../../../../images/azure-speech-logo.png)
이전 프로젝트의 이미지 분류와 마찬가지로, 음성을 오디오 파일로 받아 텍스트로 변환할 수 있는 미리 구축된 AI 서비스가 있습니다. 그러한 서비스가 인지 서비스의 일부인 음성 서비스라면, 당신의 앱에서 사용할 수 있는 미리 만들어진 인공지능 서비스입니다.
### 작업 - 음성 AI 리소스 구성하기
1. `smart-timer`라는 이 프로젝트에 대한 리소스 그룹을 만듭니다.
2. 다음 명령을 사용해 자유 연설 리소스를 만듭니다:
```sh
az cognitiveservices account create --name smart-timer \
--resource-group smart-timer \
--kind SpeechServices \
--sku F0 \
--yes \
--location <location>
```
`<location>` 를 리소스 그룹을 생성할 때 사용한 위치로 바꿉니다.
3. 코드에서 음성 리소스에 접근하려면 API 키가 필요합니다. 다음 명령을 실행하여 key를 가져옵니다:
```sh
az cognitiveservices account keys list --name smart-timer \
--resource-group smart-timer \
--output table
```
키 중 하나를 복사하십시오.
### 작업 - 음성을 텍스트로 변환하기
관련 가이드를 통해 IoT 장치에서 음성을 텍스트로 변환합니다:
- [Arduino - Wio Terminal](wio-terminal-speech-to-text.md)
- [싱글 보드 컴퓨터 - Raspberry Pi](pi-speech-to-text.md)
- [싱글 보드 컴퓨터 - 가상 장치](virtual-device-speech-to-text.md)
---
## 🚀 도전
음성 인식은 오랜 시간동안 사용되어 왔고, 지속적으로 개선되고 있습니다. 현재의 역량을 연구하고 기계가 작성한 필사본이 인간과 비교하여 얼마나 정확한 지를 포함하여 시간이 지남에 따라 어떻게 발전했는지 비교하십시오.
음성 인식의 미래는 어떻게 될 것이라고 생각합니까?
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/42)
## 복습 & 독학
- 다른 마이크 타입과 그들이 어떻게 동작하는 지에 대해 [Musician's HQ의 다이나믹 마이크와 콘덴서 마이크의 차이점](https://musicianshq.com/whats-the-difference-between-dynamic-and-condenser-microphones/)에서 읽어보세요.
- [Microsoft Docs의 음성 서비스 문서](https://docs.microsoft.com/azure/cognitive-services/speech-service/?WT.mc_id=academic-17441-jabenn)에서 Cognitive Sevices의 음성 서비스에 대해 자세히 읽어보세요.
- [Microsoft Docs의 키워드 인식 설명서](https://docs.microsoft.com/azure/cognitive-services/speech-service/keyword-recognition-overview?WT.mc_id=academic-17441-jabenn)에서 키워드 지정에 대해 읽어보세요.
## 과제
[](assignment.md)

@ -0,0 +1,213 @@
# 오디오 캡처 - Raspberry Pi
이 단원에서는 Raspberry Pi로 오디오를 캡처하는 코드를 작성합니다. 오디오 캡처는 버튼으로 제어됩니다.
## 하드웨어
Raspberry Pi는 오디오 캡처를 조절하는 버튼을 필요로 합니다.
사용할 버튼은 Grove 버튼입니다. 이것은 신호를 켜고 끄는 디지털 센서입니다. 버튼이 눌리면 높은 값의 신호를 보내고 누르지 않으면 낮은 값의 신호를 보내도록 구성됩니다. 또는 눌렀을 때 낮은 값, 누르지 않았을 때 높은 값으로도 구성할 수 있습니다.
ReSpeaker 2-Mics Pi HAT을 마이크로 이용한다면 여기에 이미 장착이 되어있기 때문에 버튼을 연결할 필요가 없습니다. 다음 섹션으로 넘어가십시오.
### 버튼 연결
버튼은 Grove base hat에 연결될 수 있습니다.
#### 작업 - 버튼 연결
![A grove button](../../../../images/grove-button.png)
1. Grove 케이블의 한 쪽 끝을 버튼 모듈의 소켓에 삽입합니다. 한 방향으로만 이루어집니다.
1. Raspberry Pi의 전원을 끈 상태에서 Grove 케이블의 다른 쪽 끝을 Pi에 부착된 Grove Base hat의 **D5**가 적힌 디지털 소켓에 연결합니다. 이 소켓은 GPIO 핀 옆의 소켓 행에서 왼쪽 두 번째입니다.
![소켓 D5에 grove 버튼을 연결합니다.](../../../../images/pi-button.png)
## 오디오 캡처
Python 코드를 이용해서 마이크로부터 오디오를 캡처할 수 있습니다.
### 작업 - 오디오 캡처
1. Pi의 전원을 켜고 부팅될 때까지 기다립니다.
1. Pi에서 직접 VS Code를 실행하거나 원격 SSH 확장을 통해 연결합니다.
1. PyAudio Pip 패키지에는 음성을 녹음하고 재생하는 함수들이 있습니다. 이 패키지는 먼저 설치해야 하는 일부 오디오 라이브러리에 따라 다릅니다. 터미널에서 아래의 명령을 실행하여 이를 설치합니다.
```sh
sudo apt update
sudo apt install libportaudio0 libportaudio2 libportaudiocpp0 portaudio19-dev libasound2-plugins --yes
```
1. PyAudio Pip 패키지를 설치합니다.
```sh
pip3 install pyaudio
```
1. `smart-timer` 이름의 새 폴더를 생성하고 새로운 파일 `app.py`를 이 폴더에 추가합니다.
1. 이 파일의 상단에 다음의 import문들을 추가합니다.
```python
import io
import pyaudio
import time
import wave
from grove.factory import Factory
```
이것은 `pyaudio` 모듈, 웨이브 파일을 처리하는 일부 표준 Python 모듈과 버튼 클래스를 생성하기 위해 `Factory`를 import 하는 `grove.factory` 모듈을 가져옵니다.
1. 아래와 같이 코드를 추가하여 Grove 버튼을 추가합니다.
ReSpeaker 2-Mics Pi HAT를 사용한다면, 다음 코드를 사용하십시오:
```python
# The button on the ReSpeaker 2-Mics Pi HAT
button = Factory.getButton("GPIO-LOW", 17)
```
이렇게 하면 ReSpeaker 2-Mics Pi HAT의 버튼이 연결된 포트 **D17**에 버튼이 생성됩니다. 이 버튼은 누르면 낮은 신호를 보내도록 설정되었습니다.
ReSpeaker 2-Mics Pi HAT를 사용하지 않고 Grove 버튼을 base hat에 연결하는 데 사용하는 경우 이 코드를 사용합니다.
```python
button = Factory.getButton("GPIO-HIGH", 5)
```
이는 **D5** 포트에 눌렀을 때 높은 신호를 전송하는 버튼을 생성합니다.
1. 아래와 같이 오디오를 처리할 PyAudio 클래스의 인스턴스를 생성합니다.
```python
audio = pyaudio.PyAudio()
```
1. 마이크와 스피커의 하드웨어 카드 번호를 선언합니다. 이는 이 수업의 앞부분에서 `arecord -l``aplay -l` 를 실행하여 찾은 카드 번호입니다.
```python
microphone_card_number = <microphone card number>
speaker_card_number = <speaker card number>
```
`<microphone card number>` 부분을 마이크 카드 번호로 수정합니다.
`<speaker card number>``alsa.conf` 파일에서 설정한 숫자와 동일한 스피커 카드 번호로 바꿉니다.
1. 아래와 같이 오디오를 캡처하고 다시 재생하는 샘플 속도를 선언합니다. 실제로 사용하는 하드웨어에 따라 변경할 수 있습니다.
```python
rate = 48000 #48KHz
```
이 코드를 실행하면서 샘플 속도 에러가 발생한다면, 이 값을 `44100` 또는 `16000`로 변경하십시오. 값을 높일 수록 더 좋은 음질을 얻을 수 있습니다.
1. 아래와 같이 `capture_audio`의 이름을 가진 새 함수를 생성합니다. 이 함수는 마이크에서 오디오를 캡처할 때 호출됩니다:
```python
def capture_audio():
```
1. 이 함수 안에 다음과 같은 오디오를 캡처하는 부분을 추가합니다:
```python
stream = audio.open(format = pyaudio.paInt16,
rate = rate,
channels = 1,
input_device_index = microphone_card_number,
input = True,
frames_per_buffer = 4096)
frames = []
while button.is_pressed():
frames.append(stream.read(4096))
stream.stop_stream()
stream.close()
```
이 코드는 PyAudio 객체를 사용해 오디오 입력 스트림을 엽니다. 이 스트림은 16KHz에서 마이크의 오디오를 캡처하여 4096바이트 크기의 버퍼에서 캡처합니다.
그 다음 코드는 버튼이 눌린 동안 4096 바이트의 버퍼를 배열로 읽어들입니다.
> 💁 `open` 메서드에 전달된 옵션들에 대해서는 [PyAudio documentation](https://people.csail.mit.edu/hubert/pyaudio/docs/)에서 확인할 수 있습니다.
버튼을 놓으면 스트림은 중지되고 close합니다.
1. 이 함수의 끝에 다음을 추가합니다:
```python
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, 'wb') as wavefile:
wavefile.setnchannels(1)
wavefile.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
wavefile.setframerate(rate)
wavefile.writeframes(b''.join(frames))
wav_buffer.seek(0)
return wav_buffer
```
이 코드는 바이너리 버퍼를 생성하고 캡처한 오디오를 [WAV file](https://wikipedia.org/wiki/WAV)로 작성합니다. 이는 압축되지 않은 오디오를 파일로 저장하는 표준적인 방법입니다. 이 버퍼는 이후 리턴합니다.
1. 오디오 버퍼를 재생하기 위해 다음과 같은 `play_audio` 함수를 추가합니다.
```python
def play_audio(buffer):
stream = audio.open(format = pyaudio.paInt16,
rate = rate,
channels = 1,
output_device_index = speaker_card_number,
output = True)
with wave.open(buffer, 'rb') as wf:
data = wf.readframes(4096)
while len(data) > 0:
stream.write(data)
data = wf.readframes(4096)
stream.close()
```
이 함수는 이번에는 출력을 위해 오디오를 재생하는 다른 오디오 스트림을 엽니다. 입력 스트림과 같은 방식으로 세팅합니다. 그런 다음 버퍼가 웨이브 파일로 열리고 4096바이트 단위로 출력 스트림에 기록되어 오디오를 재생합니다. 이후 스트림을 close합니다.
1. `capture_audio` 함수에 버튼을 누를 때까지 loop를 돌도록 아래의 코드를 추가합니다. 버튼이 한 번 눌리면 오디오가 캡처되고 재생됩니다.
```python
while True:
while not button.is_pressed():
time.sleep(.1)
buffer = capture_audio()
play_audio(buffer)
```
1. 코드를 실행합니다. 버튼을 누르고 마이크에 대고 말합니다. 끝나면 버튼에서 손을 떼고 녹음된 내용을 듣습니다.
PyAudio 인스턴스가 생성될 때 ALSA 오류가 발생할 수 있습니다. 이는 가지고 있지 않은 오디오 장치에 대한 Pi의 구성 때문입니다. 이러한 오류는 무시해도 됩니다.
```output
pi@raspberrypi:~/smart-timer $ python3 app.py
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
```
만약 다음의 오류가 발생하는 경우:
```output
OSError: [Errno -9997] Invalid sample rate
```
44100 또는 16000으로 `rate` 값을 변경합니다.
> 💁 이 코드는 [code-record/pi](../code-record/pi) 폴더에서 찾을 수 있습니다.
😀 오디오 녹음 프로그램이 성공적으로 끝났습니다!

@ -0,0 +1,140 @@
# 마이크 및 스피커 구성 - Raspberry Pi
이 단원에서는 Raspberry Pi에 마이크와 스피커를 추가합니다.
## 하드웨어
Raspberry Pi에 연결할 마이크가 필요합니다.
Pi에는 내장 마이크가 없기 때문에 외부 마이크를 추가해야 합니다. 외부 마이크를 추가하는 방법에는 여러가지가 있습니다.
* USB 마이크
* USB 헤드셋
* USB 연결 스피커폰
* USB 연결 3.5mm 잭이 있는 오디오 어댑터 및 마이크
* [ReSpeaker 2-Mics Pi HAT](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT.html)
> 💁 Raspberry Pi에서는 블루투스 마이크가 일부 지원되지 않으므로 블루투스 마이크 또는 헤드셋이 있는 경우 오디오 페어링 또는 캡처에 문제가 있을 수 있습니다.
Raspberry Pi 장치에는 3.5mm 헤드폰 잭이 있습니다. 헤드셋 또는 스피커를 연결하기 위해 이를 사용할 수 있으며 아래 방법을 통해서도 스피커를 추가할 수 있습니다.
* 모니터 또는 TV를 통한 HDMI 오디오
* USB 스피커
* USB 헤드셋
* USB 연결 가능 스피커폰
* 3.5mm 잭 또는 JST 포트에 스피커가 부착된 [ReSpeaker 2-Mics Pi HAT](https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT.html)
## 마이크와 스피커를 연결하고 구성합니다.
마이크와 스피커를 연결하고 구성해야 합니다.
### 작업 - 마이크를 연결하고 구성합시다.
1. 적절한 방법으로 마이크를 연결합니다. 예를 들어 USB 포트 중 하나를 통해 연결합니다.
1. ReSpeaker 2-Mics Pi HAT를 사용하는 경우 Grove base hat을 제거한 다음 ReSpeaker hat을 그 자리에 장착할 수 있습니다.
![A raspberry pi with a ReSpeaker hat](../../../../images/pi-respeaker-hat.png)
이 과정의 후반부에 Grove 버튼이 필요하지만, 이 모자에는 Grove base hat이 내장되어 있으므로 Grove base hat이 필요하지 않습니다.
hat이 장착되면 드라이버를 설치해야 합니다. 드라이버 설치 지침은 [Seeed getting started instructions](https://wiki.seeedstudio.com/ReSpeaker_2_Mics_Pi_HAT_Raspberry/#getting-started) 을 참고하세요.
> ⚠️ 명령어는 `git`를 사용하여 저장소를 복제합니다. Pi에 `git`이 설치되어 있지 않은 경우 다음 명령을 실행하여 설치할 수 있습니다.
>
> ```sh
> sudo apt install git --yes
> ```
1. 연결된 마이크에 대한 정보를 보려면 Pi에서 또는 VS Code 및 원격 SSH 세션을 사용하여 연결된 터미널에서 다음 명령을 실행합니다.
```sh
arecord -l
```
아래와 같이 연결된 마이크 목록이 표시됩니다:
```output
pi@raspberrypi:~ $ arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: M0 [eMeet M0], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
```
연결된 마이크가 하나일 때 하나의 항목만 표시됩니다. 리눅스에서 마이크 구성이 까다로울 수 있으므로 한 개의 마이크만 사용하고 다른 마이크는 분리하는 것을 추천합니다.
카드 번호는 나중에 필요하므로 적어 두세요. 위의 출력에서 카드 번호는 1입니다.
### 작업 - 스피커를 연결하고 구성합니다.
1. 적절한 방법으로 스피커를 연결합니다.
1. 연결된 스피커에 대한 정보를 보려면 Pi에서 또는 VS Code와 원격 SSH 세션을 사용하여 연결된 터미널에서 다음 명령을 실행합니다.
```sh
aplay -l
```
아래와 같이 연결된 스피커 목록이 표시됩니다:
```output
pi@raspberrypi:~ $ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 1: M0 [eMeet M0], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
```
헤드폰 잭이 내장돼 있어 `card 0: Headphones`이 항상 확인되는 것을 볼 수 있습니다. USB 스피커와 같은 스피커를 추가한 경우에도 이 목록은 표시됩니다.
1. 내장 헤드폰 잭에 연결된 스피커나 헤드폰이 아닌 추가 스피커를 사용하는 경우 다음 명령어를 통해 기본값으로 구성해야 합니다.
```sh
sudo nano /usr/share/alsa/alsa.conf
```
이렇게 하면 단말기 기반 텍스트 편집기인 `nano`에서 구성 파일이 열립니다. 다음 줄을 찾을 때까지 키보드의 화살표 키를 사용하여 아래로 스크롤합니다.
```output
defaults.pcm.card 0
```
호출 후 돌아온 목록에서 사용할 카드의 카드 번호를 `0`에서 `aplay -l`로 변경합니다. 예를 들어, 위의 출력에는 `card 1: M0 [eMeet M0], 장치 0: USB Audio [USB Audio]`라는 두 번째 사운드 카드가 있습니다. 이를 사용하기 위해 다음과 같이 파일을 업데이트합니다.
```output
defaults.pcm.card 1
```
이 값을 적절한 카드 번호로 설정합니다. 키보드의 화살표 키를 사용하여 숫자로 이동한 다음 텍스트 파일을 편집할 때 일반적으로 새 숫자를 삭제하고 입력할 수 있습니다.
1. `Ctrl+x`를 눌러 변경 내용을 저장하고 파일을 닫습니다. `y`를 눌러 파일을 저장한 다음 `return`을 눌러 파일 이름을 선택합니다.
### 작업 - 마이크와 스피커를 테스트합니다
1. 다음 명령을 실행하여 마이크를 통해 5초간의 오디오를 녹음합니다.:
```sh
arecord --format=S16_LE --duration=5 --rate=16000 --file-type=wav out.wav
```
이 명령이 실행되는 동안 말하기, 노래하기, 비트박스, 악기 연주 또는 하고싶은 것을 하며 마이크에 소리를 내십시오.
1. 5초 후에 녹화가 중지됩니다. 다음 명령을 실행하여 오디오를 재생합니다.
```sh
aplay --format=S16_LE --rate=16000 out.wav
```
스피커를 통해 audio bing이 재생되는 소리가 들립니다. 필요에 따라 스피커의 출력 볼륨을 조정합니다.
1. 내장된 마이크 포트의 볼륨을 조절하거나 마이크의 게인을 조절해야 할 경우 `alsamixer` 유틸리티를 사용할 수 있습니다. 이 유틸리티에 대한 자세한 내용은 [Linux alsamixer man page](https://linux.die.net/man/1/alsamixer) 에서 확인할 수 있습니다.
1. 오디오를 재생할 때 오류가 발생하면 `alsa.conf` 파일에서 `defaults.pcm.card`로 설정한 카드를 확인합니다.

@ -0,0 +1,557 @@
# 언어의 이해
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-22.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 클릭하여 더 크게 보세요.
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/43)
## 개요
지난 수업에서 음성을 텍스트로 변환했습니다. 이것을 스마트 타이머를 프로그래밍하는 데 사용하기 위해선 코드가 말하는 내용을 이해해야 합니다. 사용자가 "3분 타이머 설정해줘"과 같은 정해진 문구를 말하고 해당 표현을 분석해서 타이머가 얼마나 설정되어야 할 지 알아낼 수 있을 것이라고 가정할 수 있지만 이는 사용자 친화적이지 않습니다. 사용자가 "타이머 3분으로 설정해줘"라고 말한다면 당신과 저는 그 말이 의미하는 바를 이해할 수 있지만 코드는 그렇지 않습니다. 코드는 정해진 문구를 기대합니다.
인공지능 모델을 사용하여 텍스트를 해석하고 필요한 세부 정보를 반환하는 언어 이해가 여기서 나옵니다. 예를들어 "3분 타이머 설정해줘"와 "타이머 3분으로 설정해줘"를 모두 사용할 수 있으며 3분의 타이머를 요구하는 것이라는 것을 이해할 수 있습니다.
이 수업에서는 언어 이해 모델에 대해 어떻게 그것을 생성하고 학습시키고 사용하는지 배웁니다.
이 수업에서는 다음을 다룹니다:
- [언어의 이해](#언어의-이해)
- [언어 이해 모델의 생성](#언어-이해-모델의-생성)
- [의도와 엔터티](#의도와-엔터티)
- [언어 이해 모델의 사용](#언어-이해-모델의-사용)
## 언어의 이해
인간은 수십만 년 동안 의사 소통을 위해 언어를 사용해 왔습니다. 우리는 단어, 소리 또는 행동으로 의사소통하고 말한 내용, 단어, 소리 또는 행동의 의미뿐만 아니라 그 맥락도 이해합니다. 우리는 목소리 톤에 따라 단어가 다른 의미를 갖는 것을 허용함으로서 진정성과 빈정거림을 구분합니다.
✅ 당신이 최근에 한 대화를 생각해보십시오. 컴퓨터가 맥락을 이해하기에 어려울 대화는 얼마나 되나요?
자연어 이해라고도 불리는 언어의 이해는 단어나 문장의 세부 사항을 이해하려고 노력하는 독해를 다루는 자연어 처리(또는 NLP)라는 인공지능 분야의 일부입니다. 알렉사나 시리와 같은 음성 비서를 사용하는 경우 당신은 언어 이해 서비스를 사용한 적이 있습니다. 이는 'Alexa, 테일러 스위프트 최신 앨범 틀어줘'를 내 딸이 좋아하는 곡에 맞춰 거실에서 춤을 추는 모습으로 바꿔주는 비하인드 AI 서비스들입니다.
> 💁 컴퓨터는 모든 발전에도 불구하고 텍스트를 정말로 이해하려면 아직 갈 길이 멉니다. 컴퓨터의 언어의 이해를 언급할 때 우리는 인간의 의사소통만큼 발전된 것을 의미하는 것이 아니라 단어를 취하고 주요 세부 사항을 추출하는 것을 의미합니다.
인간인 우리들은 언어를 깊이 생가하지 않고 이해합니다. 만약 제가 다른 사람에게 "테일러 스위프트의 최신 앨범 틀어줘"라고 한다면 그들은 본능적으로 제가 무슨 뜻으로 말한 것인지 알 것입니다. 컴퓨터에게 이것은 더욱 어렵습니다. 음성에서 텍스트로 변환된 단어를 가져와 다음 정보를 계산해야 합니다:
- 음악을 재생해야 합니다.
- 아티스트 테일러 스위프트가 음악을 맡았습니다.
- 특정 음악은 순서대로 여러 트랙의 전체 앨범입니다.
- 테일러 스위프트는 많은 앨범을 발매했으므로 시간순으로 정렬하여 가장 최근에 발매된 것이 필요합니다.
✅ 커피를 주문하거나 가족에게 무언가를 전달해 달라고 요청하는 것과 같이 요청을 할 때 사용했던 다른 문장을 생각해 보십시오. 컴퓨터가 문장을 이해하기 위해 추출해야하는 정보 조각들로 문장을 분리해보십시오.
언어 이해 모델은 작은 이미지 세트를 사용하여 Custom Vision 모델을 훈련한 것과 같은 방식으로 언어에서 특정 세부 정보를 추출하도록 훈련된 다음 전이 학습을 사용하여 특정 작업에 대해 훈련된 AI 모델입니다. 모델을 선택한 다음 이해하려는 텍스트를 사용하여 모델을 훈련시킬 수 있습니다.
## 언어 이해 모델의 생성
![The LUIS logo](../../../../images/luis-logo.png)
Cognitive Services의 일부인 Microsoft의 언어 이해 서비스인 LUIS를 사용하여 언어 이해 모델을 만들 수 있습니다.
### 작업 - 제작 리소스 생성
LUIS를 사용하기 위해서는 제작 리소스를 만들어야 합니다.
1. 다음 명령어를 사용하여 `smart-timer` 그룹에 제작 리소스를 생성합니다.
```python
az cognitiveservices account create --name smart-timer-luis-authoring \
--resource-group smart-timer \
--kind LUIS.Authoring \
--sku F0 \
--yes \
--location <location>
```
`<location>`을 리소스 그룹을 만들 때 사용한 위치로 바꿉니다.
> ⚠️ LUIS는 모든 지역에서 사용 가능하지는 않습니다. 따라서 다음과 같은 오류 메세지가 뜬다면:
>
> ```output
> InvalidApiSetId: The account type 'LUIS.Authoring' is either invalid or unavailable in given region.
> ```
>
> 다른 지역을 선택합니다.
이는 무료 등급의 LUIS 제작 리소스를 생성합니다.
### 업무 - 언어 이해 앱 만들기
1. 브라우저에서 [luis.ai](https://luis.ai?WT.mc_id=academic-17441-jabenn)의 LUIS 포털을 열고, Azure에서 사용한 계정과 동일한 계정으로 로그인합니다.
1. 대화 상자의 지침에 따라 Azure 구독을 선택한 다음, 생성한 `smart-timer-luis-authoring` 리소스를 선택합니다.
1. _대화 앱_ 목록에서 **새 앱** 버튼을 선택하여 새로운 어플리케이션을 생성합니다. 새 앱의 이름을 `smart-timer`로 명명하고 _Culture_ 를 해당 언어로 설정합니다.
> 💁 예측 리소스에 대한 필드가 있습니다. 예측을 위한 두 번째 리소스를 만들 수 있지만, 무료 제작 리소스는 한 달에 1,000개의 개발에 충분한 예측을 허용하므로 이 항목을 비워둘 수 있습니다.
1. 언어 이해 모델을 학습시키기 위해 수행해야 하는 단계를 이해하려면 앱을 만든 후 나타나는 가이드를 읽어보세요. 완료되면 해당 가이드를 닫습니다.
## 의도와 엔터티
언어의 이해는 _의도__엔터티_ 를 기반으로 합니다. 의도는 단어가 의도하는 바를 말합니다. 예를 들면 음악 재생, 타이머 세팅, 음식 주문 등이 있습니다. 엔티티는 앨범, 타이머의 길이 또는 음식의 종류와 같이 의도가 참조하는 것을 말합니다. 모델이 이해하는 각각의 문장은 적어도 하나의 의도를 가져야 하고, 선택적으로 하나 또는 다수의 엔터티를 가질 수 있습니다.
예시:
| 문장 | 의도 | 엔터티 |
| ------------------------------------------------- | ------------- | --------------------------------------- |
| "Taylor Swift의 최신 앨범 재생해 줘" | _음악 재생_ | _Taylor Swift의 최신 앨범_ |
| "3분 타이머 맞춰줘" | _타이머 설정_ | _3분_ |
| "타이머 취소해줘" | _타이머 취소_ | 없음 |
| "파인애플 피자 라지 3판이랑 시저 샐러드 주문해줘" | _음식 주문_ | _파인애플 피자 라지 3판_, _시저 샐러드_ |
✅ 이전에 생각해보았던 문장에서 의도와 엔터티는 무엇인가요?
LUIS를 학습시키기 위해서는 먼저 엔터티를 설정합니다. 이는 용어의 고정된 목록이나 텍스트로부터 배운 것일 수 있습니다. 예를 들어 각 단어의 변형(또는 동의어)과 함께 메뉴에서 사용할 수 있는 고정된 음식 목록을 제공할 수 있습니다. LUIS에는 숫자와 위치와 같이 사용할 수 있는 미리 탑재된 엔터티도 있습니다.
타이머를 세팅하기 위해서, 시간에 대해 미리 작성된 숫자 엔터티를 사용하는 엔터티와 분이나 초와 같은 단위에 대해 각각의 다른 엔터티를 사용할 수 있습니다. 각 단위에는 minute과 minutes와 같은 단수형 및 복수형을 포함하는 여러 변형이 있습니다.
엔터티가 정의되면 의도를 생성합니다. 이는 당신이 제공하는 예문(발화)에 기반을 두고 학습합니다. 예를 들어, _타이머 세팅_ 이라는 의도에 대해 다음 문장들을 제공할 수 있습니다.
- `1초 타이머 설정`
- `타이머 1분 12초로 설정해줘`
- `타이머 3분으로 설정해줘`
- `9분 30초 타이머 설정해줘`
LUIS에게 이 문장들의 어떤 부분이 엔터티에 매핑되는지 알려줍니다:
![타이머 1분 12초로 설정해줘를 엔터티로 분리](../../../../images/sentence-as-intent-entities.png)
`타이머 1분 12초로 설정해줘`라는 문장은 `타이머 설정`이라는 의도를 가집니다. 또한 각각 2개의 값을 갖는 2개의 엔터티를 가집니다:
| | 시간 | 단위 |
| ---- | ---: | ---- |
| 1분 | 1 | 분 |
| 12초 | 12 | 초 |
좋은 모델을 훈련시키기 위해선 누군가가 동일한 것을 요구할 수 있는 여러 가지 방법을 다룰 수 있는 다양한 예문이 필요합니다.
> 💁 모든 AI 모델과 마찬가지로 훈련에 사용하는 데이터가 많고 정확할수록 모델이 더 좋아집니다.
✅ 같은 질문을 해서 인간이 이해하기를 기대하는 다른 방법들을 생각해보세요.
### 업무 - 언어 이해 모델에 엔터티 추가하기
타이머를 위해서는 2가지 엔터티가 필요합니다. 하나는 시간의 단위(분 또는 초)이고 다른 하나는 분이나 초의 숫자에 대한 것입니다.
LUIS 포털을 이용해서 [Quickstart: Build your app in LUIS portal documentation on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/luis/luis-get-started-create-app?WT.mc_id=academic-17441-jabenn)에서 지침을 찾을 수 있습니다.
1. LUIS 포털에서 _Entities_ 탭을 선택하고 **미리 빌드된 엔터티 추가**를 선택하여 미리 빌드된 엔터티 _number_ 를 추가합니다. 그런 다음 목록에서 _숫자_ 를 선택합니다.
1. **생성** 버튼을 사용하여 시간 단위를 위한 새 엔터티를 추가합니다. 엔터티의 이름을 `시간 단위`로 짓고 해당 타입을 _목록_ 에 세팅합니다. _정규화된 값_ 목록에 `분``초`의 값을 추가합니다. 단수형과 복수형을 _동의어_ 목록에 추가합니다. 각각의 동의어를 추가한 뒤에 `return`(엔터키)을 누르면 목록에 추가됩니다.
|정규화된 값| 동의어|
|--------|-----|
|분 |minute, minutes|
|초 |second, seconds|
### 업무 - 언어 이해 모델에 의도 추가하기
1. _의도_ 탭에서 **생성** 버튼을 눌러 새로운 의도를 추가합니다. 이 의도를 `타이머 설정`이라고 이름 짓습니다.
1. 예시에서 분, 초 및 분과 초를 결합하여 타이머를 설정하는 다양한 방법을 입력합니다. 예를 들면 다음과 같습니다.
- `1초 타이머 설정`
- `4분 타이머 설정`
- `타이머 사분 육초로 설정해줘`
- `9분 30초 타이머 설정해줘`
- `타이머 1분 12초로 설정해줘`
- `타이머 3분으로 설정해줘`
- `타이머 3분하고 1초로 설정해줘`
- `삼분 일초로 타이머 설정해줘`
- `1분 1초 타이머 설정`
- `타이머 30초로 설정해줘`
- `1초 타이머 설정`
모델이 두 가지 모두를 처리하는 방법을 학습하도록 숫자를 단어와 숫자로 혼합합니다.
1. 각 예시를 입력하면 LUIS는 엔터티를 검색하기 시작하고 찾은 항목에 밑줄을 긋고 레이블을 지정합니다.
![LUIS가 밑줄을 그은 숫자와 시간 단위의 예시](../../../../images/luis-intent-examples.png)
### 업무 - 모델을 학습시키고 테스트하기
1. 엔터티와 의도가 구성이 되고나면 상단 메뉴의 **Train** 버튼을 이용하여 모델을 학습시킬 수 있습니다. 이 버튼을 선택하면 모델은 몇 초간 학습합니다. 학습 중에는 버튼이 회색으로 표시되며, 학습이 완료되면 다시 활성화됩니다.
1. 상단 메뉴의 **Test** 버튼을 선택하여 언어 이해 모델을 테스트합니다. `타이머 5분 4초로 설정해줘` 와 같은 텍스트를 입력한 뒤 엔터키를 누릅니다. 입력한 텍스트 상자 아래의 상자에 해당 문장이 나타나며 _상위 의도_ 또는 가장 높은 가능성으로 판별된 의도가 나타납니다. 이것은 `set timer`이어야 합니다. 인텐트 이름 뒤에는 감지된 인텐트가 맞을 확률이 표시됩니다.
1. 결과의 분석을 보기 위해서는 **점검** 옵션을 선택합니다. 탐지된 엔터티 목록과 함께 백분율 확률과 함께 가장 높은 점수를 얻은 의도가 표시됩니다.
1. 테스트를 마치면 _Test_ 창을 닫습니다.
### 업무 - 모델을 게시하기
코드에서 이 모델을 사용하기 위해서는 이를 게시해야 합니다. LUIS에서 게시할 때 테스트용 스테이징 환경이나 전체 릴리즈용 제품 환경을 게시할 수 있습니다. 이 수업에서는 스테이징 환경이 좋습니다.
1. LUIS 포털에서 상단 메뉴에서 **게시하기** 버튼을 선택합니다.
1. _Staging slot_ 이 선택되었는지 확인하고 **완료**를 선택합니다. 앱이 게시되면 알림이 표시됩니다.
1. curl을 사용해서 이를 테스트할 수 있습니다. curl 명령을 빌드하기위해서는 엔드포인트, 애플리케이션 IP(앱 ID), API 키 3개의 값이 필요합니다. 이는 상단 메뉴에서 선택할 수 있는 **관리** 탭에서 접근할 수 있습니다.
1. **Settings** 섹션에서 App ID를 복사합니다.
1. _Azure Resources_ 섹션에서 _Authoring Resource_ 를 선택하고 _기본 키__엔드포인트 URL_ 을 복사합니다.
1. 다음의 curl 명령어를 명령 프롬프트 또는 터미널에서 실행합니다.
```sh
curl "<endpoint url>/luis/prediction/v3.0/apps/<app id>/slots/staging/predict" \
--request GET \
--get \
--data "subscription-key=<primary key>" \
--data "verbose=false" \
--data "show-all-intents=true" \
--data-urlencode "query=<sentence>"
```
`<endpoint url>`_Azure Resources_ 섹션에서의 엔드포인트 URL로 바꿉니다.
`<app id>`_Settings_ 섹션에서의 App ID로 바꿉니다.
`<primary key>`_Azure Resources_ 섹션의 기본 키로 바꿉니다.
`<sentence>`를 테스트하고자 하는 문장으로 바꿉니다.
1. 이 호출의 출력은 쿼리, 상위에 있는 의도와 유형별로 분류된 엔터티 목록을 자세히 설명하는 JSON 문서입니다.
```JSON
{
"query": "set a timer for 45 minutes and 12 seconds",
"prediction": {
"topIntent": "set timer",
"intents": {
"set timer": {
"score": 0.97031575
},
"None": {
"score": 0.02205793
}
},
"entities": {
"number": [
45,
12
],
"time-unit": [
[
"minute"
],
[
"second"
]
]
}
}
}
```
상단의 JSON은 `set a timer for 45 minutes and 12 seconds`을 사용한 쿼리의 결과입니다.
- `set timer`는 97%의 가능성을 가진 상위 의도입니다.
- `45``12` 두 개의 _숫자_ 엔터티가 감지되었습니다.
- `분``초` 두 개의 _시간-단위_ 엔터티가 감지되었습니다.
## 언어 이해 모델의 사용
게시가 되고 나면 LUIS 모델을 코드에서 호출할 수 있습니다. 이전 수업에서 IoT Hub를 사용하여 클라우드 서비스와의 통신을 처리하고 원격 분석을 보내고 명령을 수신했습니다. 이는 매우 비동기적입니다. 일단 원격 측정이 전송되면 코드가 응답을 기다리지 않으며 만약 클라우드 서비스가 다운되더라도 알 수 없습니다.
스마트 타이머의 경우 사용자에게 타이머가 설정되었음을 알리거나 클라우드 서비스를 사용할 수 없다고 경고할 수 있도록 즉시 응답을 원합니다. 이를 위해 IoT 장치는 IoT Hub에 의존하는 대신 웹의 엔드포인트를 직접 호출합니다.
IoT 장치에서 LUIS를 호출하는 대신에 다른 유형의 트리거(HTTP 트리거)와 함께 서버리스 코드를 사용할 수 있습니다. 이렇게 하면 함수 앱이 REST 요청을 수신 대기하고 이에 응답할 수 있습니다. 이 함수는 장치가 호출할 수 있는 REST 엔드포인트가 됩니다.
> 💁 IoT 디바이스에서 직접 LUIS를 호출할 수 있지만 서버리스 코드를 사용하는 것이 좋습니다. 이렇게 하면 호출하는 LUIS 앱을 변경하려는 경우(예: 더 나은 모델을 학습시키거나 다른 언어로 모델을 학습시키는 경우) 잠재적으로 수천 또는 수백만 개의 IoT 장치에 코드를 재배포하지 않고 클라우드 코드만 업데이트하면 됩니다.
### 업무 - 서버리스 함수 앱 만들기
1. `smart-timer-trigger`의 이름으로 Azure Function 앱을 생성하고 이를 VS Code에서 엽니다.
1. VS Code의 터미널에서 아래의 명령을 사용하여 `speech-trigger` HTTP 트리거를 추가합니다.
```sh
func new --name text-to-timer --template "HTTP trigger"
```
이는 `text-to-timer`라는 HTTP 트리거를 생성합니다.
1. functions 앱을 실행하여 HTTP 트리거를 테스트합니다. 실행하면 출력에 나열된 엔드포인트를 볼 수 있습니다.
```output
Functions:
text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer
```
URL [http://localhost:7071/api/text-to-timer](http://localhost:7071/api/text-to-timer)을 브라우저에서 로드하여 이를 테스트 합니다.
```output
This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.
```
### 업무 - 언어 이해 모델 사용하기
1. LUIS용 SDK는 Pip 패키지를 통해 사용이 가능합니다. `requirements.txt` 파일에 다음 줄을 추가하여 이 패키지에 대한 종속성을 추가합니다.
```sh
azure-cognitiveservices-language-luis
```
1. VS Code 터미널에 가상 환경이 활성화되어 있는지 확인하고 다음 명령을 실행하여 Pip 패키지를 설치합니다:
```sh
pip install -r requirements.txt
```
> 💁 오류가 난다면 다음 명령어를 통해 pip를 업그레이드 해야합니다:
>
> ```sh
> pip install --upgrade pip
> ``
> ```
1. LUIS 포털의 **관리** 탭의 LUIS API Key, Endpoint URL, App ID를 위한 새 엔트리를 `local.settings.json` 파일에 추가합니다:
```JSON
"LUIS_KEY": "<primary key>",
"LUIS_ENDPOINT_URL": "<endpoint url>",
"LUIS_APP_ID": "<app id>"
```
`<endpoint url>`_Azure Resources_ 섹션의 **MANAGE** 탭에서의 앤드포인트 URL로 바꿉니다. 이는 `https://<location>.api.cognitive.microsoft.com/`일 것입니다.
`<app id>`_Settings_ 섹션의 **관리** 탭의 App ID로 변경합니다.
`<primary key>`_Azure Resources_ 섹션의 **MANAGE** 탭의 기본 키로 변경합니다.
1. 다음의 import문들을 `__init__.py` 파일에 추가합니다:
```python
import json
import os
from azure.cognitiveservices.language.luis.runtime import LUISRuntimeClient
from msrest.authentication import CognitiveServicesCredentials
```
이는 몇몇 LUIS와 상호작용하는 시스템 라이브러리를 import합니다.
1. `main` 메서드의 내용을 삭제하고 다음 코드를 추가합니다:
```python
luis_key = os.environ['LUIS_KEY']
endpoint_url = os.environ['LUIS_ENDPOINT_URL']
app_id = os.environ['LUIS_APP_ID']
credentials = CognitiveServicesCredentials(luis_key)
client = LUISRuntimeClient(endpoint=endpoint_url, credentials=credentials)
```
그러면 LUIS 앱의 `local.settings.json` 파일에 추가한 값이 로드되고 API 키로 자격 증명 개체가 생성된 다음 LUIS 앱과 상호 작용할 LUIS 클라이언트 개체가 생성됩니다.
1. 이 HTTP 트리거는 텍스트를 JSON으로 이해하기 위해 `text`라는 속성에 있는 텍스트를 전달하는 것으로 호출됩니다. 다음 코드는 HTTP 요청의 본문에서 값을 추출하여 콘솔에 기록합니다. 다음 코드를 `main` 함수에 추가합니다:
```python
req_body = req.get_json()
text = req_body['text']
logging.info(f'Request - {text}')
```
1. 예측 요청(예측할 텍스트가 포함된 JSON 문서)을 전송하여 LUIS에서 예측을 요청합니다. 다음 코드를 사용하여 생성합니다:
```python
prediction_request = { 'query' : text }
```
1. 앱이 게시된 스테이징 슬롯을 사용하여 이 요청을 LUIS로 보낼 수 있습니다.
```python
prediction_response = client.prediction.get_slot_prediction(app_id, 'Staging', prediction_request)
```
1. 예측 응답에는 엔터티와 함께 ​​최상위 의도(예측 점수가 가장 높은 의도)가 포함됩니다. 최상위 의도가 `set timer`인 경우 엔티티를 읽어 타이머에
필요한 시간을 얻을 수 있습니다:
```python
if prediction_response.prediction.top_intent == 'set timer':
numbers = prediction_response.prediction.entities['number']
time_units = prediction_response.prediction.entities['time unit']
total_seconds = 0
```
`number` 엔터티는 숫자 배열입니다. 예를 들어 _"Set a four minute 17 second timer."_ 라고 말한다면 `number` 배열은 4와 17 2개의 단일 값을 포함할 것입니다.
`time unit` 배열은 문자열 배열이며 각 시간 단위는 배열 내부의 문자열 배열입니다. 예를 들어 _"Set a four minute 17 second timer."_ 라고 말한다면 `time unit` 배열은 각각 `['minute']``['second']` 의 단일 값을 가지는 두 배열을 포함할 것입니다.
_"Set a four minute 17 second timer."_ 에 대한 이러한 엔티티의 JSON 버전은 다음과 같습니다:
```json
{
"number": [4, 17],
"time unit": [["minute"], ["second"]]
}
```
이 코드는 또한 타이머의 총 시간을 초 단위로 정의합니다. 이는 엔터티의 값으로 채워집니다.
1. 엔터티는 연결되어 있지 않지만 우리는 이에 대해 몇 가지 가정을 할 수 있습니다. 말한 순서대로 나열되기 때문에 배열의 위치를 ​​사용하여 어떤 숫자가 어떤 시간 단위와 일치하는지 확인할 수 있습니다. 예를 들어:
- _"Set a 30 second timer"_ - 이는 하나의 숫자 `30`을 가지고 하나의 시간 단위 `second`을 가집니다, 따라서 하나의 숫자가 하나의 시간 단위와 짝을 이룹니다.
- _"Set a 2 minute and 30 second timer"_ - 이는 `2``30` 2개의 숫자를 가집니다. 또한 `minute``second`의 두 개의 시간 단위를 가집니다. 따라서 첫 번째 숫자는 첫 번째 시간 단위와 짝을 이루고(2분), 두 번째 숫자는 두 번째 시간 단위와 짝을 이룰 것입니다(30초).
다음 코드는 숫자 엔터티의 항목 수를 가져오고 이를 사용하여 각 배열에서 첫 번째 항목을 추출한 다음 두 번째 항목을 추출하는 코드입니다. 이를 `if` 블록 안에 추가합니다.
```python
for i in range(0, len(numbers)):
number = numbers[i]
time_unit = time_units[i][0]
```
_"Set a four minute 17 second timer."_ 에 대하여 이 코드는 루프를 두번 돌아 다음과 같은 값을 제공합니다:
| 반복 횟수 | `number` | `time_unit` |
| ---------: | -------: | ----------- |
| 0 | 4 | minute |
| 1 | 17 | second |
1. 이 루프 내에서 숫자와 시간 단위를 사용하여 타이머의 총 시간(분당 60초, 초당 초 수)을 계산합니다.
```python
if time_unit == 'minute':
total_seconds += number * 60
else:
total_seconds += number
```
1. 엔티티를 통과하는 이 루프 외부에서 타이머의 총 시간을 로그로 남깁니다:
```python
logging.info(f'Timer required for {total_seconds} seconds')
```
1. 함수에서 HTTP 응답으로 반환해야 하는 시간(초)입니다. `if` 블럭의 끝 부분에 다음을 추가합니다:
```python
payload = {
'seconds': total_seconds
}
return func.HttpResponse(json.dumps(payload), status_code=200)
```
이 코드는 타이머의 총 시간(초)이 포함된 페이로드를 생성하고, 이를 JSON 문자열로 변환한 다음 호출이 성공했음을 의미하는 상태 코드 200과 함께 HTTP 결과로 반환합니다.
1. 마지막으로 `if` 블럭의 밖에서 오류 코드를 반환하여 의도가 인식되지 않은 경우를 처리합니다:
```python
return func.HttpResponse(status_code=404)
```
404는 _not found_ 에 대한 상태 코드입니다.
1. function app을 실행하고 curl을 사용하여 이를 테스트합니다.
```sh
curl --request POST 'http://localhost:7071/api/text-to-timer' \
--header 'Content-Type: application/json' \
--include \
--data '{"text":"<text>"}'
```
`<text>``set a 2 minutes 27 second timer`와 같은 요청한 텍스트로 바꿉니다.
function app에서 다음과 같은 결과를 볼 수 있습니다:
```output
Functions:
text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer
For detailed output, run func with --verbose flag.
[2021-06-26T19:45:14.502Z] Worker process started and initialized.
[2021-06-26T19:45:19.338Z] Host lock lease acquired by instance ID '000000000000000000000000951CAE4E'.
[2021-06-26T19:45:52.059Z] Executing 'Functions.text-to-timer' (Reason='This function was programmatically called via the host APIs.', Id=f68bfb90-30e4-47a5-99da-126b66218e81)
[2021-06-26T19:45:53.577Z] Timer required for 147 seconds
[2021-06-26T19:45:53.746Z] Executed 'Functions.text-to-timer' (Succeeded, Id=f68bfb90-30e4-47a5-99da-126b66218e81, Duration=1750ms)
```
curl 호출은 다음을 반환합니다:
```output
HTTP/1.1 200 OK
Date: Tue, 29 Jun 2021 01:14:11 GMT
Content-Type: text/plain; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
{"seconds": 147}
```
타이머가 설정되어야 하는 시간은 `"seconds"` 단위입니다.
> 💁 이 코드는 [code/functions](../code/functions) 폴더에서 찾을 수 있습니다.
### 업무 - function을 IoT 장치에서 사용할 수 있도록 하기
1. IoT 장치가 REST 엔드포인트를 호출하려면 URL을 알아야 합니다. 이전에 이에 액세스 했을 때에는 로컬 시스템에서 REST 끝점에 액세스하기 위한 바로 가기인 `localhost`을 사용했습니다. IoT 장치가 액세스할 수 있도록 하려면 클라우드에 게시하거나 IP 주소를 가져와 로컬에서 액세스해야 합니다.
> ⚠️ Wio Terminal을 사용하는 경우 이전과 동일한 방식으로 함수 앱을 배포할 수 없음을 의미하는 라이브러리에 대한 종속성이 있습니다. 따라서 로컬에서 함수 앱을 실행하는 것이 더 쉽습니다. function 앱을 로컬에서 실행하고 컴퓨터의 IP 주소를 통해 액세스합니다. 클라우드에 배포하려는 경우 이를 수행하는 방법에 대한 이후 학습에서 정보가 제공됩니다.
- 기능 앱을 게시하기 - 이전 레슨의 지침에 따라 기능 앱을 클라우드에 게시합니다. 게시되고 나면 URL은 `https://<APP_NAME>.azurewebsites.net/api/text-to-timer`이 되고 `<APP_NAME>`은 당신의 기능 앱 이름을 의미합니다. 로컬 설정에서도 게시되었는지 확인하십시오.
HTTP 트리거를 사용하여 작업할 때 기본적으로 기능 앱 키로 보호됩니다. 이 키를 가져오기 위해선 다음 명령을 실행합니다:
```sh
az functionapp keys list --resource-group smart-timer \
--name <APP_NAME>
```
`functionKeys`에서 `default` 엔트리의 값을 복사합니다.
```output
{
"functionKeys": {
"default": "sQO1LQaeK9N1qYD6SXeb/TctCmwQEkToLJU6Dw8TthNeUH8VA45hlA=="
},
"masterKey": "RSKOAIlyvvQEQt9dfpabJT018scaLpQu9p1poHIMCxx5LYrIQZyQ/g==",
"systemKeys": {}
}
```
이 키가 쿼리 매개변수에 추가되어야 하므로 최종 URL은 `https://<APP_NAME>.azurewebsites.net/api/text-to-timer?code=<FUNCTION_KEY>`입니다. 여기서 `<APP_NAME>`이 functions 앱의 이름이 되고 `<FUNCTION_KEY>`가 기본 기능 키가 됩니다.
> 💁 `function.json` 파일의 `authlevel` 설정을 사용하여 HTTP 트리거의 인증 유형을 변경할 수 있습니다. 이에 대해 더 자세히 알기 위해선 [configuration section of the Azure Functions HTTP trigger documentation on Microsoft docs](https://docs.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=academic-17441-jabenn&tabs=python#configuration)를 참고하십시오.
- functions 앱을 로컬에서 실행하고 IP 주소를 사용하여 액세스합니다. 로컬 네트워크에서 IP 주소를 가져와 URL을 만드는 데 사용할 수 있습니다.
IP 주소 찾기:
- Windows 10에서는 [find your IP address guide](https://support.microsoft.com/windows/find-your-ip-address-f21a9bbc-c582-55cd-35e0-73431160a1b9?WT.mc_id=academic-17441-jabenn)를 따릅니다.
- macOS에서는 [how to find you IP address on a Mac guide](https://www.hellotech.com/guide/for/how-to-find-ip-address-on-mac)를 따릅니다.
- Linux에서는 [how to find your IP address in Linux guide](https://opensource.com/article/18/5/how-find-ip-address-linux)에서 개인 IP 주소 찾기 섹션을 따릅니다.
IP 주소가 있으면, `http://<IP_ADDRESS>:7071/api/text-to-timer`에 있는 함수에 접근할 수 있습니다. `<IP_ADDRESS>` 는 당신의 IP 주소입니다. 예를 들면 `http://192.168.1.10:7071/api/text-to-timer`같이 사용할 수 있습니다.
> 💁 7071 포트를 사용하는 것이 아니라, IP 주소 뒤에`:7071`이 필요합니다.
> 💁 이것은 IoT 장치가 컴퓨터와 동일한 네트워크에 있는 경우에만 작동합니다.
1. curl을 사용하여 엔드포인트에 액세스하여 엔드포인트를 테스트합니다.
---
## 🚀 도전 과제
타이머를 세팅하는 것과 같이 동일한 것을 요청하는 데는 많은 방법이 있습니다. 이를 수행하는 다양한 방법을 생각하고 LUIS 앱에서 예로 사용해봅시다. 이를 테스트하여 모델이 타이머를 요청하는 여러 방법에 얼마나 잘 대처할 수 있는지 확인합니다.
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/44)
## 복습 & 자습
- LUIS 및 해당 기능에 대해 [Language Understanding (LUIS) documentation page on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/luis/?WT.mc_id=academic-17441-jabenn)에서 더 자세히 읽어보세요.
- [natural-language understanding page on Wikipedia](https://wikipedia.org/wiki/Natural-language_understanding)에서 언어 이해에 대해 더 알아보세요.
- HTTP 트리거에 대해 [Azure Functions HTTP trigger documentation on Microsoft docs](https://docs.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=academic-17441-jabenn&tabs=python)에서 더 알아보세요.
## 과제
[타이머 취소](assignment.ko.md)

@ -0,0 +1,14 @@
# 타이머 취소
## 개요
지금까지 이 단원에서는 타이머 설정을 이해하도록 모델을 훈련했습니다. 또 다른 유용한 기능은 타이머를 취소하는 것입니다. 빵이 준비가 되었다면 타이머가 경과하기 전에 오븐에서 꺼낼 수 있습니다.
LUIS 앱에 새 의도를 추가하여 타이머를 취소합니다. 엔터티가 필요하지 않지만 몇 가지 예문이 필요합니다. 최상위 의도인 경우 서버리스 코드에서 이를 처리하고 의도가 인식되었음을 기록하고 적절한 응답을 반환합니다.
## 평가 기준
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
| ------------------------------- | --------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------- |
| LUIS 앱에 취소 타이머 의도 추가 | 의도를 추가하고 모델을 교육할 수 있었습니다. | 인텐트를 추가할 수 있었지만 모델을 교육할 수 없었습니다. | 인텐트를 추가하고 모델을 교육할 수 없습니다. |
| 서버리스 앱에서 인텐트 처리 | 의도를 최상위 의도로 감지하고 기록할 수 있었습니다. | 의도를 최상위 인텐트로 감지할 수 있었습니다. | 의도를 최상위 의도로 감지할 수 없습니다. |

@ -0,0 +1,130 @@
# 타이머 설정 및 음성 피드백 제공
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-23.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치노트. 클릭하여 크게 보세요.
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/45)
## 도입
스마트 어시스턴트는 단방향 통신 장치가 아닙니다. 그들에게 말을 걸면 그들은 대답합니다.
"Alexa, 3분짜리 타이머 설정해줘"
"네. 3분 타이머 설정했습니다."
지난 두 번의 수업에서 여러분은 강의를 듣고 음성을 통해 텍스트를 생성하는 방법을 배웠고, 생성한 텍스트에서 타이머 설정 요청에 관한 내용을 추출했습니다. 이 수업에서는 IoT 장치에서 타이머를 설정하고, 사용자에게 타이머 설정이 완료됨을 확인하는 음성안내를 송출한 후, 타이머가 종료되면 알람을 울리는 방법을 배울 것 입니다.
이 강의에서는 다음을 다룹니다.:
* [텍스트에서 음성으로](#텍스트에서-음성으로)
* [타이머 설정하기](#타이머-설정하기)
* [텍스트를 음성으로 변환하기](#텍스트를-음성으로-변환하기)
## 텍스트에서 음성으로
제목에서부터 알다시피 텍스트를 음성으로 변환하는 과정입니다. 기본 원칙은 텍스트의 단어를 구성하는 소리(음소)로 나누고, 사전 녹음된 오디오나 AI모델에서 생성된 오디오를 사용하여 하나의 오디오로 연결합니다.
![일반적인 텍스트 음성 시스템의 3단계](../../../../images/tts-overview.png)
텍스트 음성 변환 시스템은 일반적으로 3단계로 구성됩니다.
* 텍스트 분석
* 언어 분석
* 파형 발생
### 텍스트 분석
텍스트 분석에는 제공된 문장을 가져와서 음성을 생성하는 데 필요한 단어로 변환하는 작업이 포함됩니다. 예를 들어 "Hello wolrd" 를 변환하면 텍스트 분석 없이 두 단어를 음성으로 변환할 수 있습니다. 그러나 "1234"는 문맥에 따라 "천 이백 삼시 사" 혹은 "일 이 삼 사" 로 변환해야 할 수 있습니다. "나는 1234개의 사과를 가지고 있다"의 경우 문맥상 "천 이백 삼십 사"로 감지되지만, "아이가 1234ㄹ를 세었다"의 경우 "일 이 삼 사"가 됩니다.
생성된 단어는 언어 뿐만 아니라 해당 언어의 사용 환경에 따라 다릅니다. 예를 들면, 미국영어에서 "120"은 "One hundred twenty" 이지만, 영국에서는 "One hundred and twenty"으로 hundreds 뒤에 "and"를 사용합니다.
✅ 텍스트 분석이 필요한 다른 예로는 inch의 in을 발음하는 짧은 음, saint와 street의 짧은 형태의 "st"등이 있습니다. 여러분은 문맥 상 모호한 언어로 구성된 다른 예시들이 생각 나시나요?
+) 한국어 문맥 상 "난 네 말이 궁금해" 에서 동물을 의미하는 지 대화를 의미하는 지 등의 동음이의어이지 않을까 생각합니다.
문맥상 단어가 명확해지면 언어 분석을 위해 전송됩니다.
### 언어 분석
언어 분석은 단어들을 음소로 나눕니다. 음소는 사용된 문자 뿐만 아니라 단어의 다른 문자에도 기반을 두고 있습니다. 예를들어 영어에서 'car'와 'care'에서의 'a'소리가 다릅니다. 영어는 알파벳의 26개 문자에 대하여 44개의 서로 다른 음소를 가지고 있으며, 일부는 'circle'와 'serpent'처럼 다른 문자이지만 동일한 음소로 사용되는 경우도 존재합니다.
✅ 생각 해 봅시다 : 한국어 음소는 무엇이 있나요?
일단 단어들이 음소로 변환되면, 이 음소들은 문맥에 따라서 음조나 발음 시간을 조절하면서 억양을 지원하기 위한 추가적인 데이터가 필요합니다. 한 예로 영어에서 마지막 단어에 대한 음높이 증가는 질문을 의미합니다. 이를 바탕으로 문장을 질문으로 변환하는 데 사용할 수 있습니다.
예를 들어, "You have an apple"이라는 문장은 상대방이 사과를 가지고 있다는 것을 나타내는 문장입니다. 만약 마지막 'apple' 단어의 끝음이 올라간다면 "You have an apple?"의 질문 형태가 됩니다. 언어 분석 시 마지막 단어의 음이 올라가는 것을 파악하면 물음표를 사용하여 질문임을 표시해야합니다.
일단 음소가 생성되면, 오디오 출력을 생성하기 위한 파형 생성을 위해 전송될 수 있습니다.
### 파형 발생
최초의 전자 텍스트 음성 변환 시스템은 각 음소에 단일 오디오 녹음을 사용하여 매우 단조로운 로봇 음성으로 구현되었습니다. 이들은 소리 데이터베이스에서 로드되고 오디오를 만들기 위해 연결됩니다.
✅ 생각 해 봅시다 : 초기 음성 합성 시스템의 오디오 녹음을 찾아봅시다. 스마트 어시스턴트에 사용되는 것과 같은 최신 음성 합성과 비교 해 봅시다.
보다 최신의 파형 생성은 딥러닝을 사용하여 구축된 머신 러닝 모델을 사용하여 인간과 구별 할 수 없는 더 자연스러운 소리를 생성합니다.
> 💁 이러한 머신 러닝 모델 중 일부는 실제 사람이 말하는 것 처럼 들리기 위해 전이 학습을 사용하여 재학습 될 수 있습니다. 즉, 은행에서 점점 더 시도하고 있는 음성 보안 시스템은 여러분의 목소리를 몇 분 녹음한 사람은 누구나 여러분을 사칭할 수 있기 때문에 좋은 방법이 아니라고 생각합니다.
이러한 대형 머신 러닝 모델은 세 단계를 모두 end-to-end 음성 합성기로 결합하도록 학습되고 있습니다.
## 타이머 설정하기
타이머를 설정하려면 IoT 장치가 서버리스 코드를 사용하여 만든 REST 엔드포인트를 호출한 후, 결과 시간을 사용하여 타이머를 설정합니다.
### 작업 - 서버리스 함수를 호출하여 타이머 시간을 가져옵시다.
아래 안내에 따라 IoT 장치에서 REST 끝 점을 호출하고 요청한 시간 동안의 타이머를 설정 해 줍니다.:
* [Arduino - Wio Terminal](../wio-terminal-set-timer.md)
* [Single-board computer - Raspberry Pi/Virtual IoT device](single-board-computer-set-timer.ko.md)
## 텍스트를 음성으로 변환하기
음성을 텍스트로 변환하는 데 사용한 것과 동일한 음성 서비스를 사용하여 텍스트를 다시 음성으로 변환할 수 있습니다. 이는 IoT 장치의 스피커를 통해 재생할 수 있습니다. 변환하고자 하는 텍스트는 필요한 오디오 유형(ex: 샘플링 정도)정보와 함께 음성 서비스로 전송되고 오디오가 포함된 이진 데이터가 반환됩니다.
해당 요청을 보낼 때, 음성 합성 응용 프로그램을 위한 XML 기반 마크업 언어인 *Speech Synthesis Markup Language*(SSML)를 사용하여 전송합니다. 이것은 변환될 텍스트 뿐만 아니라 텍스트의 언어, 사용할 음성을 정의하며 텍스트의 일부 또는 전부에 대한 말한느 속도, 볼륨, 피치 등을 정의하는 데 사용될 수 있습니다.
예를 들어, 이 SSML은 "Your 3 minute 5 second time has been set" 라는 텍스트를 `en-GB-MiaNeural`이라는 영국 언어 음성을 사용하여 음성으로 변환하는 요청을 정의합니다.
```xml
<speak version='1.0' xml:lang='en-GB'>
<voice xml:lang='en-GB' name='en-GB-MiaNeural'>
Your 3 minute 5 second time has been set
</voice>
</speak>
```
> 💁 대부분의 텍스트 음성 시스템은 영국 억양 영어 음성, 뉴질랜드 억양 영어 음성 등 다양한 언어에 대한 다양한 음성을 가지고 있습니다.
### 작업 - 텍스트를 음성으로 변환 해 봅시다.
IoT 장치를 사용하여 텍스트를 음성으로 변환하려면 아래 가이드를 사용하십시오.
* [Arduino - Wio Terminal](../wio-terminal-text-to-speech.md)
* [Single-board computer - Raspberry Pi](pi-text-to-speech.ko.md)
* [Single-board computer - Virtual device](../virtual-device-text-to-speech.md)
---
## 🚀 도전
SSML은 특정 단어를 강조하거나, 잠깐의 텀을 두거나, 음높이를 변경하는 등 단어를 말하는 방식을 변경하는 방법을 가지고 있습니다. IoT 장치에서 다른 SSML을 전송하고 출력을 비교하면서 이 중 몇가지를 시도해봅시다. SSML에 대한 자세한 내용은 [World Wide Web Consortium의 음성 합성 마크업 언어(SSML) Version 1.1](https://www.w3.org/TR/speech-synthesis11/))에서 확인할 수 있습니다.
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/46)
## 복습 및 독학
* 음성 합성에 대한 자세한 내용은 [speech synthesis page on Wikipedia](https://wikipedia.org/wiki/Speech_synthesis) 에서 확인 하세요.
* 범죄자들이 음성 합성을 사용하여 [가짜 목소리로 돈을 훔치는 것에 대한 BBC뉴스](https://www.bbc.com/news/technology-48908736) 기사에서 확인 해 보세요.
* [TikTok 플랫폼이 성우들의 동의 없이 AI를 사용하여 그들의 목소리를 악의적으로 사용하였다.](https://www.vice.com/en/article/z3xqwj/this-tiktok-lawsuit-is-highlighting-how-ai-is-screwing-over-voice-actors) 와 관련된 기사를 확인 해 보세요.
## 과제
[타이머 취소하기](assignment.ko.md)

@ -0,0 +1,140 @@
# 텍스트 음성 변환 - Raspberry Pi
이 단원에서는 음성 서비스를 사용하여 텍스트를 음성으로 변환하는 코드를 작성합니다.
## 음성 서비스를 사용하여 텍스트를 음성으로 변환 해봅시다.
텍스트는 REST API를 사용하여 음성 서비스로 전송되어 IoT 장치에서 재생할 수 있는 오디오 파일로 음성을 얻을 수 있습니다. 음성을 요청할 때 다양한 음성을 사용하여 변환된 음성을 생성할 수 있으므로 사용할 voice를 제공해야 합니다.
각 언어는 다양한 voice를 지원하며, 음성 서비스에 대해 REST를 요청하여 각 언어에 대해 지원되는 voice 목록을 얻을 수 있습니다.
### 작업 - voice를 얻어 봅시다.
1. VS Code에서 `smart-timer` 프로젝트를 열어주세요.
1. 언어에 대한 voice 목록을 요청하려면 `say`함수 뒤에 아래 코드를 추가하십시오.
```python
def get_voice():
url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/voices/list'
headers = {
'Authorization': 'Bearer ' + get_access_token()
}
response = requests.get(url, headers=headers)
voices_json = json.loads(response.text)
first_voice = next(x for x in voices_json if x['Locale'].lower() == language.lower() and x['VoiceType'] == 'Neural')
return first_voice['ShortName']
voice = get_voice()
print(f'Using voice {voice}')
```
이 코드는 음성 서비스를 사용하여 voice 리스트를 가져오는 `get_voice`라는 함수를 정의합니다. 이후 사용중인 언어와 일치하는 첫 번째 voice를 찾습니다.
이후 이 기능을 호출하여 첫 번째 voice를 저장하면 음성 이름이 console에 출력됩니다. 이 voice는 한 번 요청할 수 있으며 텍스트를 음성으로 변환하기 위해 모든 호출에 사용되는 값입니다.
> 💁 지원되는 voice의 전체 목록은 [Microsoft Docs 언어 및 음성 지원 설명서](https://docs.microsoft.com/azure/cognitive-services/speech-service/language-support?WT.mc_id=academic-17441-jabenn#text-to-speech)에서 확인할 수 있습니다. 특정 voice를 사용하려면 이 기능을 제거하고 설명서에서 voice를 해당 voice의 명칭으로 하드코딩 할 수 있습니다.
> 예시 :
> ```python
> voice = 'hi-IN-SwaraNeural'
> ```
### 작업 - 텍스트를 음성으로 변환
1. 아래에서 음성 서비스에서 검색할 오디오 형식에 대한 상수를 정의합니다. 오디오를 요청할 때 다양한 형식으로 요청할 수 있습니다.
```python
playback_format = 'riff-48khz-16bit-mono-pcm'
```
하드웨어에 따라 사용할 수 있는 형식이 다릅니다. 오디오를 재생할 때 `Invalid sample rate`오류가 발생하면 이 값을 다른 값으로 변경하면 됩니다. 지원되는 목록은 [Microsoft Docs의 Text to speech REST API 설명서](https://docs.microsoft.com/azure/cognitive-services/speech-service/rest-text-to-speech?WT.mc_id=academic-17441-jabenn#audio-outputs) 에서 확인할 수 있습니다. `riff` 형식의 오디오를 사용해야 하며, 시도할 값은 `riff-16khz-16bit-mono-pcm`, `riff-24khz-16bit-mono-pcm` 그리고 `riff-48khz-16bit-mono-pcm`입니다.
1. 아래 코드를 통해 음성 서비스 REST API를 사용하여 텍스트를 음성으로 변환하는 `get_speech`함수를 선언합니다.
```python
def get_speech(text):
```
1. `get_speech` 함수에서 호출할 URL과 전달할 헤더를 정의합니다.
```python
url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/v1'
headers = {
'Authorization': 'Bearer ' + get_access_token(),
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': playback_format
}
```
생성된 액세스 토큰을 사용하도록 헤더를 설정하고 콘텐츠를 SSML로 설정하며 필요한 오디오 형식을 정의합니다.
1. 아래 코드를 통해 REST API로 보낼 SSML을 정의합니다.
```python
ssml = f'<speak version=\'1.0\' xml:lang=\'{language}\'>'
ssml += f'<voice xml:lang=\'{language}\' name=\'{voice}\'>'
ssml += text
ssml += '</voice>'
ssml += '</speak>'
```
이 SSML은 변환할 텍스트와 함께 사용할 언어와 voice를 설정합니다.
1. 마지막으로 REST를 요청하고 이진 오디오 데이터를 반환하는 코드를 이 함수에 추가합니다.
```python
response = requests.post(url, headers=headers, data=ssml.encode('utf-8'))
return io.BytesIO(response.content)
```
### 작업 - 오디오를 재생해봅시다
1. `get_speech` 함수 아래에 REST API 호출에 의해 반환된 오디오를 재생하는 새 함수를 정의합니다.
```python
def play_speech(speech):
```
1. 이 함수에 전달되는 `speech`는 REST API에서 반환되는 이진 오디오 데이터입니다. 다음 코드를 사용하여 이를 파형 파일로 열고 PyAudio로 전달하여 오디오를 재생합니다
```python
def play_speech(speech):
with wave.open(speech, 'rb') as wave_file:
stream = audio.open(format=audio.get_format_from_width(wave_file.getsampwidth()),
channels=wave_file.getnchannels(),
rate=wave_file.getframerate(),
output_device_index=speaker_card_number,
output=True)
data = wave_file.readframes(4096)
while len(data) > 0:
stream.write(data)
data = wave_file.readframes(4096)
stream.stop_stream()
stream.close()
```
이 코드는 오디오를 캡처하는 것과 동일한 PyAudio 스트림을 사용합니다. 여기서 차이점은 오디오 데이터에서 데이터가 읽혀지고 출력 스트림으로 설정 된 스트림으로 푸시된다는 것입니다.
샘플링 정도와 같은 스트림 세부 사항을 하드 코딩하는 대신 오디오 데이터로부터 읽다.
1. `say` 함수의 내용을 다음과 같이 바꿉니다.
```python
speech = get_speech(text)
play_speech(speech)
```
이 코드는 텍스트를 이진 오디오 데이터로 음성으로 변환하고 오디오를 재생합니다.
1. 앱을 실행하고 function 앱도 실행 중인지 확인합니다. 타이머를 설정하면 타이머가 설정되었다는 음성 응답이 들리고, 타이머의 시간이 완료되면 다른 음성 응답이 들립니다.
`Invalid sample rate` 오류가 발생하면 위에서 설명한 대로 `playback_format`을 변경하십시오.
> 💁 [code-spoken-response/pi](code-spoken-response/pi) 폴더에서 코드를 확인할 수 있습니다.
😀 타이머를 만들었어요! 야호!

@ -0,0 +1,174 @@
# 다양한 언어 지원
![A sketchnote overview of this lesson](../../../../sketchnotes/lesson-24.jpg)
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 더 큰 이미지를 보려면 클릭하세요.
이 비디오는 Azure 음성 서비스에 대한 개요를 제공합니다. 이전 단원의 음성 번역 뿐만 아니라, 음성에서 텍스트, 텍스트에서 음성을 변환하는 내용과 이번 수업에서 다를 주제를 포함하고 있습니다.
[![Recognizing speech with a few lines of Python from Microsoft Build 2020](https://img.youtube.com/vi/h6xbpMPSGEA/0.jpg)](https://www.youtube.com/watch?v=h6xbpMPSGEA)
> 🎥 위 이미지를 클릭하면 영상을 볼 수 있습니다.
## 강의 전 퀴즈
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/47)
## 개요
지난 3개의 강의에서 음성을 텍스트로 변환하고, 언어를 이해하고, 텍스트를 음성으로 변환하는 방법에 대해 배웠습니다. 이 모든 것이 AI 기반입니다. AI 가 도울 수 있는 인간 의사소통의 또 다른 영역은 언어 번역입니다. - 영어에서 프랑스어처럼, 한 언어에서 다른 언어로 변환하는 것입니다.
이번 수업에서는 AI를 사용하여 텍스트를 번역하여, 스마트 타이머가 여러 언어로 사용자와 상호 작용하는 방법을 배웁니다.
이 강의에서 다룰 내용용은 다음과 같습니다:
- [텍스트 번역](#translate-text)
- [번역 서비스](#translation-services)
- [번역가 리소스 만들기](#create-a-translator-resource)
- [번역을 통해 어플리케이션에서 여러 언어 지원하기](#support-multiple-languages-in-applications-with-translations)
- [AI 서비스를 사용하여 텍스트 번역하기](#translate-text-using-an-ai-service)
> 🗑 이것은 이 프로젝트의 마지막 강의이므로, 강의와 과제를 완료한 후, 클라우드 서비스를 종료하는 것을 잊지 마십시오. 과제를 완료하려면 서비스가 필요하므로, 과제를 먼저 완료해야 합니다.
>
> 이 작업을 수행하는 방법에 대한 지침이 필요한 경우 [프로젝트 종료 가이드](../../../../clean-up.md) 를 참조 하세요.
## 텍스트 번역
텍스트 번역은 70년 넘게 연구되어 온 컴퓨터 과학의 문제였는데, 이제서야 인공지능과 컴퓨터의 발전 덕분에 인간 번역가와 맞먹는 수준에 이르렀습니다.
> 💁 그 기원을 따라가보면, 언어 번역 기술을 개발한 9세기 아랍 암호학자인 [Al-Kindi](https://wikipedia.org/wiki/Al-Kindi)까지 거슬러 올라갈 수 있습니다.
### 기계 번역
텍스트 번역은 서로 다른 언어 쌍 간에 번역할 수 있는 기계번역(MT)로 알려진 기술로 시작되었습니다. MT는 한 언어의 단어를 다른 언어로 대체하고, 단어 대 단어 번역이 이해되지 않을 때, 구 또는 문장의 일부를 번역해 올바른 방법을 선택하는 기술이 추가되어 작동합니다.
> 🎓 번역가가 한 언어와 다른 언어 간의 번역을 지원하는 경우, 이를 *언어 쌍*이라고 합니다. 다른 도구는 다른 언어 쌍을 지원하며, 완전하지 않을 수 있습니다. 예를 들어, 번역가가 언어 쌍으로 영어에서 스페인어를 지원하고, 언어 쌍으로 스페인어에서 이탈리아어를 지원할 수 있지만, 영어에서 이탈리아어는 지원하지 않을 수 있습니다.
예를 들어, "Hello world"를 영어에서 프랑스어로 번역하는 것은 "Hello"를 "Bonjour"로, "world"를 "le monde"로 대체하는 것으로 수행되며, 이는 "Bonjour le monde"의 올바른 번역으로 이어집니다.
다른 언어가 동음이의어를 사용하는 경우 대체가 작동하지 않습니다. 예를 들어, 영어 문장인 "My name is Jim"은, 프랑스어로 "Je m'appelle Jim" 으로 번역됩니다. 문자 그대로 "I call myself Jim"이라는 의미입니다. 프랑스어로 "Je" 는 "I", "moi" 는 "me"를 의미하지만, 모음으로 시작하는 동사와 연결될 경우, "m'"가 됩니다. "appelle" 은 "call"을 의미하고, "Jim" 은 이름으로 번역되지 않습니다. 단어의 순서도 문제가 됩니다. "Je m'appelle Jim"를 단순히 대체하면, 영어와 다른 단어 순서로 "I myself call Jim"이 됩니다.
> 💁 어떤 단어는 번역되지 않습니다. 저를 소개하는데 어떤 언어를 사용하는지에 관계없이 제 이름은 Jim 입니다. 다른 알파벳을 사용하거나 다른 소리에 대해 다른 글자를 사용하는 언어의 경우, 단어는 _음역_ 될 수 있습니다. 이는 동일한 단어에 대해 적절한 소리를 내는 글자를 선택하는 것입니다.
관용구도 번역의 문제입니다. 이는 단어의 직역과 다른 의미를 가진 구입니다. 예를 들어, 영어 관용구 "I've got ants in my pants" 는 문자 그대로 당신의 옷에 개미가 있다는 것이 아니라, 안절부절 못하는 것을 의미합니다. 이를 독일어로 번역하면 "내 아래에 땅벌을 가지고 있다" 가 되어, 듣는 사람을 혼란스럽게 만들 것입니다.
> 💁 다른 지역성도 다른 복잡성을 추가합니다. 관용구 "ants in your pants"는, 미국 영어로 "pants" 는 겉옷을, 영국 영어로 "pants" 는 속옷을 의미합니다.
✅ 여러 언어를 사용하는 경우, 직역되지 않는 구문을 생각해 보세요.
기계 번역 시스템은 통계적 방법과 가능한 옵션에서 적절한 번역을 선택하기 위한 특정 구문과 관용구를 방법을 설명하는 대규모 규칙 데이터베이스에 의존합니다. 이러한 통계적 방법은 인간이 여러 언어로 번역한 거대한 데이터베이스를 사용하여 가장 가능성이 높은 번역을 선택하는 _통계적 기계 번역_ 이라는 기술 입니다. 이들 중 다수는 언어의 중간 표현을 사용하여, 한 언어를 중간 언어로 번역한 다음 중간 언어에서 다른 언어로 번역할 수 있습니다. 이 방법으로 더 많은 언어를 추가하려면 다른 모든 언어가 아닌 중간 언어로의 번역이 필요합니다.
### 신경망 번역
신경망 번역에는 AI를 사용하는 번역이 포함되며, 일반적으로 하나의 모델을 사용하여 전체 문장을 번역합니다. 이러한 모델은 웹 페이지, 책과 국제 문서와 같이 사람이 번역한 거대한 데이터 셋에 대해 훈련됩니다.
신경망 번역 모델은 구와 관용어의 거대한 데이터베이스가 필요하지 않기 때문에 일반적으로 기계 번역 모델보다 작습니다. 현대 AI 서비스는 통계적 기계 번역과 신경망 번역을 혼합한 여러 기술을 혼합한 번역을 제공합니다.
모든 언어 쌍에 대한 1:1 번역은 없습니다. 다른 번역 모델은, 모델 훈련 시 사용된 언어에 따라 아주 작은 차이의 결과를 생성합니다. 번역이 항상 대칭적인 것은 아닙니다. 한 언어에서 다른 언어로 문장을 번역 한 다음, 다시 처음 언어로 번역하면 결과적으로 약간 다른 문장이 나타날 있습니다.
✅ Apple 번역 앱이나 [Bing Translate](https://www.bing.com/translator), [Google Translate](https://translate.google.com)과 같은 다른 온라인 번역기를 사용해보세요. 몇 문장의 번역된 버전을 비교해보세요. 또한 번역한 것을 다시 번역해보세요.
## 번역 서비스
애플리케이션에서 음성 및 텍스트를 번역하는 데 사용할 수 있는 여러 AI 서비스가 있습니다.
### 인지 서비스 음성 서비스
![The speech service logo](../../../../images/azure-speech-logo.png)
지난 수업에서 사용한 음성 서비스에는 음성 인식을 위한 번역 기능이 있습니다. 음성을 인식하면, 음성에 대한 텍스트 뿐만 아니라 다른 언어의 텍스트를 요청할 수 있습니다.
> 💁 이것은 음성 SDK에서만 사용할 수 있으며, REST API에는 번역이 내장되어 있지 않습니다.
### 인지 서비스 번역 서비스
![The translator service logo](../../../../images/azure-translator-logo.png)
번역 서비스는 한 언어에서 하나 이상의 언어의 텍스트로 번역할 수 있는 전용 번역 서비스 입니다. 번역 뿐만 아니라 욕설을 포함한 다양한 추가 기능을 지원합니다. 또한 번역을 원하지 않는 용어나 잘 알려진 특정한 번역 같이, 특정 단어나 문장에 대해 특정 번역을 제공합니다.
예를 들어, "I have a Raspberry Pi"라는 문장을 프랑스어와 같이 다른 언어로 번역할 때, 싱글 보드 컴퓨터를 가리키는 "Raspberry Pi"라는 이름 그대로 유지하고 싶을 것입니다. 그 결과 "Jai une pi aux framboises" 대신 "Jai un Raspberry Pi"를 얻게 됩니다.
## 번역가 리소스 만들기
이 강의에서는 번역가 리소스가 필요합니다. REST API를 사용해 텍스트를 번역합니다.
### 작업 - 번역가 리소스 만들기
1. 터미널 또는 명령 프롬프트에서 다음 명령을 실행하여 `smart-timer` 리소스 그룹에 변환기 리소스를 만듭니다.
```sh
az cognitiveservices account create --name smart-timer-translator \
--resource-group smart-timer \
--kind TextTranslation \
--sku F0 \
--yes \
--location <location>
```
`<location>` 을 리소스 그룹을 생성할 때 사용한 위치로 바꿉니다.
2. 번역 서비스 key 가져오기:
```sh
az cognitiveservices account keys list --name smart-timer-translator \
--resource-group smart-timer \
--output table
```
key 중 하나를 복사하십시오.
## 애플리케이션의 번역에 여러 언어 지원하기
이상적인 세계에서, 전체 응용 프로그램은 음성 듣기, 음성 이해, 음성 응답에 이르기까지 가능한 많은 다른 언어를 이해해야 합니다. 이것은 많은 작업이므로, 번역 서비스를 사용하면 응용 프로그램에 제공되는 시간을 단축할 수 있습니다.
![A smart timer architecture translating Japanese to English, processing in English then translating back to Japanese](../../../../images/translated-smart-timer.png)
스마트 타이머를 구축한다고 생각해보십시오. 이는 전체 영어를 사용하고, 구어체 영어를 이해하여 텍스트로 변환하고, 영어로 언어 이해를 실행하고, 응답을 영어로 구축해 영어로 응답합니다. 일본어에 대한 지원을 추가하려는 경우, 일본어 음성을 영어 텍스트로 번역하는 것을 시작으로, 응용 프로그램의 핵심을 동일하게 적용합니다. 그런 다음, 응답을 말하기 전에 텍스트로 번역합니다.이렇게 하면 일본어 지원을 빠르게 추가할 수 있으며, 나중에 전체 일본어 지원을 제공하도록 확장할 수 있습니다.
> 💁 기계 번역에 의존하는 것의 단점은 언어와 문화에 따라 같은 것이라도 말하는 방식이 때문에, 번역이 예상한 표현과 일치하지 않을 수 있다는 것입니다.
또한 기계 번역은 사용자가 만든 콘텐츠를 생성할 때 번역할 수 있는 앱과 장치에 대한 가능성을 열어줍니다. 공상 과학 소설에는 주로 외계인 언어를 (일반적으로) 미국 영어로 번역할 수 있는 '범용 번역기'가 등장합니다. 이 장치는 외계인이라는 부분을 무시한다면 공상 과학이 아니라 과학 사실에 가깝습니다. 음성 및 번역 서비스의 조합을 사용하여 음성 및 서면 텍스트의 실시간 번역을 제공하는 앱과 장치가 이미 존재합니다.
이 비디오에서 시연되는 한가지 예로 [Microsoft 번역기](https://www.microsoft.com/translator/apps/?WT.mc_id=academic-17441-jabenn) 휴대폰 앱이 있습니다:
[![Microsoft Translator live feature in action](https://img.youtube.com/vi/16yAGeP2FuM/0.jpg)](https://www.youtube.com/watch?v=16yAGeP2FuM)
> 🎥 위 이미지를 클릭하면 영상을 볼 수 있습니다.
특히 여행을 가거나 당신이 모르는 언어를 사용하는 사람들과 교류할 때, 그러한 장치를 사용할 수 있다고 상상해 보십시오. 공항이나 병원에 자동 번역 장치가 있다면 접근성이 크게 향상될 것입니다.
✅ 조사해보세요: 상업적으로 사용 가능한 번역 IoT 장치가 있습니까? 스마트 기기에 내장된 번역 기능은 어떻습니까?
> 👽 외계인과 대화할 수 있는 진정한 범용 번역기는 없지만 [Microsoft 번역기는 Klingon를 지원합니다.](https://www.microsoft.com/translator/blog/2013/05/14/announcing-klingon-for-bing-translator/?WT.mc_id=academic-17441-jabenn). Qapla!
## AI 서비스를 사용해 텍스트 번역하기
AI 서비스를 사용하여 이 번역 기능을 스마트 타이머에 추가할 수 있습니다.
### 작업 - AI 서비스를 사용해 텍스트 번역하기
관련 가이드를 통해 IoT 장치에서 번역 텍스트를 변환합니다:
- [Arduino - Wio Terminal](../wio-terminal-translate-speech.md)
- [싱글 보드 컴퓨터 - Raspberry Pi](pi-translate-speech.ko.md)
- [싱글 보드 컴퓨터](../virtual-device-translate-speech.md)
---
## 🚀 도전
기계 번역이 스마트 기기 이외의 다른 IoT 애플리케이션에 어떤 이점을 제공할 수 있습니까? 말 뿐만 아니라 텍스트로도 번역이 도움이 될 수 있는 다양한 방법을 생각해 보십시오.
## 강의 후 퀴즈
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/48)
## 복습 및 독학
- [Wikipedia의 기계 번역 페이지](https://wikipedia.org/wiki/Machine_translation)에서 기계 번역에 대해 자세히 알아보세요.
- [Wikipedia의 신경망 기계 번역](https://wikipedia.org/wiki/Neural_machine_translation)에서 신경망 기계 번역에 대해 자세히 알아보세요.
- [Microsoft Docs의 음성 서비스 문서에 대한 언어 및 음성 지원](https://docs.microsoft.com/azure/cognitive-services/speech-service/language-support?WT.mc_id=academic-17441-jabenn)에서 Microsoft 음성 서비스에 대해 지원되는 언어 목록을 확인하세요.
## 과제
[범용 번역기 구축하기](../assignment.md)

@ -0,0 +1,17 @@
# 범용 번역기 구축하기
## 지침
범용 번역기는 여러 언어를 번역할 수 있는 장치로, 다른 언어를 사용하는 사람들이 의사소통할 수 있도록 합니다. 지난 수업에서 배운 내용을 사용하여 2개의 IoT 장치를 사용하여 범용 번역기를 구축하십시오.
> 2개의 장치가 없는 경우, 이전 수업의 단계에 따라 가상 IoT 장치를 하나의 IoT 장치처럼 설정합니다.
하나의 언어에 대해 하나의 장치를 구성하고 다른 언어에 대해 또다른 장치 하나를 구성해야 합니다. 각각의 장치는 음성을 받아들이고, 텍스트로 변환하고, IoT Hub 와 Functions 앱을 통해 다른 장치로 보낸 다음, 그것을 번역하여 음성으로 재생합니다.
> 💁 팁: 장치에서 다른 장치로 음성을 보낼 때, 사용중인 언어도 함께 보내어 번역하기 쉽도록 합니다. 먼저 IoT Hub 와 Functions 앱을 사용하여 각 장치를 등록하고, Azure Storage에 저장히기 위해 지원하는 언어를 전달할 수도 있습니다. 그런 다음 번역을 위해 Functions 앱을 사용하고, IoT 장치로 번역된 텍스트를 전송할 수 있습니다.
## 평가 기준
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
| ------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------- |
| 범용 번역기 만들기 | 한 장치에서 감지된 음성을 다른 장치에서 다른 언어로 변환되어 재생되는 범용 번역기를 구축할 수 있음 | 캡처된 음성 또는 번역과 같은 일부 구성 요소는 번역할 수 있지만, 모든 문제를 해결할 수 없음 | 범용 번역기의 어떤 부분도 만들 수 없음 |

@ -0,0 +1,112 @@
# 음성을 텍스트로 변환하기 - Raspberry Pi
이 단원에서는 음성 서비스를 사용하여 캡처한 오디오의 음성을 텍스트로 변환하는 코드를 작성합니다.
## 음성 서비스로 오디오 보내기
REST API를 사용하여 음성 서비스를 오디오로 보낼 수 있습니다. 음성 서비스를 사용하려면, 먼저 접근 토큰을 요청한 다음, 해당 토큰을 사용하여 REST API에 접근합니다. 이러한 접근 토큰은 10분 후에 만료되므로, 항상 최신의 상태인지 확인하기 위해 코드에서 정기적으로 요청해야 합니다.
### 작업 - 접근 토큰 가져오기
1. Pi에서 `smart-timer` 프로젝트를 엽니다.
2. `play_audio` 함수를 제거합니다. 스마트 타이머가 당신이 말한 것을 다시 반복하는 것을 원하지 않기 때문에 더이상 필요하지 않습니다.
3. `app.py` 파일의 맨 위에 다음 import 를 추가합니다:
```python
import requests
```
4. 음성 서비스에 대한 일부 설정을 위해 `while True` 루프 위에 다음 코드를 추가합니다:
```python
speech_api_key = '<key>'
location = '<location>'
language = '<language>'
```
`<key>` 를 음성 서비스 리소스의 API 키로 바꿉니다. `<location>` 를 음성 서비스 리소스를 만들 때 사용한 위치로 바꿉니다.
`<language>` 를 당신이 사용할 언어의 지역 이름으로 바꿉니다. 예를 들어 영어는 `en-GB`, 광둥어는 `zn-HK`로 표기합니다. 지원되는 언어 목록과 해당 지역 이름은 [Language and voice support documentation on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/speech-service/language-support?WT.mc_id=academic-17441-jabenn#speech-to-text) 에서 찾을 수 있습니다.
5. 이 아래에, 다음 함수를 추가하여 접근 토큰을 가져옵니다:
```python
def get_access_token():
headers = {
'Ocp-Apim-Subscription-Key': speech_api_key
}
token_endpoint = f'https://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken'
response = requests.post(token_endpoint, headers=headers)
return str(response.text)
```
이것은 토큰 isuuing endpoint 를 호출해, API 키를 헤더처럼 전달합니다. 이 호출은 음성 서비스를 호출하는데 사용되는 접근 토큰을 반환합니다.
6. 이 아래에, REST API를 사용해 캡쳐된 오디오의 음성을 텍스트로 변환하는 함수를 선언합니다:
```python
def convert_speech_to_text(buffer):
```
7. 이 함수 내에서, REST API URL과 헤더를 설정합니다:
```python
url = f'https://{location}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1'
headers = {
'Authorization': 'Bearer ' + get_access_token(),
'Content-Type': f'audio/wav; codecs=audio/pcm; samplerate={rate}',
'Accept': 'application/json;text/xml'
}
params = {
'language': language
}
```
이것은 음성 서비스 리소스의 위치를 사용하여 URL을 빌드합니다. 그런 다음 `get_access_token` 함수의 접근 토큰과 오디오 캡처에 사용되는 샘플 rate으로 헤더를 채웁니다. 마지막으로 URL과 함께 전달할 오디오의 언어가 포함된 일부 매개변수를 정의합니다.
8. 이 아래에, 다음 코드를 추가하여 REST API를 호출하고 텍스트를 다시 가져옵니다:
```python
response = requests.post(url, headers=headers, params=params, data=buffer)
response_json = response.json()
if response_json['RecognitionStatus'] == 'Success':
return response_json['DisplayText']
else:
return ''
```
이는 URL을 호출하고 응답으로 들어오는 JSON 값을 디코딩합니다. 응답의 `RecognitionStatus` 값은 호출이 성공적으로 음성을 텍스트로 추출할 수 있는지 나타냅니다. `Success`인 경우 함수는 텍스트를 반환하고 아닌 경우, 빈 문자열이 반환됩니다.
9. `while True:` 루프 위에, 음성을 텍스트로 변환하는 서비스에서 반환되는 텍스트를 처리하는 함수를 정의합니다. 이 함수는 콘솔에 텍스트를 텍스트를 콘솔에 출력합니다.
```python
def process_text(text):
print(text)
```
10. 마지막으로 `while True` 루프 안의 호출을 `play_audio``convert_speech_to_text` 함수로 바꾸고, 텍스트를 `process_text` 함수에 전달합니다:
```python
text = convert_speech_to_text(buffer)
process_text(text)
```
11. 코드를 실행합니다. 버튼을 누르고 마이크에 대고 말합니다. 완료되면 버튼을 놓고, 오디오가 텍스트로 변환되어 콘솔에 출력될 것입니다.
```output
pi@raspberrypi:~/smart-timer $ python3 app.py
Hello world.
Welcome to IoT for beginners.
```
동음이의어를 포함한, 다른 유형의 문장을 시도합니다. 예를 들어, 영어로 말할 때, 'I want to buy two bananas and an apple too' 라고 말하고, 어떻게 올바르게 사용되는지 주목하세요. two 와 too 는 소리 뿐만 아니라, 단어의 문맥에 기반합니다.
> 💁 이 코드는 [code-speech-to-text/pi](code-speech-to-text/pi) 폴더에서 찾을 수 있습니다.
😀 음성을 텍스트로 변환하는 프로그램이 성공적으로 완료되었습니다!

@ -81,30 +81,30 @@ We have two choices of IoT hardware to use for the projects depending on persona
| | Project Name | Concepts Taught | Learning Objectives | Linked Lesson |
| :---: | :------------------------------------: | :---------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------: |
| 01 | [Getting started](./1-getting-started) | Introduction to IoT | Learn the basic principles of IoT and the basic building blocks of IoT solutions such as sensors and cloud services whilst you are setting up your first IoT device | [Introduction to IoT](./1-getting-started/lessons/1-introduction-to-iot/README.md) |
| 02 | [Getting started](./1-getting-started) | A deeper dive into IoT | Learn more about the components of an IoT system, as well as microcontrollers and single-board computers | [A deeper dive into IoT](./1-getting-started/lessons/2-deeper-dive/README.md) |
| 03 | [Getting started](./1-getting-started) | Interact with the physical world with sensors and actuators | Learn about sensors to gather data from the physical world, and actuators to send feedback, whilst you build a nightlight | [Interact with the physical world with sensors and actuators](./1-getting-started/lessons/3-sensors-and-actuators/README.md) |
| 04 | [Getting started](./1-getting-started) | Connect your device to the Internet | Learn about how to connect an IoT device to the Internet to send and receive messages by connecting your nightlight to an MQTT broker | [Connect your device to the Internet](./1-getting-started/lessons/4-connect-internet/README.md) |
| 05 | [Farm](./2-farm) | Predict plant growth | Learn how to predict plant growth using temperature data captured by an IoT device | [Predict plant growth](./2-farm/lessons/1-predict-plant-growth/README.md) |
| 06 | [Farm](./2-farm) | Detect soil moisture | Learn how to detect soil moisture and calibrate a soil moisture sensor | [Detect soil moisture](./2-farm/lessons/2-detect-soil-moisture/README.md) |
| 07 | [Farm](./2-farm) | Automated plant watering | Learn how to automate and time watering using a relay and MQTT | [Automated plant watering](./2-farm/lessons/3-automated-plant-watering/README.md) |
| 08 | [Farm](./2-farm) | Migrate your plant to the cloud | Learn about the cloud and cloud-hosted IoT services and how to connect your plant to one of these instead of a public MQTT broker | [Migrate your plant to the cloud](./2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md) |
| 09 | [Farm](./2-farm) | Migrate your application logic to the cloud | Learn about how you can write application logic in the cloud that responds to IoT messages | [Migrate your application logic to the cloud](./2-farm/lessons/5-migrate-application-to-the-cloud/README.md) |
| 10 | [Farm](./2-farm) | Keep your plant secure | Learn about security with IoT and how to keep your plant secure with keys and certificates | [Keep your plant secure](./2-farm/lessons/6-keep-your-plant-secure/README.md) |
| 11 | [Transport](./3-transport) | Location tracking | Learn about GPS location tracking for IoT devices | [Location tracking](./3-transport/lessons/1-location-tracking/README.md) |
| 12 | [Transport](./3-transport) | Store location data | Learn how to store IoT data to be visualized or analysed later | [Store location data](./3-transport/lessons/2-store-location-data/README.md) |
| 13 | [Transport](./3-transport) | Visualize location data | Learn about visualizing location data on a map, and how maps represent the real 3d world in 2 dimensions | [Visualize location data](./3-transport/lessons/3-visualize-location-data/README.md) |
| 14 | [Transport](./3-transport) | Geofences | Learn about geofences, and how they can be used to alert when vehicles in the supply chain are close to their destination | [Geofences](./3-transport/lessons/4-geofences/README.md) |
| 15 | [Manufacturing](./4-manufacturing) | Train a fruit quality detector | Learn about training an image classifier in the cloud to detect fruit quality | [Train a fruit quality detector](./4-manufacturing/lessons/1-train-fruit-detector/README.md) |
| 16 | [Manufacturing](./4-manufacturing) | Check fruit quality from an IoT device | Learn about using your fruit quality detector from an IoT device | [Check fruit quality from an IoT device](./4-manufacturing/lessons/2-check-fruit-from-device/README.md) |
| 17 | [Manufacturing](./4-manufacturing) | Run your fruit detector on the edge | Learn about running your fruit detector on an IoT device on the edge | [Run your fruit detector on the edge](./4-manufacturing/lessons/3-run-fruit-detector-edge/README.md) |
| 18 | [Manufacturing](./4-manufacturing) | Trigger fruit quality detection from a sensor | Learn about triggering fruit quality detection from a sensor | [Trigger fruit quality detection from a sensor](./4-manufacturing/lessons/4-trigger-fruit-detector/README.md) |
| 19 | [Retail](./5-retail) | Train a stock detector | Learn how to use object detection to train a stock detector to count stock in a shop | [Train a stock detector](./5-retail/lessons/1-train-stock-detector/README.md) |
| 20 | [Retail](./5-retail) | Check stock from an IoT device | Learn how to check stock from an IoT device using an object detection model | [Check stock from an IoT device](./5-retail/lessons/2-check-stock-device/README.md) |
| 21 | [Consumer](./6-consumer) | Recognize speech with an IoT device | Learn how to recognize speech from an IoT device to build a smart timer | [Recognize speech with an IoT device](./6-consumer/lessons/1-speech-recognition/README.md) |
| 22 | [Consumer](./6-consumer) | Understand language | Learn how to understand sentences spoken to an IoT device | [Understand language](./6-consumer/lessons/2-language-understanding/README.md) |
| 23 | [Consumer](./6-consumer) | Set a timer and provide spoken feedback | Learn how to set a timer on an IoT device and give spoken feedback on when the timer is set and when it finishes | [Set a timer and provide spoken feedback](./6-consumer/lessons/3-spoken-feedback/README.md) |
| 24 | [Consumer](./6-consumer) | Support multiple languages | Learn how to support multiple languages, both being spoken to and the responses from your smart timer | [Support multiple languages](./6-consumer/lessons/4-multiple-language-support/README.md) |
| 01 | [Getting started](./1-getting-started/README.md) | Introduction to IoT | Learn the basic principles of IoT and the basic building blocks of IoT solutions such as sensors and cloud services whilst you are setting up your first IoT device | [Introduction to IoT](./1-getting-started/lessons/1-introduction-to-iot/README.md) |
| 02 | [Getting started](./1-getting-started/README.md) | A deeper dive into IoT | Learn more about the components of an IoT system, as well as microcontrollers and single-board computers | [A deeper dive into IoT](./1-getting-started/lessons/2-deeper-dive/README.md) |
| 03 | [Getting started](./1-getting-started/README.md) | Interact with the physical world with sensors and actuators | Learn about sensors to gather data from the physical world, and actuators to send feedback, whilst you build a nightlight | [Interact with the physical world with sensors and actuators](./1-getting-started/lessons/3-sensors-and-actuators/README.md) |
| 04 | [Getting started](./1-getting-started/README.md) | Connect your device to the Internet | Learn about how to connect an IoT device to the Internet to send and receive messages by connecting your nightlight to an MQTT broker | [Connect your device to the Internet](./1-getting-started/lessons/4-connect-internet/README.md) |
| 05 | [Farm](./2-farm/README.md) | Predict plant growth | Learn how to predict plant growth using temperature data captured by an IoT device | [Predict plant growth](./2-farm/lessons/1-predict-plant-growth/README.md) |
| 06 | [Farm](./2-farm/README.md) | Detect soil moisture | Learn how to detect soil moisture and calibrate a soil moisture sensor | [Detect soil moisture](./2-farm/lessons/2-detect-soil-moisture/README.md) |
| 07 | [Farm](./2-farm/README.md) | Automated plant watering | Learn how to automate and time watering using a relay and MQTT | [Automated plant watering](./2-farm/lessons/3-automated-plant-watering/README.md) |
| 08 | [Farm](./2-farm/README.md) | Migrate your plant to the cloud | Learn about the cloud and cloud-hosted IoT services and how to connect your plant to one of these instead of a public MQTT broker | [Migrate your plant to the cloud](./2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md) |
| 09 | [Farm](./2-farm/README.md) | Migrate your application logic to the cloud | Learn about how you can write application logic in the cloud that responds to IoT messages | [Migrate your application logic to the cloud](./2-farm/lessons/5-migrate-application-to-the-cloud/README.md) |
| 10 | [Farm](./2-farm/README.md) | Keep your plant secure | Learn about security with IoT and how to keep your plant secure with keys and certificates | [Keep your plant secure](./2-farm/lessons/6-keep-your-plant-secure/README.md) |
| 11 | [Transport](./3-transport/README.md) | Location tracking | Learn about GPS location tracking for IoT devices | [Location tracking](./3-transport/lessons/1-location-tracking/README.md) |
| 12 | [Transport](./3-transport/README.md) | Store location data | Learn how to store IoT data to be visualized or analysed later | [Store location data](./3-transport/lessons/2-store-location-data/README.md) |
| 13 | [Transport](./3-transport/README.md) | Visualize location data | Learn about visualizing location data on a map, and how maps represent the real 3d world in 2 dimensions | [Visualize location data](./3-transport/lessons/3-visualize-location-data/README.md) |
| 14 | [Transport](./3-transport/README.md) | Geofences | Learn about geofences, and how they can be used to alert when vehicles in the supply chain are close to their destination | [Geofences](./3-transport/lessons/4-geofences/README.md) |
| 15 | [Manufacturing](./4-manufacturing/README.md) | Train a fruit quality detector | Learn about training an image classifier in the cloud to detect fruit quality | [Train a fruit quality detector](./4-manufacturing/lessons/1-train-fruit-detector/README.md) |
| 16 | [Manufacturing](./4-manufacturing/README.md) | Check fruit quality from an IoT device | Learn about using your fruit quality detector from an IoT device | [Check fruit quality from an IoT device](./4-manufacturing/lessons/2-check-fruit-from-device/README.md) |
| 17 | [Manufacturing](./4-manufacturing/README.md) | Run your fruit detector on the edge | Learn about running your fruit detector on an IoT device on the edge | [Run your fruit detector on the edge](./4-manufacturing/lessons/3-run-fruit-detector-edge/README.md) |
| 18 | [Manufacturing](./4-manufacturing/README.md) | Trigger fruit quality detection from a sensor | Learn about triggering fruit quality detection from a sensor | [Trigger fruit quality detection from a sensor](./4-manufacturing/lessons/4-trigger-fruit-detector/README.md) |
| 19 | [Retail](./5-retail/README.md) | Train a stock detector | Learn how to use object detection to train a stock detector to count stock in a shop | [Train a stock detector](./5-retail/lessons/1-train-stock-detector/README.md) |
| 20 | [Retail](./5-retail/README.md) | Check stock from an IoT device | Learn how to check stock from an IoT device using an object detection model | [Check stock from an IoT device](./5-retail/lessons/2-check-stock-device/README.md) |
| 21 | [Consumer](./6-consumer/README.md) | Recognize speech with an IoT device | Learn how to recognize speech from an IoT device to build a smart timer | [Recognize speech with an IoT device](./6-consumer/lessons/1-speech-recognition/README.md) |
| 22 | [Consumer](./6-consumer/README.md) | Understand language | Learn how to understand sentences spoken to an IoT device | [Understand language](./6-consumer/lessons/2-language-understanding/README.md) |
| 23 | [Consumer](./6-consumer/README.md) | Set a timer and provide spoken feedback | Learn how to set a timer on an IoT device and give spoken feedback on when the timer is set and when it finishes | [Set a timer and provide spoken feedback](./6-consumer/lessons/3-spoken-feedback/README.md) |
| 24 | [Consumer](./6-consumer/README.md) | Support multiple languages | Learn how to support multiple languages, both being spoken to and the responses from your smart timer | [Support multiple languages](./6-consumer/lessons/4-multiple-language-support/README.md) |
## Offline access

12
package-lock.json generated

@ -1506,9 +1506,9 @@
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
@ -3978,9 +3978,9 @@
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"

File diff suppressed because it is too large Load Diff

@ -14,9 +14,9 @@
"vue-router": "^3.4.9"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",

@ -0,0 +1,41 @@
# Nettoyez votre projet
Après avoir terminé chaque projet, il est bon de supprimer vos ressources cloud.
Dans les leçons de chaque projet, vous avez peut-être créé certains des éléments suivants:
* Un groupe de ressources
* Un hub IoT
* Des enregistrements d'appareils IoT
* Un compte de stockage
* Une application de fonctions
* Un compte Azure Maps
* Un projet de vision sur mesure
* Un registre de conteneurs Azure
* Une ressource de services cognitifs
La plupart de ces ressources n'auront aucun coût - soit elles sont entièrement gratuites, soit vous utilisez un plan gratuit. Pour les services qui nécessitent un plan payant, vous les auriez utilisés à un niveau inclus dans l'allocation gratuite, ou ne coûterait que quelques centimes.
Même avec des coûts relativement bas, cela vaut la peine de supprimer ces ressources lorsque vous avez terminé. Vous ne pouvez avoir qu'un seul hub IoT utilisant le plan gratuit par exemple, donc si vous souhaitez en créer un autre, vous devrez utiliser un plan payant.
Tous vos services ont été créés dans des groupes de ressources, ce qui facilite leur gestion. Vous pouvez supprimer le groupe de ressources et tous les services de ce groupe de ressources seront également supprimés.
Pour supprimer le groupe de ressources, exécutez la commande suivante dans votre terminal ou invite de commande:
```sh
az group delete --name <resource-group-name>
```
Remplacez `<resource-group-name>` par le nom du groupe de ressources qui vous est propre.
Une confirmation apparaîtra:
```output
Are you sure you want to perform this operation? (y/n):
```
Entrez `y` pour confirmer et supprimer le groupe de ressources.
Il faudra un certain temps pour supprimer tous les services.
> 💁 Vous pouvez en savoir plus sur la suppression des groupes de ressources dans la [documentation sur la suppression des groupes de ressources et des ressources Azure Resource Manager sur Microsoft Docs](https://docs.microsoft.com/azure/azure-resource-manager/management/delete-resource-group?WT.mc_id=academic-17441-jabenn&tabs=azure-cli)
Loading…
Cancel
Save