늦게나마 써보는 드론 제어시스템 개발 후기

[본 글은 드론 파일럿 커뮤니티 서비스 종료로 이관된 글 입니다]

[작성일 : 2015.01.28 19:30]

아래의 개발후기는 제가 2013부터 약 1년동안 개발을 진행했던 프로젝트에 관한 후기입니다.

처음에 미래창조과학부의 지원을 받아 프로젝트를 시작하게 되었습니다.

프로젝트 기간은 약 6개월으로 짧은편이었습니다.

처음엔 간략하게 리눅스 우분투로 구동되어 자동비행이 가능한 무인기를 목표로 잡았습니다.

그리고 기획을 거쳐 기종은 DJI F450 쿼드콥터로 결정하게 되었고 일단 셋트로 컨트롤러와 조종기셋트로 구매하게 되었습니다.

전에 일반 RC비행기는 날려본적이 있었지만 쿼드콥터나 헬기를 날려본적은 없었기에 구조에 대해 알아보려 집 근처 공터에서 몇번 날려보았습니다.

주로 헬리콥터 RC의 경우 날리기가 힘들다고 많이 들어서 쿼드콥터도 비슷하지 않을까 내심 긴장을 했었는데 실제로 조종해보니 생각보다 많이 쉬웠습니다.

자체 컨트롤러가 수평을 잡아주고 또 GPS키트를 구매했기 때문에 조종간에서 손을 놓으면 알아서 정지후 해당 위치와 고도에 정지하고 있었습니다.

사실 쿼드콥터를 구매하고 날려보기전 까지 수평잡는것과 위치고정 그리고 이동하는걸 어떻게 컨트롤 하면 좋을지 굉장히 많이 고민 했었는데 그 부분이 한번에 날아가는 느낌이었습니다.

프로젝트 기간이 짧았기 때문에 가장 기본적인 수평잡기, 위치고정등의 기능을 컨트롤러가 해준다는것을 보고 생각보다 많이 개발할 수 있을것 같았습니다.

개발을 시작하고 가장 첫번째로 이 쿼드콥터를 어떻게 조종할 것 인지 고민하기 시작하였습니다.

드론개발 정보 게시판에 썼던 [DJI F450  조종 신호 분석] 글에 나와있는것 처럼

1. 쿼드콥터 로터들을 직접 제어하는 방식

2. 기존 쿼드콥터 컨트롤러를 사용하면서 조종하는 방식

이 두가지가 있었습니다.

1번의 경우는 기존 쿼드콥터 컨트롤러를 제거하고 그 컨트롤러가 하는 일(수평제어, 위치고정)을 직접 하는 방식입니다.

2번의 경우는 기존 쿼드콥터의 컨트롤러를 사용하면서 사람이 조종기를 통해 조종하듯이 우분투 시스템이 쿼드콥터를 조종하는 방식입니다.

확실히 1번의 경우가 시간이 더 많이 들었기 때문에 저희는 2번의 방법을 선택하여 개발하기로 하였습니다.

2번의 경우는 일반 조종기 수신기의 인터페이스를 그대로 사용하기 때문에 쿼드콥터 뿐 만 아니라 멀티콥터, 헬리콥터, 비행기 등 다양한 무인기에도 적용할 수 있는점도 선택의 이유중 하나가 되었습니다.

그렇게 조종 방식을 결정하고 바로 인터페이스 분석을 진행하여 프로토타입 조종시스템을 만들었습니다.

가장 처음엔 오드로이드 보드를 통해 쿼드콥터로 조종신호를 전송하려 하였지만 위 글에 나와있는 여러 이유로 인해 아두이노를 통해 조종신호를 전송하게끔 구현하였습니다. 

임베디드는 처음이었기 때문에 여기까지 한 3달 걸린것 같습니다.

간단하게 

쿼드콥터 – 아두이노 – ODROID X2 (Ubuntu 12.04) — 내부망 WiFi — PC 

이렇게 테스트를 진행하여 잘 작동하는걸 확인하였습니다.

우분투 보드는 x86 아키텍쳐를 쓰는건 여러모로 비효율적이기 때문에 ARM 아키텍쳐를 사용하는 보드로 사용하기로 하였습니다.

그 중 삼성 엑시노스 쿼드코어를 사용하는 ODROID-X2 가 가장 괜찮았습니다.

총 2개를 구매를 했었는데 그 중 하나는 테스트 중 전압 5V를 넣는다는걸 12V를 넣는 바람에 64GB SD카드(비싼거..)와 함께 사망하였습니다..

쿼드코어 프로세서가 달린 보드로 결정한 이유중 하나는 HD급의 비디오를 PC로 전송을 하기 위함이었습니다.

자동비행도 좋지만 현재 상황을 관제자에게 전달하는것 역시 중요하였기 때문이었습니다.

비디오 전송은 여러가지를 사용해보다 결국 MJPEG를 사용하기로 하였습니다.

별도 스트리밍 서버를 구축할 필요도 없고 가장 가벼운게 MJPEG 였습니다.

아무리 쿼드코어라도 CPU에 너무 부담을 주게 되면 전력소모도 심하고 다른 서비스에 영향을 줄 수도 있었기 때문입니다.


위 사진이 개발에 사용된 ODROID-X2 보드입니다.


여기까지 되어서 나온 PC에서의 조종 프로그램입니다.

Control Status 는 PC에 연결된 조이스틱의 상태를 보여주고

MODE는 현재 비행모드

MODE 오른쪽의 버튼은 모드선택

그리고 Network Statics 의 IP는 ODROID의 IP

MODE는 Connect Status (연결상태)인데 잘못 적혀있습니다.

그리고 Wireless Signal 은 WiFi의 신호세기를 말합니다.

비행모드 설명:

GPS 모드 – 자동으로 고도,위치 고정 / 자동으로 수평유지

Attitude 모드 – 자동으로 고도 고정 / 자동으로 수평유지

Manual 모드 – 수동모드 / 수평유지 안함


왼쪽편에 나오는 영상은 HD급은 아니지만 현재 비행상태를 충분히 확인할수 있을 정도의 화질이었습니다.

여기까지 개발이 된 후 첫번째로 테스트 비행을 해보았습니다.

테스트비행은 주말에 아무도 없는 학교 운동장에서 허가를 받고 진행하였습니다.

원래는 노트북을 보며 조종을 해야 했는데 햇빛이 너무 강해 노트북 화면이 제대로 보이지 않았고 

카메라가 심하게 떨려 전송되는 화면을 통해서는 조종할 수 없는 정도였습니다..

일단 좌우로 움직이고 앞뒤 위아래 등 다양한 동작에서 적은 딜레이로 날렵한 움직임을 보여주었습니다.

물론 전혀 문제도 없었습니다.

다만 테스트중 와이파이 신호가 끊기면 어떻게 되는지 궁금해서 쿼드콥터를 멀리 보내보았습니다.

결과는 쿼드콥터가 연결이 끊기기 바로 전 상태 그대로 앞으로 돌진을 하더군요….

예외처리를 하긴 했었는데 무언가 부족한 부분이 있었나봅니다..

팀원들이 완전 사색이 되어서 와이파이를 들고 다시 연결시키려 뛰어가는데 이번엔 PC 클라이언트가 에러를 일으키며 멈추었습니다.

다행히 쿼드콥터는 운동장을 벗어나지 않고 운동장을 구분하는 나무에 걸렸습니다.

그런데 어딘가 선이 빠진 모양인지 와이파이도 자동으로 잡지 못하고 연결이 안되더군요..

결국 배터리가 다 되어 멈추기를 기다렸고 10분쯤 뒤에 모터가 멈춘 뒤 나무를 타고 올라가 꺼내왔었습니다.

지금 다시 생각해도 소름이 끼치게 무섭습니다..

이 경험을 토대로 예외처리는 확실히 하게 되었습니다..

테스트에 사용했던 완벽한 무선랜입니다.

전원도 배터리를 사용해 완벽한 무선입니다.

외부에 연결될 필요 없어 내부망만 있으면 되었기 때문에 이렇게 구성하였습니다.

이렇게 한번 추락시키고 나니 그 다음에는 외부에서 테스트하기가 겁나더군요..

그래서 여러 부분을 수정하고 최적화 해서 이번엔 실내에서 시험비행을 하였습니다.

실내비행에 앞서 쿼드콥터의 프로펠러가 굉장히 위험하기 때문에 스티로폼을 잘라 가이드를 만들었습니다.

이렇게 가이드를 만든 뒤 회사 소강당에서 테스트 비행을 시도해보았습니다.

비행에 앞서 비싼 프로젝터도 혹시나 다치지 않게 꽁꽁 싸맸습니다.

바로 위의 형광등도 이불을 덮어주고 쿼드콥터를 셋팅하였습니다.

와이파이를 켜고 노트북으로 쿼드콥터에 연결한 후 테스트로 시동도 걸어보고 비행을 시작하였습니다.

결과는 부드럽게 잘 비행하였습니다.

배터리가 부족해서 출력이 부족하긴 했지만 생각보다 부드럽게 잘 비행해주었습니다.

여기까지가 아마 5개월째 였던것 같습니다.

원래는 자동조종까지 기획을 하고 있었지만 그 부분은 차후 프로젝트로 미루기로 하였고 가장 기본적인 수동조종을 충실히 하는쪽으로 개발을 진행하였습니다.

ODROID에 올라간 서버는 Python 으로 제작하였고 PC와는 내부와이파이를 통해 Socket통신을 하였습니다.

PC는 C#으로 개발하였고 조이스틱 라이브러리를 통해 Logitech 의 비행게임용 조이스틱을 지원하였습니다.

미국 공군이 사용하는 XBox 조이스틱도 지원이 가능한것 처럼 보였지만 가지고있지 않아 테스트는 할 수 없었습니다.

ODROID는 어디에 붙이면 좋을까 고민하다가 결국엔 쿼드콥터 바닥에 거꾸로 붙이게 되었습니다.

Scotch 에서 만든 Dual Lock 이라는 찍찍이로 붙이니 튼튼하게 고정되었습니다.

왼쪽 다리에는 아두이노를 붙였습니다.

사실 쿼드콥터는 균형을 잘 맞추어야 하는데 따로 붙일곳이 마땅치 않아 무게도 얼마 나가지 않기 때문에 왼쪽다리에 살짝 붙였습니다.

그리고 이 사진에는 안나와있지만 최종적으로는 전선들의 합선을 막기위해서와 선이 빠지지 않게 하기위해 글루건으로 마감처리를 해주었습니다.

Netgear 의 무선랜카드를 사용하였습니다.

ARM보드에서 지원되는 무선랜카드가 많지 않다고 가장 호환성이 좋은 이 모델로 구매했는데 알고보니 많은 무선랜카드들이 ARM보드에서 지원이 되더군요..

덕분이 이 무선랜카드는 무려 영국에서 직수입해왔습니다.

그리고 실내에서 개발 할 때는 이렇게 유선랜을 연결하여 개발하였습니다.

나중에는 쿼드콥터를 실외에서 테스트 할 때 도망가지 못하게 하기 위해 아예 랜선을 연결하고 쿼드콥터에 칭칭감아서 고정시킨 후 강아지 목줄처럼 사용하자는 의견도 나왔었지만 실내테스트를 마지막으로 테스트는 하지 못하였습니다.

마지막으로 제 개발환경입니다.

저는 자리가 여러개 있어서 쿼드콥터 개발하는 자리, 납땜하는 자리, 소프트웨어 개발하는 자리 이렇게 있었는데 

그 중 쿼드콥터를 가장 많이 만졌던 자리의 사진을 올려보았습니다.

사실 굉장히 더러웠었는데 사진찍기전 많이 치웠습니다^^

마무리를 지으면서

프로젝트 기간은 많이 짧았는데 지금 생각해보면 굉장히 긴것 같았고 재밌었던 시간이었습니다.

지금은 그 회사에서도 나왔고 팀원도 다 흩어진 상태지만 나중에 여유가 되게 된다면 다시 이어서 진행해보고 싶은 프로젝트 입니다.

이 때 개발했던 쿼드콥터는 제가 집으로 가져와 지금 책장 위에 먼지만 쌓이고 있네요

요즘은 다시 먼지를 털고 항공촬영용 Gimbal을 달아 다시한번 날려볼까 합니다.

언젠간 제가 만든 드론 제어 시스템으로 집에 앉아서 외국을 돌아다녀 볼 수도 있었으면 좋겠네요^^

DJI F450 조종 신호 분석

[본 글은 드론 파일럿 커뮤니티 서비스 종료로 이관된 글 입니다]

[작성일 : 2015.01.26 01:26]


DJI F450 NAJA M PWM Signal Analysis


DJI F450 의 조종신호를 분석해 본 내용에 대해 정리 해보았습니다.

처음에 제가 DJI F450을 구매를 했을땐 Futaba 조종기와 연결이 되있었습니다.

최종적인 목표는 쿼드콥터를 ARM 리눅스 보드로 제어하는 것 이었기 때문에 제가 선택할 수 있는 방법은 두가지가 있었습니다.

첫째는 DJI F450 컨트롤러인 NAJA-M를 제거한 뒤 모터를 수동으로 제어하는 법

두번째는 DJI F450의 컨트롤러 NAJA-M를 사용하면서 조종기가 발생시키는 신호를 시뮬레이션 해서 제어하는 법

이었습니다.

많은 분들은 첫번째 방법으로 많이 하시는것 같습니다만 저는 프로젝트 기간이 그리 길지 않았고 하나의 소프트웨어로 다양한 기종에 적용하고 싶었기 때문에 두번째 방법을 사용하였습니다.

여기서 다양한 기종에 적용을 할 수 있다는 것은 하나의 조종기를 쿼드콥터와 비행기에 장착시켜도 작동하는 것과 같이 여러 기체에서 공용으로 사용되는 연결방식(인터페이스)를 지원하게 되면 다양한 기종에서도 사용할 수 있다는 뜻 입니다.

따라서 저는 기존 조종기가 발생시키는 신호를 분석해서 조종을 하는 가장 기본적인 것 부터 시작하였습니다.

일단은 분석하기에 앞서 구글에 검색을 해서 일을 조금 덜어보려 했지만 아무리 검색해도 신호분석에 대한 글은 없더군요..

제가 유별난건지 아니면 사람들이 관심이 없었던건지 모르겠습니다.

그래서 오실로스코프로 조종기 수신기에서 나오는 신호를 분석하기 시작하였습니다.


확인해보니 1.5ms 동안 신호를 보내고 있네요!

한칸이 500us 입니다 (1000us = 1ms)

그럼 조종기를 한번 움직여볼까요 

확인해보니 최소 1.0ms 최대 2.0ms 를 보내고 있는것을 확인하였습니다.

1.0ms 일 경우 최소 2.0ms 일 경우 최대인것이죠

1.0ms 일 경우엔 스로틀이 최저 2.0ms 일 경우 스로틀이 최대가 되는것 입니다.

주기는 20ms

즉 20ms 마다 1.0ms ~ 2.0ms 의 신호를 보내어 쿼드콥터를 조종을 하는것 입니다.

20ms 마다 반복되는 모습 보이시죠?

한 칸이 5ms입니다.

조금 더 테스트를 해보았는데 그 결과는 이렇게 나왔습니다.

1.0 ms   <---------->   2.0ms

오른쪽                       왼쪽

기수상승                기수하강

스로틀최저            스로틀최대

일반적으로는 1.0 이 왼쪽 2.0이 오른쪽 일 것 같은데 반대네요 이 부분은 주의하셔야 할 것 같습니다.

AILE, ELEV, THRO, RUDD 는 위를 참고하시면 됩니다.

AUX1 의 경우는 비행모드 설정에 대한 신호입니다.

모드는 총 3가지가 있습니다.

ms신호는 F450 시스템 셋팅에 따라 다를수 있으니 참조 하시어 수정하시기 바랍니다.

GPS 모드 – 1.86ms – 자동으로 고도,위치 고정 / 자동으로 수평유지

Attitude 모드 – 1.52ms – 자동으로 고도 고정 / 자동으로 수평유지

Manual 모드 – 1.20ms – 수동모드 / 수평유지 안함


AUX2 의 경우는 기체의 기수방향 모드에 대한 신호입니다.

모드는 총 2가지가 있습니다.

일반모드 – 1.86ms – 기체 앞 부분이 향하는 곳이 앞쪽 방향으로 인식

고정모드 – 1.20ms – 북쪽방향이 기체의 앞쪽 방향으로 인식 (라디오 조종기를 사용할때 어느부분이 기체의 앞쪽방향인지 모를때 유용한 기능인것 같습니다)

저는 일반모드로 해놓고 계속 사용하였고 아두이노 코드도 그렇게 되어있습니다.

EXT1 은 사용하지 않습니다.

그럼 이제 분석한 결과를 토대로 ARM 보드로 쿼드콥터를 제어해보려 합니다.

제가 사용한 ARM보드는 ODROID-X2 모델입니다.

쿼드코어라서 성능에 걱정은 없었습니다.

그럼 이 보드의 GPIO포트(신호 출력을 위한 포트)와 NAJA 컨트롤러를 연결해보도록 하겠습니다.


그리고 GPIO PIN MAP에 따라 리눅스 디바이스 드라이버를 만들어서 시도를 했습니다만..

대실패를 하고 말았습니다.

제가 사용한 OS가 Ubuntu 12.04 였는데 일반적인 리눅스에서는 시스템 커널이 최우선이고 커널단에서 여러가지 프로그램들이 돌아가고 있기 때문에 Clock 신호(위의 조종신호와 같은 주기적인 신호)와 같은 정밀한 신호는 출력하기가 힘들다고 합니다.

만약 리눅스를 사용하면서 Clock를 출력하고 싶다면 리눅스를 커널단 부터 수정해서 직접 빌드한 후 사용하거나 RTOS를 사용해야 하는데 커널단부터 수정하기에는 시간도 너무 오래걸리고 안정성을 보장을 할 수가 없어서 불가능하고 RTOS를 사용하게 되면 Ubuntu 의 장점인 여러가지 어플리케이션들을 사용할 수 없게 되어 이것도 불가능했습니다.

그래서 한참을 고민하다 결국 처음부터 생각하고 있었던 차선책인 아두이노를 Clock신호를 발생시키는 용도로 사용하기로 하였습니다.

이 부분에 있어서 만약 호스트 머신인 ARM보드가 시스템 에러로 죽어도 재부팅 되는 동안 아두이노가 계속 쿼드콥터를 조종해 줄 수 있기 때문에 더 안전한 구조가 되었습니다.

한정적인 배터리 자원에서 전력을 사용하는 기기가 더 늘어 비행시간은 줄어들겠지만 여러가지 요인을 따져보면 이 방법이 가장 최선이었던것 같습니다.

이렇게 아두이노와 NAJA-M 컨트롤러를 연결해 주었습니다.


이렇게되면 NAJA-M 컨트롤러는 조종기가 연결된 것으로 인식하게 됩니다.

위에 적었던 신호 분석결과에 맞추어 아두이노를 프로그래밍 한 뒤 실행시키자 F450 LED가 노란색(조종기 탐색중)에서 초록색(조종기 찾음)으로 바뀌었습니다.

잘 인식되는것을 확인하고 아두이노를 종료시키니 F450 LED가 빨간불로 바뀌며 조종기 연결이 끊어졌다는걸 표시합니다.

일단 임시로 이렇게 올려두었는데 실제 비행때는 잘 고정시켜야 할 듯 합니다.

그리고 이제 실제 조종을 위해 아두이노 코드를 실제 사용을 위해 ARM보드에서 보내오는 조종신호를 NAJA-M 컨트롤러로 보내는 코드로 프로그래밍 한 뒤 ARM 보드에 연결하고 Ubuntu 위에 간단한 Python 서버와 PC에서 C#으로 클라이언트를 만들었습니다.

위의 사진은 PC에서 조이스틱을 통해 쿼드콥터를 조종하게 해 줄 클라이언트 입니다.

이 사진은 프로젝트가 많이 진행되었을 때의 사진인데 이해를 위해 가져왔습니다.

ARM보드에 카메라를 달아 실시간 영상도 볼 수 있습니다.


오른쪽에 숫자가 많은것이 ARM보드에 올라가있는 Python 서버로 가운데 PC 클라이언트에서 보내는 조종신호를 받아 아두이노로 전송하여 최종적으로 쿼드콥터를 조종하게 됩니다.

내부망에서 테스트를 해서 딜레이는 거의 없었고 아주 매끄럽게 잘 조종되었습니다.

이제 이 코드를 응용해서 실제 비행을 위한 코드를 만들면 될 것 같습니다.

– 첨부

DJI NAZA-M 컨트롤러 제어에 사용된 아두이노 코드입니다.

https://github.com/taylor224/DJI-Naja-M-Remote-Controller/blob/master/arduino.c

아두이노에 해당 코드를 컴파일하여 넣어주신후

아두이노 디지털 핀 2, 3, 4, 5, 6, 7, 8 번 포트에 각각 DJI NAZA-M 의 

AILE, ELEV, THRO, RUDD, MODE, AUX1, AUX2 와 연결하여 주시면 됩니다.

그리고 NAZA-M 컨트롤러의 접지( – 단자) 를 아두이노의 GND(Ground) 에 연결하여 주셔야 합니다.

NAZA-M 의 AILE 등 조종기를 연결하는 부분의 핀이 2개씩 있는데 그 중 어떤핀이 접지이고 어떤핀이 신호를 넣는 핀인지 확인을 하셔야 합니다.

위처럼 하신 후 아두이노를 115200bps 시리얼로 연결하신 후 아래와 같은 신호를 보내주시면 됩니다.

>1000:1000:1000:1000:1/

> 는 시작을 뜻하고 /는 끝을 뜻합니다.

순서는 첫번째부터 AILERON, ELEVATOR, THROTTLE, RUDDER, MODE 입니다.

값은 0~1000 까지 있습니다.

MODE와 THROTTLE을 제외한 모든값은 위에서 말한 방식이 적용됩니다(왼쪽은 높은값, 오른쪽은 낮은값).

예시: 

왼쪽으로 최대 1000

오른쪽으로 최대 0

THROTTLE 의 경우 0이 최저 1000이 최대입니다.

MODE 는 1 (GPS) , 2 (Attitude) , 3 (Manual) 입니다.

멀티콥터 모터 시동은 일반 조종기처럼 신호를 보내주시면 됩니다.

BMW i3 i8 Sounds

BMW i3 i8 Alert Sound & Startup Sound

System Start Sound

Please record clearly and send these sound to me if you have i3 or i8.

BMW i3 i8 시스템 엔진 시동 사운드 소리

BMW i3 i8 경고음 알림음 알림 경고 소리

Download

System Start

Alert1

Alert2

Original Alert

Kinect For Windows V2 로 특정 좌표 깊이 가져오기

Kinect For Windows V2 로 특정 좌표의 Z값(깊이)를 가져오는 코드입니다.

이 코드를 통해 x, y 값으로 z 값을 가져오실수 있습니다.

X, Y 좌표는 ColorFrame 의 해상도인 1920×1080 기준입니다.

Z 값은 Float 으로 리턴되며 키넥트 센서로부터의 미터 거리를 뜻합니다.

먼저 이 코드를 사용하시려면 Kienct for Windows V2 센서와 Kinect SDK 2.0, Visual Studio 2013 이 필요합니다.

이 코드는 WPF 프로그램의 소스 일부이며 사용하실때 자신의 코드에 맞게 수정하시어 사용하시기 바랍니다.

MapColorFrameToCameraSpace() 함수가 아래처럼 매번 프레임이 들어올때마다 실행될 경우 프로그램이 많이 느려지는 영향이 있으므로 주의하시기 바랍니다.

더 자세한 설명은 이 링크에서 영문으로 확인하실수 있습니다.

https://gist.github.com/taylor224/1a534cb9287a4205c91f

public partial class MainWindow : Window
{
private KinectSensor kinectSensor = null;
private CoordinateMapper coordinateMapper = null;
private MultiSourceFrameReader multiFrameSourceReader = null;
public MainWindow()
{
InitializeComponent();
// one sensor is currently supported
this.kinectSensor = KinectSensor.GetDefault();
// get the coordinate mapper
this.coordinateMapper = this.kinectSensor.CoordinateMapper;
// get the depth (display) extents
FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
FrameDescription frameDescription = this.kinectSensor.DepthFrameSource.FrameDescription;
// open multiframereader for depth, color, and bodyindex frames
this.multiFrameSourceReader = this.kinectSensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Color | FrameSourceTypes.Body);
this.multiFrameSourceReader.MultiSourceFrameArrived += this.Reader_MultiSourceFrameArrived;
}
private void Reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();
if (multiSourceFrame != null)
{
using (DepthFrame depthFrame = multiSourceFrame.DepthFrameReference.AcquireFrame())
{
if (depthFrame != null)
{
// Specified X, Y coordinate
// In 1920 x 1080 color frame
double x = 1000;
double y = 900;
FrameDescription depthFrameDescription = depthFrame.FrameDescription;
depthWidth = depthFrameDescription.Width;
depthHeight = depthFrameDescription.Height;
depthframeData = new ushort[depthWidth * depthHeight];
depthFrame.CopyFrameDataToArray(depthframeData);
CameraSpacePoint[] csp = new CameraSpacePoint[1920 * 1080];
this.coordinateMapper.MapColorFrameToCameraSpace(depthframeData, csp);
// Depth(Z Position) of specified coordinate
float DepthPosition = csp[(1920 * Convert.ToInt16(y)) + Convert.ToInt16(x)].Z;
}
}
}
}
}

TouchInjection C# API 사용중 문제 해결방법

윈도우 8에서 터치 이벤트를 만들기 위해 Touch Injection API 를 사용하던 도중 발생한 문제에 대한 해결 방법입니다.

Touch Injection 은 Windows 8 부터 지원되는 Windows API 이며 User32.dll 에서 Import 하실수 있습니다.

원래는 C++ 만 지원하는 API 이나 C# 에서도 지원이 가능하게 끔 만들어진 라이브러리가 있습니다.

https://code.msdn.microsoft.com/TouchInjection-on-Windows-885150d2#content

이 라이브러리를 사용하면 C# 에서도 터치 이벤트를 만들어낼수 있습니다.

아래는 제가 이 라이브러리를 사용하면서 발생했던 문제에 대한 해결 방법입니다.

문제사항 해결방법
InitializeTouchInjection() 에 Parameter를 넣으면 터치이벤트가 아예 동작하지 않음 InitializeTouchInjection() 에서 Parameter를 모두 제거해서 해결
양손 적용시 터치가 한군데에서만 연속적으로 클릭되던 문제 각 손이 각각 업데이트 되지만 업데이트는 2개 모두 하던 방식에서 각 손만 임시 contact를 만들어 각각 업데이트 하도록 해서 Injection 시키도록 하여 해결
터치인젝션으로 터치클릭 & 무브가 안되는 문제 터치클릭을 위해서 UPDATE / INRANGE / INCONTACT 옵션을 사용했는데 문제는 이 이벤트를 발생시키기 전에 DOWN / INRANGE / INCONTACT 이벤트를 발생시켜야 이 이벤트가 정상적으로 동작하여서 터치가 UP된 후 다시 터치클릭 & 무브 하려할때 해당 이벤트가 발생되지 않았으면 발생시키도록 수정
터치가 한쪽밖에 안됨 기존에 터치가 한군데에서만 연속적으로 클릭되던 문제의 원인이 LeftHand 신호를 받지 않았기 때문에 발생했던 문제였기 때문에 임시로 해결했던 contacttemp 를 만들어 각각 업데이트 하도록 Injection 시키던걸 양손 모두 한꺼번에 업데이트 하도록 변경하여서 수정
두 손 터치가 제대로 되지 않는 문제 스택오버플로우에 따르면 TOUCH_FEEDBACK_DEFAULT 는 윈도우의 Pen and Touch 설정에 영향을 받기 때문에 여러가지 문제가 생길수도 있어서 TOUCH_FEEDBACK_INDIRECT 를 통해 InitializeTouchInjection()을 초기화해서 해결 http://stackoverflow.com/questions/20875283/why-is-only-one-touch-being-injected-when-using-touch-injection-api-with-win8
두 손 터치가 인식은 되지만 터치클릭이 되지 않는 문제 Touch Down and Move 하기 전 이전 포인트 플래그가 터치가 올라갔을때 이면 Touch Down 플래그를 등록하는데 등록한 후 한번 Move 를 진행하고 그 다음번에 Touch Down and Move 를 위한 플래그로 변경시키는 방식에서 애초에 이전 플래그가 터치가 올라갔을때 이면 Touch Down 플래그를 등록한 후 바로 TouchInjection()을 호출하여 처리한 후 Touch Down and Move 를 위한 플래그를 넣고 Move 하고 Injection 하도록 하여서 해결

[파이썬] 우체국택배 배송정보 가져오는 코드

파이썬으로 만든 우체국택배 택배 배송정보를 가져오는 코드입니다.

택배 송장번호를 입력하시면 배송정보를 가져옵니다.

아래 주소에서 더 예쁘게 보실수 있습니다.

https://gist.github.com/taylor224/704e46a627519d035eb9

# -*- coding: utf8 -*-

 
import urllib, httplib, BeautifulSoup
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
 
realurl = 'http://service.epost.go.kr/trace.RetrieveRegiPrclDeliv.postal?sid1='
posturl = 'trace.epost.go.kr'
 
 
def getpost(sid):
params = {
'target_command' : 'kpl.tts.tt.epost.cmd.RetrieveOrderConvEpostPoCMD',
'sid1' : sid,
'RE_ORDID' : 'null'
}
params = urllib.urlencode(params)
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
conn = httplib.HTTPConnection(posturl)
conn.request("POST", "/xtts/servlet/kpl.tts.common.svl.SttSVL", params, headers)
response = conn.getresponse()
data = response.read()
conn.close()
 
try:
soup = BeautifulSoup.BeautifulSoup(data)
addressee = soup('table', {'class' : 'traceTable traceTableType02', 'summary' : '발송인/수취인 정보'})[0].tbody.findAll('tr')[1].td.string
statuslist = soup('table', {'class' : 'traceTable traceTableType02', 'summary' : '세부결과를 보여드립니다.'})[0].tbody.findAll('tr')
status = []
 
status.append({ 'type' : 'addressee',
'addressee' : addressee
})
 
for i in range(len(statuslist)):
if i == 1:
continue
else:
tdlist = statuslist[i].findAll('td')
if statuslist[i].get('id') == 'transport1' or statuslist[i].get('id') == 'transport2':
continue
# When Received
if len(tdlist) == 1:
statusreceiver = tdlist[0].string
status.append({ 'type' : 'recipient',
'recipient' : statusreceiver.strip()
})
else:
statusdate = tdlist[0].string
statustime = tdlist[1].string
statuslocation = tdlist[2].a.string
nowstatus = tdlist[3].string
statusdetail = tdlist[4].string
status.append({ 'type' : 'status',
'date' : statusdate,
'time' : statustime,
'location' : statuslocation,
'status' : nowstatus,
'detail' : statusdetail
})
 
for statusdata in status:
for key in statusdata.keys():
if statusdata[key]:
statusdata[key] = statusdata[key].strip()
 
return status
except IndexError:
return False
 
 
data = getpost('postnumber')
 
if not data:
print 'Invalid Number'
else:
for status in data:
if status['type'] is 'addressee':
print status['addressee']
if status['type'] is 'recipient':
print status['recipient']
if status['type'] is 'status':
print status['date']
print status['time']
print status['location']
print status['status']
print status['detail']


Asterisk 설정

Asterisk / Elastix 설정 방법

Asterisk 11 버전

Elastix 3.0 버전

Freepbx 13 버전

기준으로 작성되었습니다

Elastix 3.0 버전 쓰지마세요.. 쓰다가 집어던지고 FreePBX로 넘어왔습니다…

아래 설정은 기본설정 (Organization, Extension, User 등이 셋팅되어 있어야 합니다)

언급하지 않은 셋팅은 기본값으로 두시면 됩니다.

————————————————-

+ SIP 설정

– Elastix :

PBX – Trunks 에서 [SIP] Create New Trunk 선택 (체크박스를 SIP로 설정)

Descriptive Name 은 알기쉽게 설정 (저는 Main)

Outbound Caller ID 에 SIP 계정의 전화번호 입력

Organization 선택

Peer Settings 탭으로 들어가서

Name 은 알기쉽게 암거나 (저는 Main)

Type = peer

secret = SIP 계정 비밀번호

username = SIP 전화번호

context = from-trunk

disallow = all

allow = ulaw;alaw;gsm

qualify = yes

nat = auto

dtmfmode = rfc2833

insecure = port,invite

(Advanced Settings 를 누름)

fromuser = SIP 전화번호

defaultuser = SIP 전화번호

fromdomain = SIP 서버주소

outboundproxy = SIP 프록시 주소(없으면 입력하지 않아도 됨)

User Settings 탭으로 들어가서

Peer Settings 이랑 똑같이 하되

type = user

로 변경

Registration 탭으로 들어가서

Register String 에 

SIP전화번호:SIP계정비밀번호@SIP서버주소

라고 입력한다

그리고 저장

만약 incoming 전화가 에러가 난다면 

SIP Setting 에서 Local network 가 셋팅되어 있는지 확인해보자

아래는 FreePBX 용 셋팅

CID Option 을 Force Trunk CID 로 셋팅

(이렇게 안하면 송신자가 Call Fail 뜸)

SIP Trunk

PEER Trunk

host=서버주소

username=번호

secret=비번

type=friend

disallow=all

qualify=yes

insecure=port,invite

context=from-trunk

fromuser=번호

defaultuser=번호

authuser=번호

allow=ulaw

위 대로 해서 안된다면 이렇게 해보자 (한국쪽 SIP Provider)

host=서버주소

username=070번호

secret=비밀번호

type=friend

disallow=all

qualify=yes

insecure=port,invite

context=from-trunk

fromdomain=서버주소

outboundproxy=서버주소

callbackextension=070번호

defaultuser=070번호

fromuser=070번호

allow=ulaw

————————————————-

————————————————-

전화 자동으로 연결되게 설정하기

PBX – Inbound Routes 에서 

ADD Incomming Route 클릭

Description 에 설명입력

Set Destination 에 연결할 곳을 선택하면 끝

그리고 저장

————————————————-

————————————————-

+ SIP 설정 후 Inbound 전화가 연결음만 나고 연결이 안될때

– Asterisk :

/etc/asterisk/sip.conf 에 아래 옵션을 추가한다

externaddr=외부아이피

localnet=내부아이피

– Elastix :

PBX Admin Settings – General Settings Admin – SIP Settings 에서

Nat Support 에서

Type of NAT 을 static 으로 변경

Local Network 를 172.30.1.0 / 255.255.255.0 으로 설정

Extern Addres 에 외부아이피 입력

그리고 저장

그리고

/etc/asterisk/sip_general_custom.conf 에 

bindaddr=0.0.0.0

추가 후 저장

+ Inbound 전화가 Call Fail (비프음이 나고 끊어질 때) 뜰 때 

SIP 설정이 위와 같이 제대로 설정되어 있는지 확인

추가로 다른 설정이 기본값을 벗어났는지 확인

확인된 에러:

– Outbound Caller ID 가 설정되어 있으면 에러남

– Queue 로 전화를 연결한 후 끊으면 그 다음 전화가 Fail 날 때 Queue 의 Destination 을 지정해준다 (예: Terminate Call)

– IVR -> Queue 으로 라우팅 된 후 Queue 에서 대기하고 있을때 Queue 설정이 변경되고 Asterisk 가 reload 되면 일정확률에서 송신자 Call Fail 이 뜬다. Asterisk 를 재시작하거나 Trunk 에서 CID Option 을 다른걸로 변경하고 Apply Config 한 후 다시 원상복귀 하면 정상작동 한다 (버그인듯 하다)

멍청한 짓이였다…

No route to peer for ‘Caller ID’ from ‘IP Address’

라는 에러메세지가 떴는데 이게 요청을 보낸 아이피주소와 요청이 오는 아이피주소가 달라서 그랬던거다.. host에 아이피주소 대신 도메인 주소를 썼기때문..

그래서 SIP Trunk 의 host 를 모두 IP 주소로 변경했더니 잘 동작한다

————————————————-

SIP Trunk 에서 컬러링(Ring back tone)이 들리지 않고 그냥 기본 통화연결음만 들릴때:

이건 기본적으로 Asterisk 가 통화가 연결될 때 까지 통화연결음을 만들어 내기 때문이다.

외국에는 컬러링 같은 개념이 없나보다..

SIP.conf 에 아래 두개를 추가해주면 된다

(Freepbx – Setttings – Asterisk SIP Settings – 오른쪽 탭 Chan SIP – 맨밑에 Other SIP Setting)

progressinband=yes

prematuremedia=no (확인해보니 이건 안넣어도 되네요)

————————————————-

+ Asterisk SIP Provider 인증상태 확인

asterisk -rx “sip show registry”

+ Asterisk Log 실시간 확인

tail -f /var/log/asterisk/full 혹은 tail -f /var/log/asterisk/messages

————————————————-

Cisco CP-9971 과 CP-9951을 Asterisk 에 연결하기

http://linuxmanr4.com/2011/09/05/telefono-cisco-9951-y-elastix/

FreePBX 12 기준 : 

아래 모든 사항은 유저셋팅, Extension 셋팅이 끝나고 나서 해야한다.

Extension 은 CHAN_SIP로 만듭니다.

아래 쪽에 있는 TFTP 설치방법을 참고하여 TFTP 설치

69번 포트 오픈

/tftpboot 폴더에 필요한 파일들을 넣는다

SEP{기기고유ID}.xml

dialplan.xml

Korean_Korea_Republic (언어파일, CME 관련된 페이지에서 받을수 있는듯 하다, 없어도 됨)

RINGLIST.DAT (벨소리리스트, 없어도 됨)

Settings – SIP Setting 에서 외부아이피 다 설정하고

NAT 은 공유기 환경이면 yes 아니면 no

그리고 오른쪽 탭의 SIP Setting 에서 맨 아래 

Other SIP Settings 에 아래 두개를 추가한다

callcounter = yes

tcpenable = yes

Extension 페이지 오른쪽 탭에서 시스코 전화기를 접속시킬 Extension 을 선택하고 

NAT MODE 를 Automatic Force Both로 선택

Port 를 5061 (CHAN_SIP 포트)로

Transport 를 All – TCP Primary 로 선택하고 저장합니다.

(추가 업데이트 예정)

————————————————-

————————————————-

TFTP 서버 설치하기

sudo apt-get install xinetd tftpd tftp

vim /etc/xinetd.d/tftp 에 아래 내용 추가

service tftp

{

protocol        = udp

port            = 69

socket_type     = dgram

wait            = yes

user            = nobody

server          = /usr/sbin/in.tftpd

server_args     = /tftpboot

disable         = no

}

sudo mkdir /tftpboot

sudo chmod -R 777 /tftpboot

sudo chown -R nobody /tftpboot

sudo /etc/init.d/xinetd restart

————————————————-

CUCM 구매/설치하기

시스코 시스템 기반으로 인터넷 전화를 구성하려고 보니 라이센스 비용만 기본 100만원이 넘어가게 생겼네요..

시스코 VoIP 시스템은 인터넷에 자료도 워낙 적어서 찾느라 애를 먹었는데 일주일 정도 검색해서 정보를 모아서 결국 제일 괜찮은 구성을 찾았습니다

기본적으로 엔터프라이즈급 회사에서는 아래처럼 구성합니다

IP Phone -> L2 -> Cisco IOS Router -> CUCM -> CUBE -> SIP Provider -> Receiver

이렇게 하면 필요한 시스코 장비가 2대이고 라이센스도 여러개가 필요해서 굉장히 비쌉니다..

그래서 대체로 찾은 구성이 이것

IP Phone -> L2 -> Normal Router -> CUCM –(SIP Trunk)–> Asterisk -> SIP Provider -> Receiver

이렇게 하면 필요한 시스코 장비는 0대이고(CUCM 가상서버로 설치), 필요한 라이센스는 1 + a(필요한 기능) 입니다.

Cisco UCM(CUCM) 에서 내가 IP Phone CP-9971(Voice&Video) Enhanced 라이센스 부터 지원해서 최소 라이센스 비용이 1유저당 $210입니다.

CUCM 보통 볼륨라이센스(대량라이센스) 라이센싱을 해서 낮은 수의 유저로 계약할땐 특별할인을 받지 못해 거의 1 유저당 20만원 정도의 가격이 매겨집니다.. (나빠요 ㅠㅠ)

CUCM 설치에 대해 얘기해보자면 CUCM 가상서버에도 올라갈수 있는 설치형 시스템입니다

다만 설치할수 있는 가상서버 환경은 VMware 기반으로 제한적입니다.

최소 2기가가 필요한 제한조건만 갖추면 설치/구동이 가능합니다(성능이 낮으면 기능에 제약이 걸림). 

하지만 VMware 라이센스도 별도로 구매하려면 돈이 들고 물리적 서버를 갖추자니 관리가 힘드므로 Hyper-V 설치하는 편법을 쓰기로 했습니다

CUCM 설치시에는 하드웨어 체크를 꼼꼼히 하기때문에 회피가 불가능 하고 일단 VMware 설치를 하드디스크 파일을 Hyper-V용으로 변경 그리고 부팅하드웨어 체크 스크립트를 수정하여 하드웨어 체크를 회피합니다

이러면 Hyper-V 기반에서도 CUCM 사용이 가능합니다. 다만 정상작동 보장이 안될뿐이죠

제가 이렇게 까지 CUCM 설치하려는 이유의 첫째는 Cisco IP Phone 사용하고 싶어서 이고(조금 멋져보임) 둘째는 Auto Attendant(ARS) 사용하고 싶어서 입니다

사실 Auto Attendant 다른 VoIP 시스템에서도 가능하지만 그냥 시스코것을 써보고싶었습니다 ㅎㅎ

-> 2014.12.4 = 오늘 CUCM 구매를 하려고 기존에 알아봤던 곳에 전화를 했는데 1유저 라이센스는 너무 작아서 판매가 어렵하고 하네요ㅠㅠ.. 그래서 Cisco 쪽에 전화했더니 시스코 총판인 A회사라 곳을 알려주는데 이곳에서 B회사라는 곳을 연결해주었습니다.. (302 Redirect 몇번째인지 ㅠㅠ) 지금 B회사 쪽이랑 얘기중인데 아무래도 여기서는 CUCM 구매할수 있을것 같은 느낌이 듭니다! 개인 + 소량 구매유저에게 Enterprise 제품을 정말 안팔긴 하나봅니다…

-> 2014.12.5 = B회사에서 오늘 견적을 받았습니다만.. 이해가 안가는 부분이 있어서 다시 전화를 드렸습니다. 시스코 CUCM 라이센싱은 포트(?) 때문에 한 유저당 6유저 라이센스가 필요하다고 하네요.. 이 부분이 이해가 안가서 다시 여쭈어보고 연락을 기다리는 상황입니다..

제가 다른회사에서 전해들은 금액은 대충 20~30만원 선에서 구매하는 정도였는데 견적을 받은 대로라면 거의 40만원이 넘어가는 금액이라 예산을 초과하게 됩니다.. 근데 정말 이번 거래를 하면서 느낀점이 큰회사가 아닌 작은회사들은 시스코같은 큰 회사 제품을 구매하기 조차 힘들다는 것 이었습니다… 돈주고 구매하겠다는데도 안팔려고 하는곳도 많고 홀대하는 느낌을 많이 받네요… 많이 실망입니다..

2014.12.5 15:55 = 솔직히 맘같아선 CUCM 말고 그냥 Asterisk 로 구축하고 싶습니다.. 이번 거래 끝나면 Cisco 본사에 컴플레인 메일을 보낼 예정입니다. 기분이 나쁘군요

Cisco CP-9971 구매

Cisco VoIP 시스템을 구현하고 사용하기 위해서 Cisco CP-9971 인터넷전화기를 구매했다

일단은 CUCM이라는 VoIP 매니저 서버를 구축한 뒤 그 서버에 이 전화기를 연결하면 된다고 하는데 아직 해보지는 않아서 잘 될지 모르겠다..
CUCM 10.5 Enhanced 라이센스가 $180 정도 하는것 같은데 이것도 구매하려 하는데 시스코 한국 파트너사들이 개인고객에게 친절하지 않아서 애를 먹고있다.. 차라리 외국 파트너에게 구매하는게 나을 정도..

CP-9971은 비디오와 음성을 지원하고 터치스크린인게 특징

방사능 측정 시스템 만들기

방사능 측정기를 샀는데 쓰지도 않고 구석에만 박혀있길래 이걸 응용해서 방사능 측정/데이터 쌓는 시스템을 만들어서 서비스로 공개해보았습니다

일단 주소는 http://ems.cloudsquare.kr 이고 들어가시면 방사능 측정 데이터를 보실 수 있습니다.

사실 제 기억속엔 측정기 만들면서 사진을 여러장 찍어놓은것 같은데 찾아보니 사진이 이것밖에 없네요

아래 사진이 제가 사용한 방사능 측정(가이거 계수기) 기판입니다.

전원을 연결하면 방사선이 저 밑에있는 가이거 튜브를 땅 하고 때릴때 마다 1이라는 신호를 전송해줍니다.

방사선이 튜브를 땅 하고 많이 때리면 방사능 수치가 높은거고 적게 때리면 수치가 낮은거지요.

이게 가이거 튜브라고 방사선한테 허구언날 퍼 맞는 앱니다.

무려 400V가 넘어가는 전압을 사용해서 만질때 조심해야 합니다.

전류가 많이 낮아서 괜찮지만 전류가 심장을 흐를경우 좋지는 않다고 하네요(안만져봐서 몰라요).

이 튜브를 위 기판의 아래쪽 네모칸에 넣어서 사용합니다.

그리고 아래가 제가 만든 최종 가이거카운터 입니다.

중간 과정에 너무 집중하느라 싹 빼먹고 다 만든 사진만 있네요.

아두이노에 가이거 카운터 데이터 케이블을 연결해서 CPM이라는 값을 모아 nSv/h 로 변환합니다(공식사용).

조금 없어보이긴 한데 가이거 튜브에서 열도 많이나고 위험하기도 해서 대충 케이스를 만들었습니다.

아두이노는 ARM보드에 연결되어서 10분간 데이터를 모아서 평균을 낸 후 서버로 전송합니다.

아래가 위 장비에서 나온 방사능 측정 값 입니다(mSv/h)

여기에 100을 곱한게 nSv/h 입니다.

보시다시피 변화의 폭이 너무 커서 바로 사용하기에는 무리가 있어서 10초 간격으로 나오는 데이터를 10분동안 모아서

평균을 내서 사용합니다.