[66호]AutoSwitch
2020 ICT 융합 프로젝트 공모전 장려상
AutoSwitch
글 | 명지대학교 한찬영
1. 심사평
칩센 작품의 외관 디자인 및 모델링이 매우 깔끔하게 된 것이 눈에 띕니다. 기능적으로 보아 하나의 스위치만 컨트롤할 수 있는 구조라서 다른 스위치 조작을 어떻게 할지에 대한 아쉬움이 있습니다. 스위치 컨트롤을 위한 서보 모터 구동하기 위해서 적지 않은 전류 소모가 있는 듯 한데, 이 부분에 대한 개선은 필요할 것으로 보입니다. 기어 등을 적용하는 것도 하나의 방안이 되지 않을까 합니다.
펌테크 독특한 하우징 구현방식과 구글어시스턴트를 사용한 음성명령 처리방식 등의 기술 구현방식이 인상적이었습니다. 아이디어와 창의성, 실용성이 모두 돋보이며 실생활과도 밀접한 작품이라고 판단되며 전체적인 기술 구현방식과 제품완성도 등에서 우수한 작품이라고 생각합니다.
위드로봇 유행하는 기존 IoT 스위치와 차별점을 강조하면 더 좋은 작품이 될 것 같습니다.
2. 작품 개요
누구나 쉽게 핸드폰으로 집안의 전등을 원격 제어 할 수 있는 IoT 기기이다.
서보 모터를 통해 물리적으로 스위치를 작동 시켜 불을 켜거나 끌 수 있다. 단지 집 안 스위치 위아래에 이 제품을 붙이기만 하면 설치를 끝낼 수 있다. 이는 기존 제품들과 다르게 전기공사를 할 필요가 없다는 점에서 편리하다.
Blynk web server service를 이용했기 때문에 어디에서도 제어할 수 있고, 매번 연동할 필요가 없습니다. 또한 Google assistant를 통해 음성제어도 할 수 있다.
NodeMCU 기반으로 제작되어 있기 때문에, WiFi 통신이 가능하다. 또한 오픈소스 하드웨어이기 때문에 다른 IoT 기기와의 확장이 가능하다.
외부전원 12V를 통해 서버 모터와 메인 보드에 전원을 공급한다. 이는 마찰이 강한 스위치에 대해서도 작동성을 확보하기 위함이다. 마찰이 적은 스위치의 경우 5V Micro usb를 통해서도 전원 공급이 가능하다. 제품은 3D 프린팅을 통해 제작가능하며, 나사 한 개로 고정할 수 있게 설계되었다.
3. 작품 설명
3.1. 주요 동작 및 특징
3.1.1. 스위치 작동 부분
그림 1 사진의 경우처럼 스틱이 서보 모터에 의해 움직이며, 일정 각도 이상에서 스위치와 접촉하게끔 설계되어 있다. 접촉각을 넘어서는 경우부터 스위치를 물리적으로 작동시킬 수 있다.
이 제품은 가정집에서 사용되는 스위치 커버를 이용했다. 단순한 접착방식은 서보 모터의 반발력을 충분하게 지탱하지 못한다. 커버의 고정장치 부분을 모델링하여 기존 스위치 커버 대신 고정될 수 있도록 개선되었다. 이런 방식은 추가적인 접착 및 고정을 고려하지 않고 어렵지 않게 고정되게끔 한다.
NodeMCU와 브레드보드가 탑재된 지지대가 고정될 수 있는 부분으로, 슬라이드 방식으로 탑재가 가능하도록 고려되었다. 이는 보드를 쉽게 교체할 수 있도록 고안된 것이며 제품이 데이터를 공유하고 저장하는 등 다른 IoT와의 연결성 및 업그레이드를 고려한 것이다. 소비자는 이 모듈을 단순하게 교체하면 업그레이드를 받을 수 있다.
3.1.2. NodeMCU와 Blynk의 통신
Blynk는 위 사진처럼 아두이노와 같은 오픈소스 라이브러리로서 이를 이용하면 IoT 프로젝트를 손쉽게 제작할 수 있다. 위 사진은 현재 프로젝트에서 사용된 어플의 위젯 레이아웃이다. 버튼 몇 개와 슬라이더로 구성되어 있으며, 버튼의 작동을 인지하면 신호가 아두이노로 전송된다. 이를 통해 서보를 작동시키는 트리거가 된다.
Blynk는 자체 웹서버를 구축해 놓아서 사용자와 개발자는 웹서버 개발에 대해 비용을 투입할 필요가 없다. LTE를 통한 제어도 가능하다는 이점이 있다.
3.1.3. GoogleAssistance
구글에서 제공하는 음성인식 서비스를 IFTTT의 webhook와 연결하여 사용할 수 있다. 위 사진과 같은 형식으로 작동한다.
스마트폰이나 구글 홈 미니와 같은 제품에서 ‘Turn on the light’와 ‘Turn off the light’의 제시어를 통해 Virtual pin 2에 신호를 주고 이를 통해 NodeMCU 내의 Flag를 변경토록 하였다. 서보 모터를 제어하도록 설정되어 있다. 설정한 대로 작동되면 답어 ‘Yes, master’와 ‘Good night, master’를 외치게 되어 작동 여부를 확인할 수 있다. 위의 사진처럼 Blynk의 웹서버 설정을 이용할 수 있다.
3.2. 전체 시스템 구성(작동상세)
3.2.1. 스위치 작동 부분
스위치의 작동을 위해서는 저항과 마찰을 극복할 수 있는 수준의힘이 필요한데 13g 이상의 서보 모터와 12v 어댑터를 이용한 외부전원이 필요하다.
서보 모터는 5V로도 작동하지만 아두이노에서는 충분한 전류를 제공하지 못해 위 사진과 같은 외부전원 컨버터를 이용했다.
서보 모터가 신호를 받아 작동하면 목표 각도에 대해 이동하게 된다. 이 과정에서 스위치가 방해하면 이를 극복하면서 스위치를 작동하도록 설계된 것이다. 하지만 각도가 특정 값을 넘어서게 되면 물리적 한계에 봉착하게 되고 이는 보드의 전원 차단으로 이어지게 된다.
이를 고려하여 어플에서는 각 이동의 최대값을 설정할 수 있게끔 되어 있다.
이는 여러가지 제품과 집 상황에 따라 달라질 수 있는 값이므로 사용자가 커스텀할 수 있게끔 고려되었다.
3.2.2. NodeMCU와 Blynk의 통신
Blynk의 웹서버는 개발자버전과 배포버전으로 나누어지는데, 이중 개발자 버전으로 개발되었다. 이를 구분하는 요소로 특정한 코드를 사용하는데, 이를 통해 기기를 구분한다.
Blynk는 아두이노의 GPIO를 직접적으로 제어할 수 있지만, Blynk만의 Virtual pin 제어를 추가적으로 제공한다. 이는 일종의 가상 GPIO로서 사용하는데 큰 제약이 없다는 것이 특징이다. 위 사진에서의 코드는 Virtual pin의 실사용 예이다. Blynk서버로부터 신호가 수신되면 interrupt가 작동되어 해당 함수가 작동되는 방식이다.
위 경우 수신되는 신호의 정보를 Flag로 업데이트 하는 방식이고, 이는 직후 아두이노의 Loop 반복문에서 서보 작동을 위해 사용된다. 또한 이 Virtual pin은 Google assistance에서의 신호수신에도 사용된다.
3.2.3. Code
스위치 타격을 위한 서보 작동 코드이다. Loop 문에서 Flag를 지속적으로 확인하며, Blynk의 Virtual pin이 Flag를 변동시켜 작동조건에 해당하게 만들 경우 서보가 작동된다. Turn on/off를 Flag의 True/False로 판단한다.
이 때 서보의 작동각도도 Blynk의 앱에서 수치를 사용한다. 첫 초기화된 각도는 작동범위보다 작게 설정되어 있다. 이는 큰 각에 대해서는 물리적 한계에 직면했을 때 작동오류를 범할 수 있기 때문이다. 이는 사용자가 적정 범위를 설정해야 함을 의미한다.
3.3. 개발 환경(개발 언어, Tool, 사용 시스템 등)
3.3.1. Model
Type : 자체제작 및 디자인
· MainBody Switch Cover type
· Motor Arm
· Controler Module
Design tool : SolideWorks 2018
Material : PLA 15%
Manufature : 3D printer
3.3.2. 구동부
motor : 13g servo motor
3.3.3. MicroProcessor
Chipset : ch340g
Board : NodeMCU
IDE : 아두이노 IDE
언어 : C++
`이름 : Blynk
스위치 및 위젯의 프리셋과 아두이노 라이브러리를 제공하여 쉬운 어플 제작이 가능하며, 웹서버 서비스를 LTE 통신이 가능하게끔 한다.
3.3.5. 음성인식 및 webhook
이름 : Google Assistance
스마트폰 및 AI 스피커를 이용해 육성으로 집안의 전등을 제어할 수 있도록 해준다. 육성으로 내린 명령은 IFTTT의 Webhook을 통해 Blynk 웹서버로 신호를 전송한다.
Blynk는 어플과 더불어 NodeMCU에게 신호를 전송하여 서보를 동작하게끔 한다.
4. 단계별 제작 과정
4.1. Model 및 제작과정
4.1.1. Modeling
해당 제품의 특징인 쉬운설치와 관리를 위해서 구조가 간단할 필요가 있었으며, 젊은 사람을 타겟으로 잡은 만큼 디자인도 고려되어야 했다.
처음 형태는 본드 및 테이프 접합으로 접촉면이 넓게 고려되어 디자인 되었으나, 경우에 따라 스위치의 마찰 및 저항이 클 경우 이로 인한 문제가 발생했다.
더 크고 힘이 쎈 모터의 반발이 본드 및 테이프 접합으로는 충분하지 못했었다. 이에 추가로 고려된 것이 훅 형태로 고정된 스위치 커버의 형태를 본 뜨는 것이다. 이는 테이프 접합이 필요 없이 강력하게 고정할 수 있다.
Modeling 과정 및 Printing 과정에서 가장 어려웠던 부분이 바로 이 훅 부분이다. 조금의 오차를 넘어서게 되면 고정이 어려워 진다. 정밀한 측정 및 모델링이 요구되었다.
모델은 크게 3파트로 구성되었다. MainBody를 제외한 나머지 두 파트의 경우 모델러들의 추가 개조 자유성이 보장된다. Body 후면의 홀더의 경우 사이즈만 맞다면 다양한 형태의 모듈이 장착될 수 있다. 현재는 그리스 신전의 모습을 본 딴 제어모듈이 탑재되어 있지만, 디자인으로 개선된다면 제품을 커버하는 작품을 입힐 여지가 있다.
스위치 타격부 스틱의 경우 이름을 적거나 크기를 키워서 효과를 줄 수 있다.
4.1.2. 3D Printing
3D printer는 디자인의 자유성을 크게 향상시켰고, 디자인에 걸리는 소요시간을 효과적으로 단축시켰다. 현재 모델은 서포트를 크게 만들지 않았기에 인쇄하자마자 바로 사용이 가능한 수준이었다.
스위치 커버형태로 개선된 모델은 커버 하단에 많은 서포트가 만들어졌지만, 넓고 얇게 구성되어 있어 쉽게 제거할 수 있다.
3D Printing의 가장 큰 문제는 불규칙한 수축성이다. 일반적으로 2~3% 수축하지만 환경에 따라서 7%까지도 발생할 수 있다. 이를 예측하는 것은 확률에 의해 고려될 수 있으므로 제작과정 중 사용할 수 없을 정도의 저품질이 발생 하는 경우도 있었다.
Motor Mount 부분의 오차 때문에 재인쇄하는 경우가 많았다. 최대한 알맞게 설계하다 보니, 작아서 장착할 수 없는 경우가 많았다. 하여 제작시 고려된 모터보다 살짝 큰 제품은 장착 할 수 없다. 일반적으로 설계시 0.1mm 여유를 두고 설계했다.
밀도는 15%로 인쇄되었으며 적당한 강도를 제공한다. 하지만 훅 부분의 유연성이 떨어지게 되며 장착과정에서 파괴될 수 있다. 10% 정도의 경우 훅 부분에 유연성이 커져서 적은 오차를 보정해줄 수 있다. 하지만 이경우 Module Holder 부분의 강도가 떨어져서 장착, 해제 과정에서 파괴될 수 있다.
4.1.3. 회로구성 및 부품구성
NodeMCU가 제공하는 MicroUSB 전원공급 방식은 모터에 충분한 전류를 제공하지 못한다. 때문에 12V 어답터를 5v로 강하시키는 장치를 사용했으며 외부전원으로 활용되었다. 이 전원은 NodeMCU에도 공급되었다.
제어 모듈 위 브레드보드에 회로를 간단하게 구성했다. 회로가 간단하기 때문에 PCB 제작 및 크기를 줄일 수 있으며 디자인 개선 여지가 크다고 볼 수 있다.
서보 모터의 경우 13g 7천원 상당의 메탈 기어드 서보 모터를 사용했다. 흔하게 쓰이는 9g와 크기는 크게 차이가 안나지만, 큰 마찰 및 저항을 갖는 스위치에는 사용할 수 없다. 현재 제품에는 4.5kgcm 의 토크를 갖는 모터가 사용되었다.
4.2. NodeMCU Coding
4.2.1. Blynk & nodeMCU 개발환경
NodeMCU를 사용하기 위해선 간단하게 설정할 것이 있다. IDE 상에서 Board manager를 사용하여 관련 드라이버를 설치해야 한다. 이 과정에서 ESP32와 같은 WiFi Module 또한 사용할 수 있게 된다.
Blynk의 경우 관련 라이브러리와 어플을 설치하는 것으로 시작된다. IDE 상에서 Blynk클래스를 실행하고, 디바이스마다 발급받은 AuthToken과 통신환경(현재 프로젝트의 경우 WiFi)의 ID 및 password를 정의하면 된다.
Blynk의 통신을 위한 작업은 더 이상 필요없다. 이후 사용에 대해서는 사용량과 필요 요구 용량에 따라 달라지는 요구비용이 있을 뿐이다. 개발하는 환경에 대해서는 큰 요금이 들지 않는다.
4.2.2. 신호송수신 테스트
송수신을 하는데 가장 유용한 것은 Blynk만의 Virtual Pin을 이용하는 것이다. 어플 내에서의 위젯설정으로 Virtual pin의 원하는 숫자를 결정하고 나서, NodeMCU 코딩 부분에 추가 함수 형태로 정의하면 된다. Virtual Read는 추가 선언해야 하는 부분이 없다. 단지 셋업 및 Loop 밖에서의 다른 함수 형태를 취할 뿐이다.
이 함수는 버튼 클릭시 호출되며 인터럽트와 같은 형태이다. 값의 변경 뿐만 아니라 함수 내용 전체가 발동되니 제한없이 다양하게 할 수 있다.
4.2.3. Flag update을 통한 Servo 작동
Blynk Virtual pin에 의한 함수가 발동되면 수신되는 값에 따라 서보 작동이 판단된다. 1의 경우와 0의 경우 서보 회전각이 달라진다. 이는 Flag 형태가 되며 Loop문 내에서 지속적으로 해당 Flag가 업데이트되는지 확인한다. 업데이트되면 서보 회전을 위한 두가지 조건이 모두 만족되면서 결과적으로 작동하게 되어 불을 끄는 역활을 하게 한다.
두가지 조건이 필요한 것은 비의도적 한 번 초과의 클릭, 동작 중 정지 등 예외상황에 의도치 않은 움직임을 막기 위한 최소한의 조건이다. 이는 또한 Google assistance 작동 중 오류를 방지 하기 위함이다.
4.3. Blynk 및 google assistance 설정
IFTTT는 Google assistance와 Blynk의 연결성을 제공한다. IF this Then do That의 줄임말로 this와 that을 커스텀하여 어플리케이션을 제공한다.
This 항목에 대해 Google assistance를 that 항목에 Webhook을 선택한다. 이는 Google assistance가 트리거가 되고 Webhook가 Blynk 서보에 Body(신호)를 담은 Json이 전송되는 형태를 가지고 있다.
Google assistance 설정에서는 원하는 제시어와 답어를 입력할 수 있다. Webhook 설정에서는 트리거 이후의 작동에서 보낼 url과 언어의 형태 및 방법, Body를 설정할 수 있다. 이 프로젝트에서 사용된 것은 Json이며 작동 시 1, 비 작동시 0으로 하였다. Turn on 및 Turn off의 형태를 각각 지정해 주었고, Virtual pin의 경우 어플의 경우와 다른 pin을 사용토록 했다. 하지만 어플에서의 Flag와 동일하기 때문에 작동이 어플의 경우와 동일하게 된다.
5. 기타(회로도, 소스코드, 참고문헌)
5.1. 회로도
NodeMCU 및 12v to 5v 컨버터의 경우 회로도의 부품 이름과 맞지 않을 수 있다. 기능은 동일하거나 비슷하므로 각각을 대체할 수 있다.
5.2. 참고문헌
· 아두이노(https://www.arduino.cc/education)
· googleAssitance-nodeMCU 연동(https://www.youtube.com/watch?v=1WJTkocV2Ik)
· Blynk(https://Blynk.io/)
5.3. 소드코드
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Servo.h>
#define BLYNK_PRINT Serial
char auth[] = “GnCBNY4yVH2j95PCaNmN1QniKvU1QgfL”;
// Your WiFi credentials.
// Set password to “” for open networks.
char ssid[] = “HanNet”;
char pass[] = “chan0812″;
Servo switchServo;
int oper_angle = 22; // 집마다 설정 다름
int lightServoSwtich = 0; //중립 0, On : 1, Off : 2;
int lightServoFlag = 1; // 지속적 신호에 대한 Flag
int middleAngle = 90 + 3;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Blynk.begin(auth, ssid, pass);
switchServo.attach(4); //D2
switchServo.write(middleAngle); //Prevent reset move 180 degree
Serial.println(“Start!! nodeMCU_lightSwitch”);
}
BLYNK_WRITE(V0){
int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable
if(pinValue == 1){
Serial.println(“Swtich On”);
lightServoSwtich = 1;
} if(pinValue == 0) {
Serial.println(“Swtich Off”);
lightServoSwtich = 2;
}
}
BLYNK_WRITE(V1){
int pinValue = param.asInt();
oper_angle = pinValue;
Serial.print(“Servo Max Angle is Changed : “);
Serial.println(oper_angle);
}
BLYNK_WRITE(V2){
int pinValue = param.asInt();
// Serial.print(“Received Signal from Google to turn on : “);
// Serial.println(pinValue);
if(pinValue == 1){
Serial.println(“Swtich On”);
lightServoSwtich = 1;
} if(pinValue == 0) {
Serial.println(“Swtich Off”);
lightServoSwtich = 2;
}
//Serial.println(“Servo Activated!!!!!”);
}
void loop() {
// put your main code here, to run repeatedly:
Blynk.run();
if (lightServoSwtich == 1 && lightServoFlag == 1){ // light On
Serial.println(“ServoActivate : light On”);
switchServo.write(middleAngle – oper_angle);
delay(2000);
lightServoFlag = 2;
lightServoSwtich = 0;
switchServo.write(middleAngle);
} if(lightServoSwtich == 2 && lightServoFlag == 2){ // light Off
Serial.println(“ServoActivate : light Off”);
switchServo.write(middleAngle + oper_angle);
delay(2000);
lightServoFlag = 1;
lightServoSwtich = 0;
switchServo.write(middleAngle);
}
}
[66호]Computer Vision과 Visual Servoing을 이용한 군집 로봇 시스템, minister
2020 ICT 융합 프로젝트 공모전 장려상
Computer Vision과 Visual Servoing을
이용한 군집 로봇 시스템, minister
글 | KAIST 이준, 정송현, 박종건, 고형석, 강민수
1. 심사평
칩센 지원자의 작품 개요에도 표현되어 있다시피 군집 로봇 시스템을 위해서는 로봇 내부에 다양한 센서나 위치 측정 부품, 또는 자체 시스템 구축 등을 위하여 비용이 많이 들게 되어 있습니다. 금번 작품과 같이 Computer visioning 을 통해 구분을 할 경우 판단이 필요한 부분을 모두 Control system 하나에서 해결이 가능할 수도 있습니다. 하지만, 군집 로봇 시스템이 적용되는 형태나 최종 사용 목적을 보자면, 현재 구성한 것과 같은 카메라를 통한 Visioning만으로 한계가 분명 존재하게 됩니다. 군집 드론을 위해서는 카메라는 어디에 있어야 할까요? 주변 환경을 판단하는 센서가 없다면, 카메라에 보이지 않는 사각 공간 또는 변수에서는 어떤 동작을 해야 할까요? 아이디어와 핫한 기술 트렌드에 대하여 심플하게 접근하여 어느 정도의 결과를 얻어내는 것은 좋지만, 작품이 적용될 목표 환경에 구체적인 고민도 함께하면 더 완벽한 작품이 나올 듯 합니다.
펌테크 아이디어와 실용성을 갖춘 작품이라고 생각이 듭니다. 단 제출된 보고서 내용을 감안하자면 목표대비 작품의 기획과정 및 소프트웨어, 하드웨어에 관련된 일부 진행 과정이 확인이 되었을 뿐 출품작의 구체적인 진행 과정이나 최종 완성도를 정확히 판단할 수가 없었습니다.
위드로봇 계획한 대로 모두 만들어졌다면 더 좋은 점수를 받을 수 있었던 작품입니다.
2. 작품 개요
군집 로봇은 IoT의 발전과 함께 다양한 방면에서 사용되고 있다. 로봇 하나로는 해결하지 못 하는 일도 여러 대의 로봇이 협력하면 해결할 수 있게 되는 문제들의 해법으로 주로 군집 로봇이 제시된다. 대형 물류 회사의 창고 정리를 위한 로봇이나, 어두운 하늘에 아름다운 무늬를 펼치는 드론 쇼가 대표적이다. 이런 군집 로봇 시스템을 제작하기 위해서는 다양한 센서가 필요하거나 비싼 위치인식 센서가 필요한 경우가 많다. 이러한 점을 해결하기 위해서 마커와 엔코더를 이용한 방식의 군집로봇도 많이 등장하게 되었다. 하지만, 엔코더나 마커를 사용하는 방식은 개별 로봇에 처리능력을 부여해야 하기에 시스템을 구축하는 데에 여전히 적지 않은 비용이 필요하게 된다. 우리 팀은 이러한 문제에 대한 해법을 Visual Servoing이라는 방법을 통해서 해결해 보고자 했다. Visual Servoing은 Computer Vison과 연관이 깊은 기술로, 이미지에서 시스템의 위치나 자세 등의 정보를 알아내 그것을 이용해 Feedback 하여 시스템을 제어할 수 있는 기술이다. 따라서 개별 로봇의 단가를 더욱 낮추면서 손쉽게 사용할 수 있는 군집로봇 시스템에 적용하기 적합했다. 프로젝트의 결과로 카메라를 이용한 로봇의 Motion Tracking 및 Path Planning을 수행하는 코드를 작성하였으며, WiFi를 이용한 통신으로 보안성과 신뢰성을 가진 시스템을 구축할 수 있었다.
3. 작품 설명
3.1. 주요 동작 및 특징
완성된 로봇은 가로 7cm, 세로 7cm, 높이 10cm의 직육면체 형태이며, 바퀴 두개를 이용하여 움직일 수 있도록 설계되었다. 로봇의 윗부분에 마커로 사용한 스티커를 부착하였으며, 카메라가 이를 인식해 로봇의 좌표를 읽고, 각각의 로봇을 스티커의 색으로 분간하여 특정 로봇을 특정 위치로 제어 가능하다.
3.2. 전체 시스템 구성
전체 시스템의 컨셉은 아래 그림과 같다. 여러 대의 로봇이 카메라가 비추는 평면 안에서 돌아다닐 수 있으며, 높은 곳에 고정되어 서버와 연결된 카메라는 로봇을 관찰하고, 위치를 파악하며, 서버는 카메라를 통해 얻은 이미지르 바탕으로 로봇에게 명령을 내린다.
3.2.1. 로봇
로봇은 Wi-Fi를 사용할 수 있는 IoT 보드 중에서 좋은 성능을 보이는 ESP32칩을 내장한 보드인 LOLIN32 Lite 보드를 사용하였다. 그 외의 부품으로는 모터는 작으면서도 높은 기어비의 DC모터를 사용했으며, Li-Po 배터리와 모터드라이버, 배터리 보호회로를 내장했다. 동체는 3D 프린팅으로 제작하였다.
LOLIN32 Lite 보드는 디바이스 마트에서 판매중인 ESP32 칩셋 기반 개발보드로 Atmega 칩셋을 사용한 제품군이 주력인 아두이노에 비해 비교적 저렴한 값으로 높은 성능을 얻을 수 있으며, Bluetooth와 WiFi 기능을 기본 내장하고 있어 IoT 제품 뿐만 아니라 간단한 로봇을 제작하는 데까지 폭 넓게 이용될 수 있는 다재다능한 개발보드이다.
LOLIN32 Lite 보드는 대부분의 핀이 PWM과 ADC 기능을 지원하고 있으며, 5V 동작전압을 가진 Arduino Uno에 비해 3.3V로 낮은 동작전압과 작은 사이즈를 가지고 있음에도 핀 수와 기능으로는 전혀 밀리지 않는다. 게다가 4MB의 큰 플래시 메모리를 가지고 있어(Uno의 경우 32KB) MicroPython과 같은 소형 파이썬 인터프리터 바이너리를 업로드하여 사용할 수도 있다.
LOLIN32 Lite는 많은 기능을 가지고 있지만 사용하기도 매우 쉽다. 기존에 아두이노를 프로그래밍하기 위해 나온 Arduino Ide에 새로운 보드 프로필을 추가하기만 하면 마치 아두이노를 프로그래밍하듯 쉽게 프로그래밍 할 수 있다는 강력한 장점이 있다. 또한 ESP32에 내장된 WiFi나 Bluetooth를 사용한 NetBIOS나 OTA와 같은 다양한 기능들을 사용한 예제들을 기본적으로 제공하기 때문에, 복잡하게 참고 문서들을 뒤져가면서 코딩할 필요 없이 예제를 참고하기만 하면 사용하고자 하는 기능들을 쉽게 사용할 수 있다. 이와 같은 여러가지 장점들로 인해, minister 프로젝트에서는 LOLIN32 Lite 보드를 로봇의 메인 보드로 삼았다.
3.2.2. 서버
서버는 파이썬으로 작성된 프로그램을 실행시킬 수 있는, 로봇과 같은 네트워크상에 위치한 컴퓨터다. 카메라를 USB를 통해 연결하였으며, 로봇과는 WiFi를 통해 연결된다. 서버의 파이썬 시스템은 아래와 같이 구성되어 있다.
아래는 코드에 대한 자세한 설명이다.
크게 통신을 담당하는 스레드인 clientObj() 클래스와 로봇 위치인식, 모터 속도 계산을 담당하는 스레드인 robotObj() 클래스로 나누어져 동작한다. 두 클래스는 모두 연결된 로봇의 개수만큼 존재하며 일대일 대응 관계를 가진다.
ListenRobot() : 무한루프를 돌면서 새로운 로봇이 서버에 연결되는 즉시 해당 로봇의 인덱스 i와 소켓 객체를 이용해서 robotObj(), clientObj() 객체(스레드)를 새로 생성한다.
clientObj() : Threading 모듈을 상속받아 새로운 스레드를 만든다. comms[i] 명령 큐를 계속 관찰하다가 새로운 명령이 들어오면 아스키로 인코딩해서 로봇으로 전송하고 전송한 명령은 pop한다.
robotObj() : Threading 모듈을 상속받아 새로운 스레드를 만든다. 카메라로 로봇의 위치를 인식하고 waypoint를 입력 받은 뒤 로봇으로 전송해야 할 명령을 연산해서 전역 리스트 comms[i]에 집어넣는다.
Computer Vision 노드는 아래와 같이 실행된다.
로봇의 위치를 파악하기 위해서 사용한 Computer Vison 프로그램의 demo이다. 기본적으로 파이썬의 OpenCV 라이브러리를 이용하였으며, 로봇에 부착된 컬러 마커 이미지는 웹 캠으로 읽어온다. 상세한 코드는 5. 기타에 첨부하였다.
로봇의 위치를 CV 알고리즘으로 알아낼 수 있게 되었기에 로봇을 특정 위치로 옮겨가게 만들 수 있다. 아래 그림은 알고리즘을 설명한다.
그림에서 설명하는 것과 같이 로봇에 방향을 알아낼 센서가 없기 때문에 짧은 이동과 진행해야 하는 방향의 차이를 이용하여 이동 각도를 수정하고 그것을 속도에 반영해서 움직이도록 짜여 있다.
3.3. 개발 환경
개발은 모두 우분투 16.04 LTS 환경에서 진행하였다. LOLIN32 Lite 보드에 코딩하기 위해서 Arduino IDE를 사용했으며, 파이썬 코드의 작성은 Visual Studio Code를 이용해 작성하였다. 영상 처리에서는 OpenCV를 사용하였으며, 로봇과 TCP로 통신하였다.
4. 단계별 로봇 제작 과정
4.1. 개발
4.1.1. 부품 선정
부품은 디바이스마트에서 선정하여 주문 후 제작하였다. 모터는 로봇이 너무 빠르게 움직이면 추적이 어렵기에 높은 기어비를 가진 모터를 선정하였으며, 배터리와 배터리 보호 회로, 모터 드라이버를 사용하는 전력량에 맞추어 넉넉한 스펙으로 준비하였다.
4.1.2. 부품 사이즈
측정 부품의 사이즈를 실측하는 과정을 통해 설계 파일과의 오차를 확인하고, 3D 프린팅을 통해 프레임을 제작하였을 때 잘 들어맞도록 한다. 본 과정을 거치면서 3D 프린팅 모델의 치수가 실제 부품과 잘 맞게 된다.
4.1.3. 3D 모델링
개선 실제로 모델을 출력하여 로봇을 조립해보며 부족한 점을 개선하고 수정하여 최종 모델을 향해 나아간다.
4.2. 제작
4.2.1. 메인보드의 제작
납땜으로 LOLIN32 Lite 보드와 배터리 보호회로, 모터 드라이버 및 I/O를 연결해준다. I/O를 통해 모터와 배터리를 쉽게 교체 가능하게 제작하였다.
4.2.2. 3D 프린팅
몸체는 PLA로 FDM방식의 3D 프린터를 이용해 제작하였다. 간단하고 작은 구조를 가지기에 3D 프린터로 출력하기에 알맞은 모델이다. 출력이 완료되면 조금의 사후처리를 거치면 완성이다.
4.2.3. 조립
몸체에 모터를 고정하고 배터리를 넣어준 후, 메인보드에 선을 연결하고 몸체에 끼워주면 조립이 끝난다. 조립이 완료된 후, 로봇의 윗부분에 인식용 마커를 붙여준다. 완성된 로봇의 모습은 <Fig 1>과 같다.
4.3. 향후 추가 개발 계획
Minister의 새로운 디자인은 햄스터볼과 스타워즈의 BB-8을 연상케 하는 굴러가는 형태이다. 이전의 단순한 직육면체에서 업그레이드된 디자인으로, 외관의 공에 색을 칠하여 로봇 전체가 이미지 인식에 용이한 디자인을 염두에 두었다. 바퀴로 구르는 대신 공을 안에서 굴리기 때문에 구동방식과 공간 배분에 다소 디자인 노력이 들어갔다.
첫째로, 모든 부품이 둥그런 껍질 속에 빽빽하게 들어간다. 허용되는 공간이 줄어들수록 디자인이 힘들어지는데, 현재 개발은 야구공 정도의 작은 크기를 염두에 두고 있어 공간 배분이 첫번째 난관이었다. 게다가 둥그렇게 잘 굴러야 해서 외부에 들어간 곳도 나온 곳도 없도록 충전과 데이터 포트를 무선으로 전환했다. 모터, 배터리, 프로세서, 충전포트가 모두 공 안에 들어가며 외부는 깔끔한 구면(球面)이다.
공간 배분에 있어서 가장 큰 문제는 내부에서 껍데기와 마찰이 필요한 곳만 닿게 하는 것이었다. 최우선으로 배치한 부품은 배터리로, 크기도 가장 크고 무게도 가장 무거워서 나머지 부품들의 위치선정과 무게중심의 위치에 가장 기여도가 컸다. 그 위치에 따라 바퀴는 껍데기에 닿지만 모터는 닿지 않는 바퀴 크기와 모터 마운트, 회로와 배선 또한 닿지 않게 뚜껑이 있는 상자를 설계하였다.
다른 문제는 구동 방식이었다. 평범하지 않은 구동을 채택하면서 로봇을 어떻게 움직일지 몇 가지 방법이 논의되었다. 이 디자인에서는 그 중 보조바퀴를 이용하여 접촉을 유지한 채 공을 굴리는 방법이 고려되었다. 방향 전환은 정지상태에서 두 바퀴를 반대 방향으로 돌려 내부의 동체가 원하는 방향을 바라보게 한 후 움직이도록 설계하였다.
5. 소스코드
5.1. CV 노드 상세 파이썬 코드
#from collections import deque
import cv2
import imutils
#import time
import numpy as np
waypoint = [] #로봇이 이동할 지점의 좌표
hsv = np.zeros((1,3), dtype = int)
colorUpper = (0,0,0) #color 범위 지정
colorLower = (0,0,0)
CAM_ID = 0
def mouse_callback(event, x, y, flags, param):
—————————-
마우스 콜백 함수
마우스 왼쪽 버튼 클릭할 경우 이동할 좌표를 waypoint 리스트에 추가
마우스 오른쪽 버튼 클릭할 경우 해당 마우스가 해당하는 pixel의 색깔을 추출한 후, 색상 범위 설정
마우스 휠 클릭할 경우 waypoint 에서 가장 최근의 좌표 제거(경로 취소할 때 이용)
—————————-
global waypoint, hsv, colorUpper, colorLower
if event == cv2.EVENT_LBUTTONDOWN:
waypoint.append((x,y))
if event == cv2.EVENT_RBUTTONDOWN:
color = img[1][y, x]
one_pixel = np.uint8([[color]])
hsv = cv2.cvtColor(one_pixel, cv2.COLOR_BGR2HSV)
hsv = hsv[0][0]
colorUpper = np.array([hsv[0] + 35, hsv[1] + 35, hsv[2] + 35])
colorLower = np.array([hsv[0] – 35, hsv[1] – 35, hsv[2] – 35])
if event == cv2.EVENT_MBUTTONDOWN:
if len(waypoint) != 0:
waypoint.pop(len(waypoint)-1)
def Tracking(frame, hsvUpper, hsvLower):
—————————-
컬러마커를 둘러싸는 원을 그리고 원의 중심을 return
(1. 지정한 색깔 범위에 포함되는 부분에 마스크를 생성한다.
2. 마스크로부터 contour를 구한 후 contour를 둘러싸는 외접원을 구한다.
3. 외접원의 반지름이 5 이상일 경우 원을 frame에 그리고, 원의 중심을 return한다. )
—————————-
frame = frame[1]
frame = imutils.resize(frame, width = 600)
blurred = cv2.GaussianBlur(frame, (11,11),0)
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, hsvLower, hsvUpper)
mask = cv2.erode(mask, None, iterations = 2)
mask = cv2.dilate(mask, None, iterations = 2)
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
center = None
if len(cnts) > 0:
c = max(cnts, key=cv2.contourArea)
((x,y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"]/M["m00"]), int(M["m01"]/M["m00"]))
if radius > 5:
cv2.circle(frame, (int(x),int(y)), int(radius), (180,170,220),5)
cv2.imshow(“Frame”, frame)
#time.sleep(0.1)
return (center)
def AddTracking():
—————————-
컬러마커가 이동하여(로봇이 이동하여) 지정한 경로의 좌표에 근접하는 경우, 그 좌표를 waypoint list에서 빼낸다.
—————————-
global waypoint, X1, Y1
if len(waypoint) != 0:
for i in waypoint:
X2 = i[0]
Y2 = i[1]
print(waypoint)
if abs(X1-X2)<25 and abs(Y1-Y2)<25:
waypoint.pop(0)
print(waypoint)
vs=cv2.VideoCapture(CAM_ID)
cv2.namedWindow(“Frame”)
cv2.setMouseCallback(“Frame”, mouse_callback)
while True:
img = vs.read()
if Tracking(img, colorUpper , colorLower) != None:
X1, Y1 = Tracking(img, colorUpper , colorLower)
AddTracking()
key = cv2.waitKey(1) & 0xFF
if key == ord(“q”):
break
cv2.destroyAllWindows()
cs
[66호]물체탐지 4족 보행로봇
2020 ICT 융합 프로젝트 공모전 장려상
물체탐지 4족 보행로봇
글 | 선문대학교 곽호권
1. 심사평
칩센 개인적으로 더 좋은 하드웨어 적용과 더 많은 시간을 들이면 훨씬 개선된 움직임이 보여질 거라는 생각이 드는데, 작품이 개발자의 초기 기대치가 어느 정도인지가 궁금합니다. 로봇을 통해 사람이 하기에 어렵거나 위험한 일을 대신하게 하는 것은 앞으로도 계속 추구 되어야할 중요한 기술 개발 분야라고 생각됩니다. 공모전을 통해 이번에 시작을 한거라고 보면, 추가로 충분히 개선이 가능할만한 작품으로 보여 기대가 됩니다.
펌테크 아이디어와 실용성을 갖춘 작품이라고 생각이 듭니다. 단 제출된 보고서 내용을 고려하자면 작품에 대한 기획 의도는 우수하다고 생각되지만 계획에 대비해서 출품작의 일부 진행 과정은 확인이 되나 최종적인 완성도를 구체적으로 확인할 수가 없었습니다.
위드로봇 게이트 제어 및 영상 스트리밍 등 높은 난이도 기술을 구현한 작품입니다.
2. 작품개요
2.1. 선정동기
뉴스에서 자연재해로 인한 인명구조를 하기 위해 위험을 무릅쓰고 탐사하는 모습을 보고 물체 탐지 4족 보행로봇을 통해 인명구조를 하면 좋을 것 같다는 생각에 선정하게 되었습니다.
사람이 들어갈 수 없는 덕트 안에 어떠한 물체가 걸리는 현상이 발생하여 한 개의 라인 자체를 중단시키고 위험하게 물체를 꺼내는 모습을 보고 로봇을 통해 그 문제를 해결하면 좋을 것 같다는 영감을 얻어 선정하게 되었습니다.
2.2. 목적
· 재해로 접근하기 어려운 지역이나 좁은 지역에서 사용할 수 있는 로봇입니다.
· 좁고 어두운 하수관 같은 곳에 사용할 수 있는 자동화 로봇입니다.
· 물체 탐지 4족 보행로봇을 통해 효율적으로 일을 처리할 수 있게 하기 위함입니다.
2.3. 필요성
· 여러 인력을 동원해서 해야 하는 번거로운 일을 혼자서 핸드폰으로 할 수 있으므로 불필요한 인력이 감소됩니다.
· 공장에서 사용시 라인이 중단되는 현상을 막아 시간과 비용이 절감됩니다.
· 사람이 접근하기 위험한 곳에 로봇이 직접 탐사를 진행하기 때문에 안전하고 위험 부담이 감소합니다.
3. 작품 설명
3.1. 주요 동작 및 특징
· 4족 보행로봇 보행
· 적외선 센서 감지 및 DATA 출력
· Application 스트리밍
· Application 조작
3.2. 전체 시스템 구성
Application을 통한 4족 보행로봇 제어 및 실시간 영상 스트리밍, 센서 DATA값 확인합니다.
3.3. 개발 환경(개발 언어, Tool, 사용 시스템 등)
· CodeVision AVR
· Visual Studio 2017 (C++)
· PADS Logic
· PADS Layout
· Autodesk Inventor
· Simplify 3D Printing with Ultimaker Cura 4.0
· RasberryPi Python
· Android studio
4. 단계별 제작 과정
4.1. 모터 제어 시스템 개발 (CodeVision AVR Tool 사용)
4.1.1. 모터 제어 실험 준비
1. PCB Bread Board에 Pinheader를 사용하여 ATmega128를 부착할 수 있도록 설계합니다.
2. 서보모터를 제어하기 위해 PortA(0~7) 와 PortC(0~3)까지 12개의 포트를 와이어링합니다.
4.1.2. 모터 제어 실험
1. 서보모터인 SG90과 MG90s에 PWM신호를 입력하여 원하는 각도로 제어할 수 있도록 변하는 값을 각도는 각도기로 PWM신호는 오실로스코프로 확인합니다.
2. 1400usec만큼 PWM신호를 입력하였을 때 서보모터가 90도만큼 회전합니다.
4.2. 4족 보행로봇 3D Modeling 및 3D Print (Inventor Tool 사용)
※ Inventor를 이용한 4족 보행 로봇 부품 설계
4.3. 4족 보행로봇 걸음걸이 연구 (AVR과 MFC 시리얼 통신)
4.3.1. 걸음걸이 알고리즘 연구
4.3.2. Creep Gait (크립 걸음걸이)
1. 크립 걸음 걸이는 사용하기 가장 쉬운 걸음 걸이입니다. 로봇은 지상에서 3피트를 유지하고 그 3피트가 형성하는 삼각형 안에 무게중심(CoG)을 유지합니다. CoG가 너무 오랫동안 삼각형을 벗어나면 넘어질 것입니다.
문제점 : 걷는 동안 안정성을 유지하는 방법
1. 이것은 시작 위치이며 한쪽은 두 개의 다리가 밖으로 나오고 다른 두 개의 다리는 안쪽으로 당겨집니다.
2. 오른쪽 상단 다리가 로봇의 앞쪽으로 들어 올려 밖으로 나옵니다.
3. 모든 다리가 뒤로 움직여 몸을 앞으로 움직입니다.
4. 뒷좌석 다리가 몸체와 함께 앞으로 들어 올려집니다. 이 위치는 시작 위치의 대칭 이미지입니다.
5. 왼쪽 상단 다리가 로봇 앞쪽으로 들어 올려 밖으로 나옵니다.
6. 다시 모든 다리가 뒤로 움직여 몸이 앞으로 움직입니다.
7. 뒤 우측 다리가 들어 올려 신체로 다시 들어가서 우리를 시작 위치로 되돌립니다. 모든 시간에 다리의 바닥에 형성된 삼각형에는 무게중심(CoG)이 포함됩니다. 이것은 크립 걸음 걸이의 본질입니다. 이 패턴을 보면, 그것이 본질적으로 두 세트의 미러 된 움직임 임을 알 수 있습니다. 단계, 단계 및 교대, 다른 단계, 단계 및 교대가 뒤 따릅니다.
4.4. 전자회로기판 설계
PADS Logic Tool, PADS Layout Tool 사용
4.5. 라즈베리파이와 적외선 센서 연동 (Python 사용)
· 이름 : mini PIR motion sensor
· 크기 : 20mm*20mm*12mm
· 감지거리 : 2m(25°C)
· 감지방법 : 적외선 온열 신체 감지
· 공급전압 : 2.7~3.3V
· 이름 : analog sensor
· 크기 : 25*10mm
· 감지거리 : 5~15mm
· 감지방법 : 적외선 반사 감지
· 전압 : 5V
· 출력 : 아날로그 전압
4.6. 라즈베리파이와 적외선 카메라 연동 (Python 사용)
4.6.1. 라즈베리 파이 설정
4.6.2. 인터페이스 옵션 설정(카메라활성화)
4.6.3. mjpg streamer 패키지 설치
4.6.4. 편집기를 이용한 옵션사용
- i뒤에 내용은 파이카메라 옵션
- o뒤에 내용은 스트리밍 출력옵션
4.6.5. 옵션변경
4.7. 동영상 스트리밍 서버 구현
4.7.1. 라즈베리파이 ip주소 찾기
4.7.2. 서버접속 후 스트리밍 확인
4.8. Application 개발 (Android Studio 사용)
4.8.1. 버튼 세 개(실시간 영상,센서값, 같이보기)를 만듭니다.
4.8.2. loadData를 이용한 앱 안 동영상 스트리밍과 Toast를 이용한 알림 창 생성합니다.
4.8.3. 안드로이드 앱 안 동영상 스트리밍합니다.
4.8.4. 라즈베리파이 센서 데이터 값 App에 전송
5. 회로도
6. 참고문헌
· App 개발 때 사용한 문헌 : http://blog.naver.com/PostView.nhn?blogId=juke45ef&logNo=220827583111
· https://webnautes.tistory.com/995
· https://tony2012.tistory.com/category/Develop/IT
7. 소스코드
Android Studio – Application 개발
* 버튼 세개만들기
<Button
android:id=”@=id/btn_video”
android:layout_width=”295dp”
android:layout_height=”217dp”
android:text=”실시간 영상”
android:layout_gravity=”center”/>
<Button
android:id=”@=id/btn_sensor”
android:layout_width=”296dp”
android:layout_height=”217dp”
android:text=”실시간 영상”
android:layout_gravity=”center”/>
<Button
android:id=”@=id/btn_video”
android:layout_width=”295dp”
android:layout_height=”217dp”
android:text=”실시간 영상”
android:layout_gravity=”center”/>
1. AndroidManifest.xml에 추가
<uses-permission android:name=”android.permission.BLUETOOTH” />
<uses-permission android:name=”android.permission.BLUETOOTH_ADMIN” />
2. 디바이스에서 블루투스를 지원하는지 체크
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
showErrorDialog(“This device is not implement Bluetooth.”);
return;
}
3. 사용자에게 블루투스를 켜도록 요청
if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
}
4. 블루투스 활성화시 showPairedDevicesListDialog() 메소드를 호출
if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
}
else {
Log.d(TAG, “Initialisation successful.”);
showPairedDevicesListDialog();
}
5. 페어링 되어 있는 블루투스 장치들의 목록을 보여준다.
public void showPairedDevicesListDialog()
{
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
final BluetoothDevice[] pairedDevices = devices.toArray(new BluetoothDevice[0]);
if ( pairedDevices.length == 0 ){
showQuitDialog( “No devices have been paired.\n”
+”You must pair it with another device.”);
return;
}
String[] items;
items = new String[pairedDevices.length];
for (int i=0;i<pairedDevices.length;i++) {
items[i] = pairedDevices[i].getName();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(“Select device”);
builder.setCancelable(false);
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
// Attempt to connect to the device
ConnectTask task = new ConnectTask(pairedDevices[which]);
task.execute();
}
});
builder.create().show();
}
6. 블루투스 페어링 확인
pi@raspberrypi:~ $ bluetoothctl
7. 라즈베리파이 센서 데이터 값 APP에 전송
7-1) 기본 ExampleFragment 에 버튼을 추가
<span style=”font-size: 12pt;”><Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”put”
android:id=”@+id/button1″
android:layout_marginTop=”120dp”
android:layout_centerHorizontal=”true” /><#textarea></span></p><div class=”autosourcing-stub-extra”></div></blockquote><div style=”color: rgb(161, 161, 161); line-height: 1.5; margin: 25px 0px 10px; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 11px; font-family: Dotum, sans-serif; background-color: rgb(255, 255, 255);”>
</div>
</div>
<p></p><p><br /></p></div><p></p><p><br /></p></span>
7-2) ExampleFragemnt 파일에 Activity 로 콜백해주는 함수를 선언하고 button 클릭시 만든 함수를 호출하는 이벤트를 작성
<span style=”font-size: 12pt;”>package com.hardcopy.btctemplate.fragments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.hardcopy.btctemplate.R;
public class ExampleFragment extends Fragment {
private Context mContext = null;
private IFragmentListener mFragmentListener = null;
private Handler mActivityHandler = null;
Button button1;
String message = “%Swif^”;
public ExampleFragment(){}
@SuppressLint(“ValidFragment”)
public ExampleFragment(Context c, IFragmentListener l, Handler h) {
mContext = c;
mFragmentListener = l;
mActivityHandler = h;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main_dummy, container, false);
//버튼 클릭이벤트
button1 = (Button) rootView.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(message != null && message.length() > 0)
sendMessage(message); //아래 함수에 message를 전달하여 호출
}
});
return rootView;
}
//*****************************************
// Activity 에 데이터를 전달하는 콜백하는 함수
//*****************************************
private void sendMessage(String message) {
if(message == null || message.length() < 1)
return;
// send to remote
if(mFragmentListener != null)
mFragmentListener.OnFragmentCallback(IFragmentListener.CALLBACK_SEND_MESSAGE, 0, 0, message, null,null);
else
return;
}
}
<#textarea></span></font></strong></span></p><div class=”autosourcing-stub-extra”></div><p></p></blockquote></div></div><p><br /></p></span>
7-3) MainActivity.java 파일에 해당 콜백에 얻어오는 데이터를 가지고 블루투스에 전송
public void OnFragmentCallback(int msgType, int arg0, int arg1, String arg2, String arg3, Object arg4) {
switch(msgType) {
case IFragmentListener.CALLBACK_RUN_IN_BACKGROUND:
if(mService != null)
mService.startServiceMonitoring();
break;
case IFragmentListener.CALLBACK_SEND_MESSAGE:
if(mService != null && arg2 != null)
mService.sendMessageToRemote(arg2);
//sendMessageToRemote()함수는 BTCTempleteService.java에서 구성된 블루투스 송신메서드.
default:
break;
}
}
<#textarea></span></font></strong></p>
<p style=”list-style: none; margin: 0px; padding: 0px; font-family: inherit; font-size: inherit;”><strong><font color=”rgb(0,0,0)” style=”background-color: rgb(228, 255, 117);”><br /></font></strong></p></blockquote></div></div><p><br /></p>
출처: https://tony2012.tistory.com/?page=28 [Useless]
CodeVision AVR_4족보행로봇 제어
#include <mega128.h> //codevision에서 Atmega128사용시
#include <string.h> //string 관련 함수 , sprintf
#include <stdlib.h>
#include <stdio.h>
#include <delay.h>
#include <math.h>
// 시리얼통신에서 입력한 명령을 담아두는 곳
//예: m00123(enter) -> string[0]=’m’ string[1]=’0′ … string[6]=0x0d=‘\r’(enter에 해당하는 ASCII Code값;
char string[64];
int iTimer0,iTimer2; //타이머 초기화 카운트 값
int cmd_count;
//cmd_count: command counting, 키보드입력시 입력 횟수
int count_10usec;
//count_10usec; 10usec마다 카운팅되는 변수 timer2 overflow 시
//DegMotor:각 모터의 회전 각도,
//CntMotor:각모터의 PWM Pulse 폭
int DegMotor[12],CntMotor[12];
int OffsetDegMotor[12]={0,-5,10,5,-5,-10,13,-5,-20,-3,-2,15};
//Leg Length
float La=55,Lb=77.5,Lc=27.5;
float pi=3.141592;
/*———————————–
data를 시리얼 포트로 1 바이트 송신하기
————————————*/
void sendChar(char data)
{
int i = 0;
// To send data with the USART put the data in the USART data register
UDR0 = data;
// Check to see if the global interrupts are enabled
if(SREG & 0×80)
{
// Wait until the byte is sent or we count out
while ( !(UCSR0A&0×40) && (i<10000) )
{
i++;
}
}
else // Wait until the byte is sent
while( !(UCSR0A&0×40) ); //송신완료시까지 대기
// Clear the TXCflag
UCSR0A=UCSR0A|0×40;
}
/*—————————————————
문자열 str을 문자열 맨뒤 NULL이 나올떄 까지 한바이트씩 보내기
—————————————————-*/
void sendString(char* str)
{
while(*str) sendChar(*str++);
}
/*
만일 str[ ] = “rs232c”라면
str[0]=’r'=0×72
str[1]=’s’
str[2]=’2′=0×32
str[3]=’3′
str[4]=’2′=0×32
str[5]=’c’
str[6]=”
‘ ‘:char
” “:string
str: str이라는 문자열이 저장된 주소
*str:str에 저장된 내용
step1:while(*str) => while(‘r’); ->while(true)
step2:sendChar(*str); =>sendChar(‘r’);
step3: str++; //주소증가
step4:while(*str) => while(‘s’); ->while(true)
step5:sendChar(*str); =>sendChar(‘s’);
step6: str++;
….
stepn:while(*str) => while(”); ->while(false)
*/
/*————————————————–
시리얼 포트 초기화
8비트 데이터, 1 stop 비트,non parity, 9600 보레이트
————————————————–*/
void rs232c_initialize(void)
{
DDRE=DDRE|0×02; //포트 E의 입출력 설정 ,PE0:0 Rx0,PE1:1 Tx0
//USART Register Init
UCSR0A=0×00;
UCSR0B=0×98; //1001 1000 //7 bit : RX Complete Interrupt Enable
//4 bit : Receiver Enable. Writing this bit to one enables the USARTn Receiver.
//3 bit : Transmitter Enable. Writing this bit to one enables the USARTn Transmitter.
//2 bit : UCSR0C 2,1 bit와 연결사용
UCSR0C=0×06; //0000 0110 //6 bit : USART Mode Select. Asynchronous Operation
//5,4 bit : Parity Mode. Disabled
//3 bit : Stop Bit Select. 1-bit
//2,1 bit : Character Size. 8-bit
//0 bit : Clock Polarity. Transmitted Data : Rising XCKn Edge
// Received Data : Falling XCKn Edge
UBRR0H=0×00; //USARTn Baud Rate Register
UBRR0L=0×67; //USARTn Baud Rate Register 9600
//UBRR0L=0×8; //USARTn Baud Rate Register 115200
}
void init_Timer()
{
//prescale=1024, input frequency=16MHz/1024=64KHz T=1/64usec
//Overflow시 timing 간격은 256/64msec=16.348msec
iTimer0=0;
//Timer2, input f=16MHz, 160count=10sec is TCNT2=256-160=96
//but from tunning process, the timer2 initialzed variable is 113
iTimer2=113;
DDRC=DDRA=0xff; //Set PORTA all output
PORTC=PORTA=0×00; //Set All PortA Low
TCCR0=0×07; //oc0 not enable, normal, 분주비 1024, pb4 일반포트로 사용
TCCR2=0×01; //0b00000101 //oc2 not enable, normal, 분주비 1, pb7 일반포트로 사용
// 0 0 00 0 010
TIMSK=0×41; //timer 0and 2 overflow interrupt enable
TCNT0=iTimer0; //time 0 초기값
TCNT2=iTimer2;
}
//Deg로 입력된 것을 Pulse Width로 변환하기
void ConvertDeg2Cnt()
{
int i;
for(i=0;i<12;i++)
CntMotor[i]=(10.58*(DegMotor[i]+OffsetDegMotor[i])+459.4)/10+3;
}
interrupt [TIM0_OVF] void timer_int0()
{
PORTC=PORTA=0xff;
count_10usec=0;
TIMSK = TIMSK | 0×40; //Timer 2 Interrupt Enable
}
interrupt [TIM2_OVF] void timer_int2()
{
TCNT2=iTimer2; //the initialzed variable for 10usec timer2 overflow interrupt
count_10usec++;
if(count_10usec == CntMotor[0] ) PORTA.0=0;
if(count_10usec == CntMotor[1] ) PORTA.1=0;
if(count_10usec == CntMotor[2]) PORTA.2=0;
if(count_10usec == CntMotor[3]) PORTA.3=0;
if(count_10usec == CntMotor[4]) PORTA.4=0;
if(count_10usec == CntMotor[5]) PORTA.5=0;
if(count_10usec == CntMotor[6]) PORTA.6=0;
if(count_10usec == CntMotor[7]) PORTA.7=0;
if(count_10usec == CntMotor[8]) PORTC.0=0;
if(count_10usec == CntMotor[9]) PORTC.1=0;
if(count_10usec == CntMotor[10]) PORTC.2=0;
if(count_10usec == CntMotor[11]) PORTC.3=0;
if(count_10usec > 300)
{
TIMSK = TIMSK & 0xbf; //Timer 2 Interrupt disable
}
}
/*———————————–
수신 받은 문자열 s[]데이터 분석
————————————*/
void parseInput(char * s)
{
int imotor;
// parse first character
switch (s[0])
{
//xx축 motor 개별 제어
//m+XX(motor no.)+xxx+’\r’
case ‘m’:
case ‘M’:
//if m11135 이면 s[1]=’1′=0×31, s[2]=’1′=0×31
//아스키코드값으로 표현된 연속된 두 숫자를 10진수로 변환 하기 imotor->1*10+1=11
imotor=(s[1]-’0′)*10+(s[2]-’0′);
//s+3부터 문자열을 ascii to integer변환
/*
s[0]=’m’
s[1]=’1′
s[2]=’1′
s[3]=’1′
s[4]=’3′
s[5]=’5′
s[6]=”
이므로 s[3[부터 끝까지 ascii로되어있는 문자를 정수로 변환하시오
DegMotor[imotor]=135;
*/
DegMotor[imotor]=atoi(s+3);
ConvertDeg2Cnt();
break;
//12개의 모터 제어 데이터 열
//d+xxxxxxxxxxxx+’\r’, x는 unsigned char data
case ‘d’:
case ‘D’:
for(imotor=0;imotor<12;imotor++)
DegMotor[imotor]=(unsigned char)s[imotor+1];
ConvertDeg2Cnt();
break;
case ‘c’:
sendString(“Your command input is ..!\r\n”);
sendString(s);
sendString(“\r\n”);
break;
case ‘e’:
sendString(“Your message input is ..!\r\n”);
sendString(s);
sendString(“\r\n”);
break;
default:
break;
}
//앞에서 받은 명령을 초기화하기
s[0] = ”;
}
/*———————————————–
하이퍼터밀널에서 입력을 하고 엔터키를 입력하면
입력 데이터 뒤에 /n 이 연이어 붙는다.
/r:Carriage Return 0x0D
/n:new line , line feed 0x0A
———————————————–*/
//카보드로 입력이 들어오면 인터럽트 발생
interrupt [USART0_RXC] void usart0_rx_isr(void) //USART0 수신 완료 인터럽트
{
string[cmd_count++] = UDR0; //rx로 받은 값을 data에 저장
// “m11135+/r”=>motor 2 angle 135
/*
string[0]=‘m’
string[1]=‘1′
string[2]=‘1′
string[3]=‘1′
string[4]=‘3′
string[5]=‘5′
string[6]=‘\r’=0x0d
cmd_count->7
*/
if(string[cmd_count-1] == ‘\r’)
{
string[cmd_count-1] = ”;
//convert to a string. 문자로 하나하나 모았다가 문자열로 변환하기위해서 뒤에 NULL()로 변경
/*
string[0]=‘m’
string[1]=‘1′
string[2]=‘1′
string[3]=‘1′
string[4]=‘3′
string[5]=‘5′
string[6]=‘’=0×00
문자열로 변경된 상태
*/
parseInput(string); //명령 분석
cmd_count = 0;
}
else if(string[cmd_count-1] == ‘c’)// ‘c’+/r
{
string[1]=”; //convert to a string
parseInput(string);
cmd_count = 0;
}
else if(string[cmd_count-1] == ‘e’)// ‘e’+/r
{
string[1]=”; //convert to a string
parseInput(string);
cmd_count = 0;
//EnableTimer0Interrupt();
}
}
void Leg1(int x,int y,int z, int * theta0,int * theta1,int * theta2)
{
int w,v;
float t0,t1,t2;
w=sqrt(x*x+y*y);
v=w-Lc;
t1=atan2(v,z)+acos((La*La+z*z+v*v-Lb*Lb)/(2*La*sqrt(z*z+v*v)));
t2=acos((La*La+Lb*Lb-z*z-v*v)/(2*La*Lb));
t0=atan2(x,y);
*theta0=t0*180/pi+90;
*theta1=90-t1*180/pi;
*theta2=t2*180/pi;
}
void main(void)
{
int i;
char buff[20];
rs232c_initialize();
cmd_count=0;
for(i=0;i<12;i++) DegMotor[i]=90;
ConvertDeg2Cnt();
init_Timer();
sendString(“rs232c connected….\r\n”);
SREG=0×80; //1000 0000 //7 bit : Global Interrupt Enable
Leg1(80,40,-15,&DegMotor[0],&DegMotor[1],&DegMotor[2]);
ConvertDeg2Cnt();
itoa(DegMotor[0],buff);
sendString(buff);
sendString(“\r\n”);
itoa(DegMotor[1],buff);
sendString(buff);
sendString(“\r\n”);
itoa(DegMotor[2],buff);
sendString(buff);
sendString(“\r\n”);
while(1)
{
}
}
CodeVision AVR (모터제어)
#include <mega128.h> //For Atmega128 in Code Vision
#include <delay.h> //For dalay_ms, delay_us
int iTimer0,iTimer2; //Timer initialized variable
int count; //count timer2 overflow interrupt
int Degree,Motor;
interrupt [TIM0_OVF] void timer_int0()
{
PORTC=PORTA=0xff;
count=0;
TIMSK = TIMSK | 0×40; //Timer 2 Interrupt Enable
}
interrupt [TIM2_OVF] void timer_int2()
{
TCNT2=iTimer2; //the initialzed variable for 10usec timer2 overflow interrupt
count++;
if(count == Motor ) PORTA.0=0;
if(count == Motor ) PORTA.1=0;
if(count == Motor) PORTA.2=0;
if(count == Motor) PORTA.3=0;
if(count == Motor) PORTA.4=0;
if(count == Motor) PORTA.5=0;
if(count == Motor) PORTA.6=0;
if(count == Motor) PORTA.7=0;
if(count == Motor) PORTC.0=0;
if(count == Motor) PORTC.1=0;
if(count == Motor) PORTC.2=0;
if(count == Motor) PORTC.3=0;
if(count > 300)
{
TIMSK = TIMSK & 0xbf; //Timer 2 Interrupt disable
}
}
void main(void)
{
iTimer0=0;
//Timer2, input f=16MHz, 160count=10sec is TCNT2=256-160=96
//but from tunning process, the timer2 initialzed variable is 113
iTimer2=113;
DDRC=DDRA=0xff; //Set PORTA all output
PORTC=PORTA=0×00; //Set All PortA Low
TCCR0=0×07; //oc0 not enable, normal, 분주비 1024, pb4 일반포트로 사용
TCCR2=0×01; //0b00000101 //oc2 not enable, normal, 분주비 1, pb7 일반포트로 사용
// 0 0 00 0 010
//TIMSK=0xC3; //timer 0 oVerflow interrupt enable
TIMSK=0×41; //timer 0and 2 overflow interrupt enable
TCNT0=iTimer0; //time 0 초기값
TCNT2=iTimer2;
SREG=0×80; //인터럽트 인에이블
Degree=90;
while(1)
{
Motor=(10.58*Degree+459.4)/10+3;
}
}
Python – 라즈베리파이와 적외선 카메라 연동
1. SUDO RASPI-CONFIG 접속
sudo raspi-config
2. 인터페이스 옵션 설정(카메라활성화)
3. mjpg-streamer 소스 코드를 다운로드 받을 디렉토리를 생성
pi@raspberrypi:~ $ mkdir project
pi@raspberrypi:~ $ cd project
pi@raspberrypi:~/project $
4. 소스 코드를 다운로드 받기위해서 git가 필요
pi@raspberrypi:~/project $ sudo apt-get install git
5. mjpg-streamer 소스 코드를 다운로드
pi@raspberrypi:~/project $ git clone https://github.com/jacksonliam/mjpg-streamer.git
6. mjpg-streamer 소스 코드를 컴파일하기 위해 필요한 패키지를 설치
pi@raspberrypi:~/project $ sudo apt-get install cmake python-imaging libjpeg-dev build-essential
7. mjpg-streamer 소스 디렉토리로 이동하여 컴파일 및 설치를 진행
pi@raspberrypi:~/project $ cd mjpg-streamer/mjpg-streamer-experimental/
pi@raspberrypi:~/project/mjpg-streamer/mjpg-streamer-experimental $ make CMAKE_BUILD_TYPE=Debug
pi@raspberrypi:~/project/mjpg-streamer/mjpg-streamer-experimental $ sudo make install
pi@raspberrypi:~/project/mjpg-streamer/mjpg-streamer-experimental $ cd
pi@raspberrypi:~ $
8. 캠으로부터 캡처한 영상을 HTTP 포트 8090으로 스트리밍
pi@raspberrypi:~ $ mjpg_streamer -i “input_uvc.so” -o “output_http.so -p 8090 -w /usr/local/share/mjpg-streamer/www/”
Python – 라즈베리파이와 적외선센서연동
터미널창에서 state_Value 라는 파이썬 파일 만들기
nano state_Value.py
소스 코드 입력하기(sensor 값 최소 최대설정)
import Rpi.GPIO as GPIO
import time
IR = 7
GPIOIN = 17
GPIOOUT = 27
GPOI.setmode(GPIO.BCM)
print (” motion detection start”)
GPIO.setup(IR, GPIO.IN)
try:
저장후 명령어 python state_Value.py 로 코드 실행
nano state Value.py
python state_Value.py
Visaul Studio 2017(4족 보행로봇 제어)
// QuadrupedRobotControlDlg.cpp: 구현 파일
//
#include “stdafx.h”
#include “QuadrupedRobotControl.h”
#include “QuadrupedRobotControlDlg.h”
#include “afxdialogex.h”
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 응용 프로그램 정보에 사용되는 CAboutDlg 대화 상자입니다.
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 대화 상자 데이터입니다.
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 지원입니다.
// 구현입니다.
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CQuadrupedRobotControlDlg 대화 상자
CQuadrupedRobotControlDlg::CQuadrupedRobotControlDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_QUADRUPEDROBOTCONTROL_DIALOG, pParent)
, m_str_comport(_T(“”))
, m_str_baudrate(_T(“”))
, m_Motor0(90)
, m_Motor1(90)
, m_Motor2(90)
, m_Motor3(90)
, m_Motor4(90)
, m_Motor5(90)
, m_Motor6(90)
, m_Motor7(90)
, m_Motor8(90)
, m_Motor9(90)
, m_Motor10(90)
, m_Motor11(90)
, m_Motion_Sleep(0)
, m_iExecIndex(0)
, m_StepDelay(0)
, m_MotionCommand(_T(“”))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CQuadrupedRobotControlDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_COMBO_COMPORT, m_combo_comport_list);
DDX_Control(pDX, IDC_COMBO_BAUDRATE, m_combo_baudrate_list);
DDX_CBString(pDX, IDC_COMBO_COMPORT, m_str_comport);
DDX_CBString(pDX, IDC_COMBO_BAUDRATE, m_str_baudrate);
DDX_Control(pDX, IDC_EDIT_RCV_VIEW, m_edit_rcv_view);
DDX_Control(pDX, IDC_EDIT_RCV_DATA, m_edit_rcv_data);
DDX_Text(pDX, IDC_MOTOR0, m_Motor0);
DDX_Text(pDX, IDC_MOTOR1, m_Motor1);
DDX_Text(pDX, IDC_MOTOR2, m_Motor2);
DDX_Text(pDX, IDC_MOTOR3, m_Motor3);
DDX_Text(pDX, IDC_MOTOR4, m_Motor4);
DDX_Text(pDX, IDC_MOTOR5, m_Motor5);
DDX_Text(pDX, IDC_MOTOR6, m_Motor6);
DDX_Text(pDX, IDC_MOTOR7, m_Motor7);
DDX_Text(pDX, IDC_MOTOR8, m_Motor8);
DDX_Text(pDX, IDC_MOTOR9, m_Motor9);
DDX_Text(pDX, IDC_MOTOR10, m_Motor10);
DDX_Text(pDX, IDC_MOTOR11, m_Motor11);
DDX_Control(pDX, IDC_EDIT_MOTION_COMMAND, m_Edit_Motion_Command);
DDX_Text(pDX, IDC_MOTION_SLEEP, m_Motion_Sleep);
DDX_Text(pDX, IDC_INDEX, m_iExecIndex);
DDX_Text(pDX, IDC_EDIT_STEPDELAY, m_StepDelay);
DDX_Text(pDX, IDC_EDIT_MOTION_COMMAND, m_MotionCommand);
}
BEGIN_MESSAGE_MAP(CQuadrupedRobotControlDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_MYCLOSE, &CQuadrupedRobotControlDlg::OnThreadClosed)
ON_MESSAGE(WM_MYRECEIVE, &CQuadrupedRobotControlDlg::OnReceive)
ON_BN_CLICKED(IDC_BT_CONNECT, &CQuadrupedRobotControlDlg::OnBnClickedBtConnect)
ON_BN_CLICKED(IDC_BT_CLEAR, &CQuadrupedRobotControlDlg::OnBnClickedBtClear)
ON_CBN_SELCHANGE(IDC_COMBO_COMPORT, &CQuadrupedRobotControlDlg::OnCbnSelchangeComboComport)
ON_CBN_SELCHANGE(IDC_COMBO_BAUDRATE, &CQuadrupedRobotControlDlg::OnCbnSelchangeComboBaudrate)
ON_BN_CLICKED(IDC_BT_SEND, &CQuadrupedRobotControlDlg::OnBnClickedBtSend)
ON_BN_CLICKED(IDC_BT_MOTION, &CQuadrupedRobotControlDlg::OnBnClickedBtMotion)
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_MOTION, &CQuadrupedRobotControlDlg::OnDeltaposSpinMotion)
ON_BN_CLICKED(IDC_BT_EXECUTION, &CQuadrupedRobotControlDlg::OnBnClickedBtExecution)
ON_BN_CLICKED(IDC_BT_CONT_EXECUTION, &CQuadrupedRobotControlDlg::OnBnClickedBtContExecution)
ON_BN_CLICKED(IDC_BT_SEND2, &CQuadrupedRobotControlDlg::OnBnClickedBtSend2)
ON_BN_CLICKED(IDC_OPEN, &CQuadrupedRobotControlDlg::OnBnClickedOpen)
ON_BN_CLICKED(IDC_LOAD, &CQuadrupedRobotControlDlg::OnBnClickedLoad)
END_MESSAGE_MAP()
// CQuadrupedRobotControlDlg 메시지 처리기
BOOL CQuadrupedRobotControlDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 시스템 메뉴에 “정보…” 메뉴 항목을 추가합니다.
// IDM_ABOUTBOX는 시스템 명령 범위에 있어야 합니다.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 이 대화 상자의 아이콘을 설정합니다. 응용 프로그램의 주 창이 대화 상자가 아닐 경우에는
// 프레임워크가 이 작업을 자동으로 수행합니다.
SetIcon(m_hIcon, TRUE); // 큰 아이콘을 설정합니다.
SetIcon(m_hIcon, FALSE); // 작은 아이콘을 설정합니다.
// TODO: 여기에 추가 초기화 작업을 추가합니다.
m_combo_comport_list.AddString(_T(“COM1″));
m_combo_comport_list.AddString(_T(“COM2″));
m_combo_comport_list.AddString(_T(“COM3″));
m_combo_comport_list.AddString(_T(“COM4″));
m_combo_comport_list.AddString(_T(“COM7″));
m_combo_comport_list.AddString(_T(“COM9″));
m_combo_comport_list.AddString(_T(“COM10″));
m_combo_comport_list.AddString(_T(“COM13″));
m_combo_comport_list.AddString(_T(“COM14″));
m_combo_comport_list.AddString(_T(“COM16″));
m_combo_baudrate_list.AddString(_T(“1200″));
m_combo_baudrate_list.AddString(_T(“9600″));
m_combo_baudrate_list.AddString(_T(“19200″));
m_combo_baudrate_list.AddString(_T(“38400″));
m_combo_baudrate_list.AddString(_T(“115200″));
comport_state = false;
GetDlgItem(IDC_BT_CONNECT)->SetWindowText(_T(“OPEN”));
m_str_comport = _T(“COM7″);
m_str_baudrate = _T(“9600″);
m_iExecIndex=iExecindex = 0;
m_StepDelay = 500;
UpdateData(FALSE);
return TRUE; // 포커스를 컨트롤에 설정하지 않으면 TRUE를 반환합니다.
}
void CQuadrupedRobotControlDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 대화 상자에 최소화 단추를 추가할 경우 아이콘을 그리려면
// 아래 코드가 필요합니다. 문서/뷰 모델을 사용하는 MFC 응용 프로그램의 경우에는
// 프레임워크에서 이 작업을 자동으로 수행합니다.
void CQuadrupedRobotControlDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 그리기를 위한 디바이스 컨텍스트입니다.
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 클라이언트 사각형에서 아이콘을 가운데에 맞춥니다.
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() – cxIcon + 1) / 2;
int y = (rect.Height() – cyIcon + 1) / 2;
// 아이콘을 그립니다.
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// 사용자가 최소화된 창을 끄는 동안에 커서가 표시되도록 시스템에서
// 이 함수를 호출합니다.
HCURSOR CQuadrupedRobotControlDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
LRESULT CQuadrupedRobotControlDlg::OnThreadClosed(WPARAM length, LPARAM lpara)
{
// overlapped i/o 핸들을 닫는다
((Ccomm*)lpara)->HandleClose();
delete ((Ccomm*)lpara);
return 0;
}
LRESULT CQuadrupedRobotControlDlg::OnReceive(WPARAM length, LPARAM lpara)
{
CString str;
char data[20000];
if (m_comm)
{
m_comm->Receive(data, length); // length 길이 만큼 데이터를 받는다.
data[length] = _T(”);
for (int i = 0; i < length; i++)
{
str += data[i];
}
m_edit_rcv_view.ReplaceSel(str); // 에디터 박스에 표시하기 위함
str = _T(“”);
m_edit_rcv_view.LineScroll(m_edit_rcv_view.GetLineCount()); // 화면이 넘어가면 우측 스크롤을 맨 아래로 내려 주는 역할
}
return 0;
}
void CQuadrupedRobotControlDlg::OnBnClickedBtConnect()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
if (!comport_state) // Comport가 없다면..
{
m_comm = new Ccomm(_T(“\\\\.\\”) + m_str_comport, m_str_baudrate,
_T(“None”), _T(“8 Bit”), _T(“1 Bit”)); // initial Comm port
if (m_comm->Create(GetSafeHwnd()) != 0) // 통신 포트를 열고 윈도우의 핸들을 넘긴다.
{
AfxMessageBox(_T(“COM 포트열림”));
comport_state = true;
GetDlgItem(IDC_BT_CONNECT)->SetWindowText(_T(“CLOSE”));
GetDlgItem(IDC_BT_SEND)->EnableWindow(true);
}
else
{
AfxMessageBox(_T(“ERROR!”));
}
}
else
{
if (m_comm) // Comport가 존재하면..
{
m_comm->Close();
m_comm = NULL;
AfxMessageBox(_T(“COM 포트닫힘”));
comport_state = false;
GetDlgItem(IDC_BT_CONNECT)->SetWindowText(_T(“OPEN”));
GetDlgItem(IDC_BT_SEND)->EnableWindow(false);
}
}
}
void CQuadrupedRobotControlDlg::OnBnClickedBtClear()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
GetDlgItem(IDC_EDIT_RCV_VIEW)->SetWindowText(_T(” “));
}
void CQuadrupedRobotControlDlg::OnCbnSelchangeComboComport()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData();
}
void CQuadrupedRobotControlDlg::OnCbnSelchangeComboBaudrate()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData();
}
void CQuadrupedRobotControlDlg::OnBnClickedBtSend()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString str;
int i;
GetDlgItem(IDC_EDIT_RCV_DATA)->GetWindowText(str);
str += _T(“\r”);; //문장 뒤에 ’\r’ carriage return 을 붙여 명령 끝을 알린다.
//m_comm->Send(str, str.GetLength());
char* ss = LPSTR(LPCTSTR(str)); //CString을 char 형으로 변환
/*마이콤에서 해독하는 데 일정 시간이 소요되어 마치 키보드 입력처럼 명령사이에
지연시간을 준다.
또한 CString을 char로 변환시 하나의 문자가 2바이트로 변환되며 두번쨰는 비어있다.
예로
CString “2345: -> “2″,”",”3″,”",”4″,”",”5″,”"
*/
for (i = 0; i < str.GetLength(); i++)
{
m_comm->Send(ss, 1);
ss = ss + 2; //2 byte offset
Sleep(5);
}
}
void CQuadrupedRobotControlDlg::OnBnClickedBtSend2()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString str;
int i;
GetDlgItem(IDC_EDIT_RCV_DATA)->GetWindowText(str);
char* ss = LPSTR(LPCTSTR(str)); //CString을 char 형으로 변환
for (i = 0; i < str.GetLength(); i++)
{
m_comm->Send(ss, 1);
ss = ss + 2; //2 byte offset
Sleep(5);
}
}
void CQuadrupedRobotControlDlg::OnBnClickedBtMotion()
{
int i;
char buf[30];
char * ss;
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData(true); //read parameter
if (m_Motion_Sleep == 0) //Motion Command
{
buf[0] = ‘d’;
buf[1] = m_Motor0;
buf[2] = m_Motor1;
buf[3] = m_Motor2;
buf[4] = m_Motor3;
buf[5] = m_Motor4;
buf[6] = m_Motor5;
buf[7] = m_Motor6;
buf[8] = m_Motor7;
buf[9] = m_Motor8;
buf[10] = m_Motor9;
buf[11] = m_Motor10;
buf[12] = m_Motor11;
buf[13] = ‘\r’;
ss = buf;
for (i = 0; i < 14; i++)
{
m_comm->Send(ss, 1);
ss++;
Sleep(5); //실험으로 정함 매우중요
}
}
else //Sleep Command
{
buf[0] = ‘s’;
buf[1] = m_Motion_Sleep;
buf[2] = ‘\r’;
ss = buf;
for (i = 0; i < 3; i++)
{
m_comm->Send(ss, 1);
ss++;
Sleep(5);
}
}
//m_comm->Send((char *)buf, strlen((char *)buf));
}
void CQuadrupedRobotControlDlg::OnDeltaposSpinMotion(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
if (pNMUpDown->iDelta > 0)
{
iExecindex–;
if (iExecindex < 0) iExecindex = 0;
}
else
{
iExecindex++;
}
m_iExecIndex = iExecindex;
UpdateData(false);
*pResult = 0;
}
/*
CString -> int 변환, int->CString 변환
CString → int
int형 = _ttoi(CString형);
int → CString
CString형.Format(_T(“%d”), int형);
*/
void CQuadrupedRobotControlDlg::OnBnClickedBtExecution()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString str, strBuff;
CString mCode;
CString mMotor0, mMotor1, mMotor2, mMotor3, mMotor4, mMotor5, mMotor6, mMotor7, mMotor8, mMotor9, mMotor10, mMotor11, mSleep;
UpdateData(true); //read parameter
GetDlgItem(IDC_EDIT_MOTION_COMMAND)->GetWindowText(str);
if (AfxExtractSubString(strBuff, str, m_iExecIndex, ‘\n’))
{
mCode = strBuff.Left(1);
if (mCode.Compare(_T(“d”)) == 0)
{
AfxExtractSubString(mMotor0, strBuff, 1, ‘,’);
AfxExtractSubString(mMotor1, strBuff, 2, ‘,’);
AfxExtractSubString(mMotor2, strBuff, 3, ‘,’);
AfxExtractSubString(mMotor3, strBuff, 4, ‘,’);
AfxExtractSubString(mMotor4, strBuff, 5, ‘,’);
AfxExtractSubString(mMotor5, strBuff, 6, ‘,’);
AfxExtractSubString(mMotor6, strBuff, 7, ‘,’);
AfxExtractSubString(mMotor7, strBuff, 8, ‘,’);
AfxExtractSubString(mMotor8, strBuff, 9, ‘,’);
AfxExtractSubString(mMotor9, strBuff, 10, ‘,’);
AfxExtractSubString(mMotor10, strBuff, 11, ‘,’);
AfxExtractSubString(mMotor11, strBuff, 12, ‘,’);
m_Motor0 = _ttoi(mMotor0);
m_Motor1 = _ttoi(mMotor1);
m_Motor2 = _ttoi(mMotor2);
m_Motor3 = _ttoi(mMotor3);
m_Motor4 = _ttoi(mMotor4);
m_Motor5 = _ttoi(mMotor5);
m_Motor6 = _ttoi(mMotor6);
m_Motor7 = _ttoi(mMotor7);
m_Motor8 = _ttoi(mMotor8);
m_Motor9 = _ttoi(mMotor9);
m_Motor10 = _ttoi(mMotor10);
m_Motor11 = _ttoi(mMotor11);
m_Motion_Sleep = 0;
}
else if (mCode.Compare(_T(“s”)) == 0)
{
AfxExtractSubString(mSleep, strBuff, 1, ‘,’);
m_Motion_Sleep = _ttoi(mSleep);
}
else
{
}
m_iExecIndex++;
}
UpdateData(false);
OnBnClickedBtMotion();
//출처: https://bigmark.tistory.com/11 [마크의 맥시멈 라이프]
}
void CQuadrupedRobotControlDlg::OnBnClickedBtContExecution()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString str, strBuff;
m_iExecIndex = 0;
UpdateData(true); //read parameter
GetDlgItem(IDC_EDIT_MOTION_COMMAND)->GetWindowText(str);
while(AfxExtractSubString(strBuff, str, m_iExecIndex, ‘\n’))
{
OnBnClickedBtExecution();
Sleep(m_StepDelay);
}
m_iExecIndex = 0;
UpdateData(false);
}
void CQuadrupedRobotControlDlg::OnBnClickedOpen()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString m_strStatus,tmp;
CString m_strPath;
CStdioFile file;
// CFile file;
CFileException ex;
CFileDialog dlg(TRUE, _T(“*.txt”), NULL, OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT, _T(“Motion Files(*.txt)|*.txt|”), NULL);
if (dlg.DoModal() == IDOK)
{
m_strPath = dlg.GetPathName();
if (m_strPath.Right(4) != “.txt”)
{
m_strPath += “.txt”;
}
file.Open(m_strPath, CFile::modeRead, &ex);
while (file.ReadString(tmp))
m_MotionCommand += tmp + _T(“\n”);
file.Close();
//http://blog.naver.com/PostView.nhn?blogId=kan0909&logNo=90138161650
UpdateData(false);
}
}
void CQuadrupedRobotControlDlg::OnBnClickedLoad()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
CString m_strStatus;
CString m_strPath;
CStdioFile file;
// CFile file;
CFileException ex;
CFileDialog dlg(FALSE, _T(“*.txt”), NULL, OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT, _T(“Motion Files(*.txt)|*.txt|”), NULL);
if (dlg.DoModal() == IDOK)
{
m_strPath = dlg.GetPathName();
if (m_strPath.Right(4) != “.txt”)
{
m_strPath += “.txt”;
}
file.Open(m_strPath, CFile::modeCreate | CFile::modeReadWrite | CFile::modeRead, &ex);
GetDlgItem(IDC_EDIT_MOTION_COMMAND)->GetWindowText(m_strStatus);
file.WriteString(m_strStatus);
file.Close();
}
}
[66호]WORLD IT SHOW 2021
WORLD IT SHOW 2021
글 | 이규연 기자 kylee@ntrex.co.kr
과학기술정보통신부가 최신 전자, 정보 통신 기술과 제품·서비스를 전시·체험할 수 있는 월드 IT 쇼 2021 전시회를 지난 4월 21일부터 23일까지 서울 코엑스에서 개최하였다. 올해 월드 IT 쇼는 디지털 뉴딜 행사 중에서 규모가 가장 컸으며 총 305개사 900여 부스가 운영됐다.
이번 행사의 슬로건은 ‘5G 날개를 달고 디지털 뉴딜을 펼치다’이며 주요 전시품목은 코로나19로 인해 수요가 증가한 비대면 교육 및 업무솔루션(Un-tact Technology)과 인공지능(AI), 사물인터넷(IoT) 점점 커지는 1인 미디어 시대에 맞는 스마트 장비(Early tech Adopter)와 지능형 ICT 융합 서비스의 제품이다.
(주)메이는 웨이퍼 위에 액정표시장치(LCD)를 올리는 마이크로 디스플레이의 한 종류인 LCOS(실리콘 액정 표시 방식) 패널 제조업체로 이를 활용한 다양한 제품 개발 및 판매 중에 있으며 국내 유일하게 생산 라인을 가지고 있다. 메이의 주력상품은 4K 초고화질 패널을 사용한 VR/AR 겸용 헤드셋과 4K 프로젝터, 홀로그램이며 VR Simulator X-DRIVING 과 4K VR/AR HMD 기기를 통해 롤러코스터 체험을 할 수 있게 전시했다.
(주)엑소아틀레트아시아는 웨어러블 로봇 전문 기업으로 혁신적인 웨어러블 로봇 기술을 활용하여 하지 보행 환자의 효율적 재활치료를 위한 웨어러블 의료재활로봇, 근력 약화 노약자와 편마비 환자들의 가정 내 언택트(Untact) 보행 훈련을 위한 보행보조로봇을 개발한다.
금번 전시회에 소개한 제품은 엑소아틀레트 II 제품이며 유럽 CE 인증과 국내 의료기기 인증을 취득하였고, 더 빠르고 편리한 제품 착용과 탈착이 가능해진 사용 편리성, 그리고 한층 세련된 제품 디자인이 그 특징이다. 또한 환자가 로봇의 무게감을 크게 느끼지 못하도록 설계되었고 인체 공학적 디자인과 자연스러운 보행패턴을 탑재한 웨어러블 재활 로봇이다.
(주)엑사 로보틱스는 국내외 우수 기업들과 첨단 기술의 공동 개발 및 연구를 통해 인공지능 로봇, lOT 시스템, 통합 관제 시스템 기술을 융합하여 신개념 세계 최초 스마트 빌딩 솔루션을 공급하는 기업이다.
전시된 자율 주행 로봇의 이름은 ‘코리’이며 자율 주행 기술의 다양한 모듈을 장착할 수 있고 모듈에 따라 적재적소에 최적화된 서비스 로봇으로 변신이 가능하다. 이번 전시회에서 선보인 약 20종의 자율 주행 로봇 코리 라인업은 건물 내에서 사용하는 실내용과 건물 내에서 안내하는 인도형 로봇이 주를 이뤘다.
(주)딥인사이트는 인공지능 기술과 3D 심도를 개발해 3D 카메라 솔루션을 공급하는 업체이다. 이번 전시회에서는 AI 기술 기반 차량 실내 3D 센싱 카메라 (Deep in View) 제품을 선보였다. 차량 실내 3D 센싱 카메라는 자율주행차 스마트카에서 활용할 수 있는 제품으로 차량 실내 운전자와 승객을 비추는 감시 기반 센싱 카메라이다. 주행 중 위험 상황을 통합적으로 판단하는 경보 시스템이 있으며 운전자 및 승객의 상태 정보를 3D 카메라로 확인하여 실시간으로 분석할 수 있는 시스템이다.
차량 실내용 3D 카메라는 ToF(비행시간 측정) 방식이며, 광선파를 발사한 뒤 되돌아오는 광선파를 받아 ‘걸린 시간 또는 위상 차이’ 토대로 파악한다.
(주)드림팜 솔루션은 일반 가정에서 식물을 재배하기 위한 재배기 제품을 개발과 식물 재배에 필요한 온, 습도를 콘트롤 할 수 있는 소프트웨어를 개발하는 업체이며, 전시회에서 소개한 제품은 하우스 팜이다.
식물재배기와 크라우드 서버를 연결하여 재배환경의 지속적 모니터링이 가능한 스마트 팜이며 다양한 시스템이 설계되어 있다.
온습도 모니터링과 조도에 따른 자동 조명 on/off 시스템, 적시 광물 공급을 위한 PH 센서 탑재, 효율적인 LED 광원을 통해 최소 비용으로 최대 일광 조명을 제공한다. 현재까지 야채 6종, 허브 6종, 꽃 6종 등을 재배에 성공 시켰으며, 추후에 더 많은 식물 재배를 성공할 것으로 기대된다.
(주)일루 베이션은 3D 스캐닝 기술과 영상처리 기술을 활용한 새로운 형태의 비접촉식 양돈 모바일 체중 관리기를 개발하는 업체이다.
일루 베이션의 주력 제품은 양돈 모바일 체중 관리기(Viiew) 로써 돈사 내에서 체중을 측정하여 95% 이상의 정확도로 최적의 출하시기를 예측하고, 105~125kg의 규격돈을 선별하여 출하가 가능하며 촬영부터 체중 확인까지 단 10초가 걸린다. 앞으로의 양돈 농가뿐만 아닌 축산 농가 전체에서도 사용될 수 있도록 기대해본다.
(주)퓨리움은 세계 최초로 인공지능 스마트 loT 에어샤워를 개발 및 특허 등록, 상품화에 성공한 기업이다. 금번 전시회에서 주력상품인 인공지능 스마트 loT 에어샤워 게이트를 선보였다.
일상생활에서 누구나 이용이 가능하며 사용자의 키에 따라 적합하게 에어 샤워 기능이 작동하며 스마트 loT 센싱 기술로 실내 공기 질 상태를 24시간 감시하고 분석하여 공간에 맞게 공기 청정 기능을 수행한다. 외부 사람이 출입할 때 에어샷으로 미세먼지를 털어내고 외부 유해 물질이 내부로 유입되는 걸 막아주며 친환경 UV-Dual LED와 천연 피톤치드를 이용하여 공기를 항균 및 탈취하므로 인체에 무해하고 다양한 형태의 에어샤워 게이트를 고객의 니즈에 맞추어 제작할 수 있다.
(주)룰루랩은 삼성전자 C-Lab에서 스핀오프한 기업으로, AI 및 빅데이터 기술을 활용하여 피부를 분석하고 각 고객별 최적의 화장품을 추천해 주는 솔루션을 개발하는 업체이다. 룰루랩은 인공지능 기반의 피부 데이터 활용 가치를 인정받아 세계 최대 IT 전시회 CES 연사 및 3년 연속 혁신상을 수상하였다.
금번 전시회에서 선보인 주력 제품은 루미니 키오스크 V2이며, 이 키오스크는 카메라가 영상을 찍고 AI가 촬영한 영상을 토대로 피부를 진단하는 방식이다. 10초 남짓한 짧은 시간 동안 피부 분석을 통해 어떤 화장품을 사용해야 하는지 추천까지 해준다. 피부 분석은 모공, 피지, 홍조, 잡티, 주름, 여드름, 유수분 등 7가지 항목을 점검할 수 있다.
3층 C 홀에서는 삼성전자, LG전자, SKT, KT, 현대 자동차 등 대기업들의 ICT 전시회가 열리고 있어 볼거리가 풍성했다.
먼저 3층에 들어서자마자 사람들의 이목을 집중시킨 것은 SK Telecom의 5G METAVERSE VR CINEMA였다. VR HMD를 착용하고 놀이기구를 통해 조금 더 실감 나는 가상현실 체험을 할 수 있다.
대기하는 인원이 많아 가장 인기가 좋았던 부스 중 하나였다.
점프 스튜디오는 아시아 최초의 메타버스 제작시설이다. 무려 100대가 넘는 카메라를 사방에 배치해서 사람의 움직임을 촬영하여 사람 형태의 디지털 홀로그램을 보여준다. 점프 스튜디오가 구현한 홀로그램은 기존 VR, AR 보다 더 생동감 있는 영상을 제공한다. 또한 이렇게 형성된 홀로그램을 점프 AR 애플리케이션으로 바로 만나 볼 수 있다.
그 다음 사람들의 관심을 끈 건 바로 현대자동차의 전기차 아이오닉5이며, 아이오닉 5는 현대자동차 전기차 전용 플랫폼 ‘E-GMP’를 적용한 차량이다. 운전자의 스타일에 맞춰 인테리어 부품과 하드웨어 기기 등을 구성할 수 있는 ‘스타일 셋 프리’를 탑재해 관람객들의 관심을 더 끌었다.
또한 아이오닉 5에는 세계 최초로 다양한 충전 인프라를 이용할 수 있는 400V/800V 멀티 급속 충전 시스템을 적용해 800V 초고속 충전과 일반 400V 충전기도 사용할 수 있게 편리성을 극대화했고, 4분 30초만 충전해도 100km 이상 주행할 수 있으며 1회 충전으로 430km를 주행할 수 있다.
LG 전자는 IT 제품, 로봇 서비스 솔루션, 맞춤형 산업용 디스플레이 솔루션 등을 소개했다. 그 중 관람객들에게 인기가 좋았던 커넥티드 카는 집에서 즐기던 콘텐츠를 차 안에 탑재되어 있는 올레드 디스플레이에서 이어서 시청할 수 있고 차 안에서 집안의 가전 등을 제어할 수 있으며 의류, 냉장 기능을 갖춘 가전도 탑재했다.
KT는 ‘ABC 기반 디지털 혁신 선도 기업’ 디지코(DIGICO) 라는 이름으로 전시하였고 총 7개 구역에서 인공지능, 빅데이터, 클라우드 등 ABC 기술을 한눈에 볼 수 있었다.
통신 3사 최초로 AP 개발을 마친 ‘와이파이 6E’와 구독형 클라우드 게임 서비스, 차세대 지능형 교통 체계(C-ITS)를 선보였으며 코로나19로 수요가 증가된 언택트 일상의 알맞은 솔루션도 소개했다.
삼성전자는 최신 가전과 모바일, IT 기기 위주로 선보였다. ‘마이크로 LED’, ‘Neo QLED 8K’ 등 고화질을 자랑하는 프리미엄 TV, 냉장고 등 다양한 가전제품이 전시되었고 그중 마이크로 LED는 현존 최고의 디스플레이 기술이 집약된 제품으로 ‘CES 2021’에서 최고 혁신상을 수상했다.
가장 눈에 띄는 모바일 제품으로는 초고화질 카메라와 깔끔한 디자인으로 사랑받는 갤럭시 S21 시리즈와 혁신적인 디자인을 자랑하는 폴더블폰 갤럭시 Z 폴드 2와 갤럭시 Z 플립 5G를 볼 수 있었다.
뿐만 아니라 360도 회전 가능한 터치 디스플레이와 스마트 S펜을 탑재해 최상의 사용성을 제공하는 2-in-1 노트북 ‘갤럭시 북 플렉스 2, 더 커진 11mm 우퍼, 6.5mm 트위터의 2-way 다이나믹 스피커와 진화한 인텔리전트 ANC를 탑재한 무선 이어폰 ‘갤럭시 버즈 프로’도 눈길을 끌었다.
화웨이는 이번 전시를 통해 유무선 네트워크 솔루션, 화웨이 스토리지와 엔터프라이즈 솔루션 등 다양한 솔루션을 소개했다.
유무선 네트워크 솔루션으로는 고품질의 5G 서비스를 이용할 수 있는 디지털 인도어 솔루션인 램프 사이트와 리퀴드-OTN AR 라우터 eAL WIFI가 있고 리퀴드-OTN은 화웨이가 대역폭 조절을 매끄럽게 하기 위해 개발한 솔루션이다.
화웨이는 회의에 필요한 지능형 협업 솔루션 아이디어 허브도 선보였는데 뛰어난 기술에 슬림한 디자인으로 완성형이 높은 제품이다.
또한, 이번 전시 솔루션을 스마트 제조, 다이닝, 헬스케어, 금융, 교육, 등 다양한 업계에 제공할 예정이다.
올해 월드 IT 쇼는 작년 2020년 코로나19로 인해 개최가 취소되어 아쉬움이 많은 사람들에게 보란 듯이 좋은 아이템과 향상된 기술력으로 실망시키지 않은 전시회였다. 그 중심에는 대기업이 있겠지만 중소기업과 스타트업의 성장을 빼놓지 않을 수 없다.
내년까지 코로나19가 지속된다면 언택트 시대에 맞는 교육 솔루션과 온라인 방송을 위한 디지털 미디어 장비가 계속해서 개발될 것이다.
온라인 디지털 솔루션도 개발되면 좋지만 하루 빨리 코로나19가 종식되어 인공지능, 사물인터넷 등 스마트 장비를 오프라인에서 사용할 수 있기를 바라본다. DM
[66호]2021 ICT 융합 프로젝트 공모전 결과 발표 및 시상 소식
2021 ICT 융합 프로젝트 공모전
결과 발표 및 시상 소식
글 | 편집부 press@ntrex.co.kr
전자 로봇, 기계 분야 중 자유 주제로 2021년 2월 1일부터 3월 31일까지 진행된 2021 ICT 융합 프로젝트 공모전 발표와 시상이 지난 6월 2일 (주)엔티렉스 본사 대회의실에서 진행되었습니다.
이번 2021 ICT 융합 프로젝트 공모전에는 총 146개 팀 및 개인이 응모하여, 이 중 28개 작품을 최종 선정하였으며, 대상은 호서대학교 박정준, 김진우, 박정섭, 김선혁, 장서윤, 이성용, 이준용이 응모한 “오목AI 베타오”가 수상했습니다.
대상을 수상한 호서대학교 박정준, 김진우, 박정섭, 김선혁, 장서윤, 이성용, 이준용님은 본사를 방문하여 상장과 상금을 수령하고 간단한 인터뷰 시간을 가졌습니다.
올해는 특별히 더 많은 인원이 참여해 주셔서 이에 보답하고자 대상, 최우수, 우수 작품으로 선정된 전 인원(팀원 모두)에게 디바이스마트 인기 상품인 강화유리 커팅 매트를 상장 외 특별상품으로 지급했습니다.
이번 공모전 심사는 (주) 엔티렉스, 위드로봇(주), (주)칩센, (주)펌테크, (주)뉴티씨에서 심사를 담당하였고, 심사 결과는 하단에서 확인할 수 있습니다.
이번 공모전에 수상하신 모든 분들께 축하의 말씀을 드리며, 참여하신 심사 업체 및 지원자분들에게 감사의 인사를 드립니다.
수상작들에 대한 내용은 다음 디바이스마트 매거진을 통해 소개하고자 하며, 내년 2월에 진행될 다음 대회에도 많은 참여와 관심 부탁드립니다. DM
2021 ICT 융합 프로젝트 공모전 심사결과
구분 | 수상자 | 작품명 | 시상
내역 |
대상 | 호서대학교 박정준, 김진우, 박정섭, 김선혁, 장서윤, 이성용, 이준용 |
오목AI 베타오 | 100만원 |
최우수상 | 숭실대학교 김성호, 이경주, 이하늘 | LoRa통신 기반의 오픈소스 IoT플랫폼을 활용한 스마트 공원 관리: 스마트 파크(Smart Park) |
50만원 |
광운대학교 최예지, 최혁순 | 메타버스를 이용한 문서 보안 시스템 | 50만원 | |
우수상 | 대한상공회의소 서울기술교육센터 한승진, 김연진, 신상우, 유종선, 진민경 |
일회용품을 사용하지 않는 친환경 자판기 (Eco-friendly Vending Machine) |
20만원 |
건양대학교 장건호, 이지훈 | 간호사의 업무 부담을 줄여주는 EMR연동 vital sign계측 시스템 |
20만원 | |
포항공과대학교 이도현 | 메카넘휠과 2D lidar를 이용한 자율주행 로봇 | 20만원 | |
영남대학교 양성은, 박유나, 이유진, 김형덕 | 스마트 쇼핑 카트 | 20만원 | |
숭실대학교 오세찬, 강전완, 윤형섭, 임소현 | 투명 폐페트병 분리배출 로봇 | 20만원 | |
장려상
|
한양대학교 (ERICA) 윤우성, 김대완, 김홍진, 이건이, 한대희, 임동건, 제선명, 김정한, 김성중, 김민서 |
펭듀 | 10만원 |
세종대학교 권재연 | 쿠킹 헬퍼(Cooking Helper) | 10만원 | |
연세대학교 장다운, 차현호 | KeyGuard | 10만원 | |
한국외국어대학교 김동영, 김소연, 윤현빈, 채희경 | 스마트 IoT 스토어 | 10만원 | |
김영모 | 블루투스 링거 알림 시스템 | 10만원 | |
부산대학교 최재호, 민경석, 박태훈, 안영준 | Si Rain | 10만원 | |
대구대학교 김 현, 김동혁, 이휘준, 김완기 | 독거노인 안심 케어 시스템 | 10만원 | |
금오공과대학교 백민재, 숭실대학교 유준하, 서울과학기술대학교 박재우 |
확진자 방역 소독을 위한 4족 보행로봇 | 10만원 | |
국민대학교 노문호, 김준호, 문형석 | 버스 뒷문 옷 끼임 방지 시스템 | 10만원 | |
세종대학교 구나영, 서울과학기술대학교 김석림, 동국대학교 노영준, 중앙대학교 송혜령, 한국산업기술대학교 이동혁, 경기대학교 임영주 |
돕고노인 | 10만원 | |
동의대학교 김가영, 류민주, 배동훈 | 체온을 재면 손 소독제가 나오는 장치 | 10만원 | |
부산대학교 정창기, 엄아영, 이창훈, 박영준 | AI비전센서 운반로봇 | 10만원 | |
중앙대학교 환순민, 김주영, 김진엽, 이지우 | CO, tail (코테일) | 10만원 | |
한성대학교 양미연, 이영상, 김준연, 홍제호 | MOdoUSE | 10만원 | |
순천향대학교 황예원, 홍기영, 김연주, 김유청 | 바쁜 생활 속 반려견과 함께하는 이동식 건강 지킴이 | 10만원 | |
전남대학교 배준영, 박상준, 포항공과대학교 조성언 | Dobi | 10만원 | |
단국대학교 김민우, 박상재, 김원석 | SLAM 및 군집을 이용한 호텔 서비스 로봇 | 10만원 | |
이화여자대학교 김연의, 김희연, 안하린, 우혜민, 이지영 | SMART BUS (Smart bus with Mobility All people can Ride – Technology) |
10만원 | |
국민대학교 이강민, 아주대학교 장성용, 평택대학교 박인서, 백석대학교 박재현, 건국대학교 고건 |
시각장애인을 위한 스마트 분리수거함 | 10만원 | |
부산대학교 김민지, 박서정, 염희수 | Hear is your seat | 10만원 |