반응형
해당 글에서는 디바이스에 탑재되어 있는 자이로 센서를 이용하는 expo-sensors 라이브러리를 활용하여 자이로스코프(Gyroscope)와 디바이스 모션(DeviceMotion)을 이해하고 값을 추출하는 방법을 이해하기 위한 글입니다.
1) 라이브러리 사용 목적
💡 사용자가 디바이스를 어떠한 각도에서 카메라로 측정을 하고 있는지에 대한 값을 추출하고 이를 계산하여 각도 값을 도출하여 최적의 디바이스의 카메라 각도를 파악하기 위해서 이를 추출 및 계산하여 데이터를 적재하는 것을 목적으로 하고 있습니다
2) 자이로 센서(Gyrosensor)와 자이로스코프(Gyroscope) 정의
💡 ‘자이로(Gyro)’라는 단어는 라틴어로 ‘회전하는 것’이라는 의미로 회전하는 물체의 회전각 센서를 통해 디바이스의 위치를 감지하는 것을 ‘자이로 센서(Gyrosensor)’라고 합니다. 이 자이로 센서를 기반으로 디바이스의 회전 위치 값의 범위를 의미하는 것이 ‘자이로 스코프(Gyroscope)'라고 합니다.
3) expo-sensors의 Gyroscope 메서드
1. Gyroscope 메서드 설명
💡 자이로 센서를 기반으로 expo-sensors의 Gyroscope 메서드는 액세스를 제공하여 3D 공간의 회전 변화에 응답하여 줍니다. 해당 메서드는 변화에 따라 값을 도출해 냅니다. 디바이스가 X, Y, Z 축이 모두 0인 상태에서 디바이스의 움직임에 따라서 각각의 값을 도출해 줍니다.
2. 적용 소스코드
💡 해당 소스 구성은 화면이 렌더링 되었을 때, Gyroscope 값을 불러오는 함수를 호출합니다.
이 함수에서 Gyroscope를 호출하여서, 디바이스의 감지 속도 시간을 ‘밀리세컨드’ 기준으로 얼마 시간 동안 움직임에 대한 감지 할지에 대한 지정을 합니다. 지정을 통해서 리스너를 수행하여서 디바이스의 변화된 위치 값을 도출해 줍니다.
useEffect(() => {
fn_gyroscope();
},[]);
/**
* [함수] expo-sensors를 기반으로 Gyroscope 값을 도출합니다.
*/
const fn_gyroscope = (): void => {
// 감지 속도 지정 (밀리세컨드 기준)
Gyroscope.setUpdateInterval(1000);
// 디바이스의 변화된 위치에 대해서 감지를 한다.
Gyroscope.addListener((gyroscopeData) => {
const { x, y, z } = gyroscopeData;
const round_x = Math.floor(x);
const round_y = Math.floor(y);
const round_z = Math.floor(z);
console.log(`디바이스 변화값 x: [${round_x}] y: [${round_y}] z: [${round_z}]`);
// 좌표값을 정수로 변경하여 변화가 발생하였을 경우 이를 체크함
if (round_x !== 0 || round_y !== 0 || round_z !== 0) {
console.log("[+] Device Position Change ");
}
});
};
[참고] 자이로 스코프 라이브러리 관련 공식 사이트
💡 해당 메서드를 사용해서는 디바이스가 위치한 위치 값을 알 수 없었고,
디바이스의 변화량 자체만 체크하기에 목적과 상이하다고 판단하였습니다.
4) expo-sensors의 DeviceMotion 메서드
1. DeviceMotion 메서드 설명
💡 DeviceMotion을 메서드도 자이로 센서를 기반으로 디바이스의 기준 값을 3개의 축으로 제공해 줍니다. 이는 X는 왼쪽에서 오른쪽으로, Y는 아래에서 위로, Z는 화면을 통해 뒤에서 앞으로 수직으로 이어지는 값을 반환해 줍니다.
[참고] Alpha, Beta, Gamma에 따른 방향 설명
[출처] https://levelup.gitconnected.com/tower-a-react-native-device-motion-example-app-8a8e81d333c3
[참고] 디바이스 모션 관련 공식 사이트
2. DeviceMotion의 인수
- listener (function) - DeviceMotion 업데이트를 사용할 수 있을 때 호출되는 콜백입니다. 호출 시 리스너에는 다음 필드를 포함하는 객체인 단일 인수가 제공됩니다.
- interval (number) - 기본 플랫폼에서 데이터를 가져오는 간격입니다. 밀리 초로 표시됩니다.
- acceleration (object) - x, y, z 키가 있는 개체로 세 축의 장치 가속. m/s 2로 표시됩니다.
- accelerationIncludingGravity (object) - x, y, z 키가 있는 개체로 세 축에 대한 중력의 영향을 받는 장치 가속. m/s 2로 표시됩니다.
- rotation (object) - 알파, 베타, 감마 키가 있는 객체로서의 공간에서의 장치 방향. 여기서 알파는 Z 축 주위 회전, 베타는 X축 회전, 감마는 Y축 회전입니다.
- rotationRate (object) - 초당 각도(deg/s)로 표시되는 공간에서의 장치 회전 속도입니다.
- alpha (number): X축 회전
- beta (number): Y축 회전
- gamma (number): Z 축 회전
- orientation (number) - 화면 회전을 기준으로 한 장치 방향입니다. 값은 0(세로), 90(오른쪽 가로), 180(거꾸로), -90(왼쪽 가로)에 있습니다.
3. 각각에 대한 값 설명
💡 DeviceMotion 메서드를 통해서 도출되는 Alpha, beta, Gamma 값을 통해서 각각 이동된 값을 실시간으로 도출해 줍니다.
[참고] 해당 메서드를 이용한 소스코드 작성
useEffect(() => {
fn_deviceMotion();
},());
const fn_deviceMotion = () => {
DeviceMotion.setUpdateInterval(1000);
DeviceMotion.addListener((_resultValue: DeviceMotionMeasurement) => {
// 장치의 좌표계 최소 -3.14 최대 3.14
const { alpha, beta, gamma } = _resultValue.rotation;
// 수평 기준의 디바이스 위치 값
const round_alpha = Math.round(alpha * 100) / 100;
const round_beta = Math.round(beta * 100) / 100;
const round_gamma = Math.round(gamma * 100) / 100;
}
}
[참고] 설명 기준(기본값) 값의 범위
값 | 설명 | 기준(기본값) | 값의 범위 |
Alpha | 디바이스의 좌우 움직임에 대한 값 | 디바이스의 위치가 북쪽(0) | -3.14 ~ 3.14 |
Beta | 디바이스의 상하 움직임에 대한 값 | 디바이스의 위치가 평지(0) | -1.57 ~ 1.57 |
Gamma | 디바이스의 회전 움직임에 대한 값 | 디바이스의 위치가 정면(0) | -3.14 ~ 3.14 |
4. 계산식을 적용한 각각의 각도 계산
💡 DeviceMotion 메서드를 통해서 도출되는 Alpha, beta, Gamma 값을 통해서 각각 이동된 값을 실시간으로 도출된 값을 통해서 각도를 계산합니다.
[참고] 해당 메서드를 이용한 소스코드 작성
[참고] 해당 메서드를 이용한 소스코드 작성
useEffect(() => {
fn_deviceMotion();
},[]);
const fn_deviceMotion = () => {
DeviceMotion.setUpdateInterval(1000);
DeviceMotion.addListener((_resultValue: DeviceMotionMeasurement) => {
// 장치의 좌표계 최소 -3.14 최대 3.14
const { alpha, beta, gamma } = _resultValue.rotation;
// 수평 기준의 디바이스 위치 값
const round_alpha = Math.round(alpha * 100) / 100;
const round_beta = Math.round(beta * 100) / 100;
const round_gamma = Math.round(gamma * 100) / 100;
// 위치 값에 따른 계산식 : (값) / 3.14 x 180
const x_angle = Math.round(round_alpha / 3.14 * 180);
const y_angle = Math.round(round_beta / 3.14 * 180);
const z_angle = Math.round(round_gamma / 3.14 * 180);
}
}
💡 위에 메서드에서 전달받은 내용을 기반으로 (값) / 3.14 x 180이라는 계산식을 통해서 결과를 도출하였음.
범위 | 기준(기본값) | 90도 | 180도 or 0도 | -90도 | |
Alpha | -180도 ~ 180도 | 디바이스의 위치가 북쪽(0도) | 디바이스의 위치가 서쪽(90도) | 디바이스의 위치가 남쪽(180도 or -180도) | 디바이스의 위치가 동쪽(-90도) |
Beta | -90도 ~ 90도 | 디바이스의 위치가 평지(0도) | 디바이스를 세운 경우(90도) | 디바이스를 눕힌 경우(0도) | 디바이스를 아래로 세운 경우(-90도) |
Gamma | -180도 ~ 180도 | 디바이스의 위치가 정면(0도) | 디바이스가 오른쪽을 바라본 경우(90도) | 디바이스를 눕힌 경우(180도) | 디바이스가 왼쪽을 바라본 경우(-90도) |
useEffect(() => {
fn_deviceMotion();
},[]);
/**
* [함수] expo-sensors의 DeviceMotion를 통하여 디바이스의 x,y,z 축을 구하는 함수
*/
const fn_deviceMotion = () => {
DeviceMotion.setUpdateInterval(1000);
DeviceMotion.addListener((_resultValue: DeviceMotionMeasurement) => {
// 장치의 좌표계 최소 -3.14 최대 3.14
const { alpha, beta, gamma } = _resultValue.rotation;
// console.log(`디바이스 포지션 지정 : round_alpha [${alpha}] / round_beta [${beta}] / round_gamma [${gamma}] `);
// 수평 기준의 디바이스 위치 값
const round_alpha = Math.floor(alpha * 100) / 100;
const round_beta = Math.floor(beta * 100) / 100;
const round_gamma = Math.floor(gamma * 100) / 100;
console.log(`디바이스 좌표값 x: [${round_alpha}] y: [${round_beta}] z: [${round_gamma}]`);
/**
* 위치 값에 따른 계산식 : (값) / 3.14 x 180
*/
const x_angle = Math.floor(round_alpha / 3.14 * 180);
const y_angle = Math.floor(round_beta / 3.14 * 180);
const z_angle = Math.floor(round_gamma / 3.14 * 180);
console.log(`x축 [${x_angle}도] y축 [${y_angle}도] z축[${z_angle}도]`);
});
}
[참고] 최종 적용 소스코드
[참고 소스코드] 최종 적용 소스코드
1. fn_insertTable() 함수 내에서는 디바이스의 각도를 추출받아서 x, y, z축의 값을 데이터베이스로 적재를 합니다.
2. fn_resultDeviceMotion() 함수 내에서는 DeviceMotion을 통해서 x, y, z축의 값을 반환해 줍니다.
** 해당 소스코드에서 부분에서 주의할 점이 있습니다.
DeviceMotion.getListenerCount()를 통해서 누적되는 리스너를 확인해 보았을 때, 매번 1개씩 누적이 되고 있습니다.
이에 따라서 관리를 위해 누적된 리스너를 _subscription.remove() 메서드를 통해서 제거를 해주어야 합니다.
import { DeviceMotion, DeviceMotionMeasurement } from 'expo-sensors';
const DeviceMotionComponent = () => {
/**
* [구조체] 디바이스의 축 각도
*/
type DeviceAxisAngleType = {
x_axis_angle: number;
y_axis_angle: number;
z_axis_angle: number;
}
/**
* [함수] 테이블 내에 디바이스의 x,y,z축의 각도 INSERT
*/
const fn_insertTable = () => {
const _insertObj = {
device_angle_x: 0,
device_angle_y: 0,
device_angle_z: 0,
}
const _subscription = DeviceMotion.addListener((_resultValue: DeviceMotionMeasurement) => {
DeviceMotion.setUpdateInterval(1000); // 감지 시간 지정(1초)
const _deviceMotionObj: DeviceAxisAngleType = fn_resultDeviceMotion(_resultValue); // [함수] 자이로 센서 기반의 디바이스 모션 관리 호출
// 자이로 센서로부터 받은 값이 있다면 INSERT
if (_deviceMotionObj) {
const { x_axis_angle, y_axis_angle, z_axis_angle } = _deviceMotionObj;
_insertObj.device_angle_x = x_axis_angle; // 디바이스 X축 값
_insertObj.device_angle_y = y_axis_angle; // 디바이스 Y축 값
_insertObj.device_angle_z = z_axis_angle; // 디바이스 Z축 값
fn_insertHeadPose(_insertObj); // Table Insert
if (DeviceMotion.getListenerCount() >= 1 && _subscription !== null) _subscription.remove(); // 루프를 수행하면서 쌓인 리스너 취소
}
});
}
/**
* [함수] 자이로 센서 기반의 디바이스 모션 관리
* @param { DeviceMotionMeasurement }_esultValue
* @returns {DeviceAngleType}
*/
const fn_resultDeviceMotion = (_resultValue: DeviceMotionMeasurement): DeviceAxisAngleType => {
const _resultObj: DeviceAxisAngleType = {
x_axis_angle: 0,
y_axis_angle: 0,
z_axis_angle: 0
}
if (_resultValue.rotation) {
// 장치의 좌표계 최소 -3.14 최대 3.14
const { alpha, beta, gamma } = _resultValue.rotation;
// 수평 기준의 디바이스 위치 값
const round_alpha: number = Math.round(alpha * 100) / 100;
const round_beta: number = Math.round(beta * 100) / 100;
const round_gamma: number = Math.round(gamma * 100) / 100;
// 위치 값에 따른 계산식 : (값) / 3.14 x 180
_resultObj.x_axis_angle = Math.round(round_alpha / 3.14 * 180);
_resultObj.y_axis_angle = Math.round(round_beta / 3.14 * 180);
_resultObj.z_axis_angle = Math.round(round_gamma / 3.14 * 180)
}
return _resultObj;
}
}
export default DeviceMotionComponent();
5) [참고] 메서드 활용 사례
[출처] https://www.youtube.com/watch?v=t9n9ZCWmbBM
[출처] Device Motion 관련 공식 사이트 예제
6) 오늘의 결론
해당 메서드의 정보를 기반으로 다양하게 활용이 되고 있고, 활용할 범위가 많을 것 같습니다.
이해하고 추후에 기능을 추가하게 된다면 활용하시면 좋을 것 같습니다.
오늘도 감사합니다😀
반응형
'React & React Native > 라이브러리 활용' 카테고리의 다른 글
[RN] React Native 페이지 이동 관리 이해하고 설정하기: react-native-navigation (4) | 2023.06.09 |
---|---|
[RN] react-native-cli에서 expo-cli 모듈 사용하기 (0) | 2022.05.15 |
[RN] React Native Tensorflow.js 메모리 관리 이해하기 (0) | 2022.04.10 |
[RN] React Native 앱 상태 관리 이해 및 구성 방법: AppState (0) | 2022.03.27 |
[RN] React Native Tensorflow의 blazeface 모델을 이용한 얼굴 감지 (6) | 2022.03.09 |