[65호] 리드샤인 모터 신규 입고 및 대폭 할인!
리드 샤인
리드 샤인 모터 신규 입고 및 대폭 할인!
리드 샤인 (LeadShine Technology Co., Ltd)은 중국 심천에 본사를 둔 스텝 모터 분야의 리딩 기업으로 다양한 통신 규격을 지원하는 모션 컨트롤러, 서보 드라이버와 모터를 생산하고 있습니다. 높은 성능과 신뢰성 및 합리적인 가격을 얻은 결과 높은 성능과 신뢰성 및 합리적인 가격을 확보 한 경쟁없는 연구와 기술 혁신을 전 세계 시장에서 긍정적 인 반응을 끌어 냈으며 5 대륙에있는 글로벌 대기업 및 강소 고객사에 특화된 모션 컨트롤 솔루션을 제공합니다. 리드 샤인의 R & D 팀은 모션 컨트롤 업계 최상위 권에 설립자 Warrem Li 박사가 직접 연구 개발을 이끌고 100여 명 이상의 공학 박사가 함께한다. 당사의 공장은 ISO-9001 인증을 기초로 최고의 제조 장비와 시스템을 통해 운영되고 있고 정확한 설계와 정밀한 제조 과정을 통과 한 제품은 의료 기기, 조립 및 검사 장비 및 CNC 라우터, 머신 머신, 조각기 등 다 양한 산업군에서 문서있다. 리드 샤인의 표준형 제품은 전반적인 시장의 니즈를 눈금 한 조사와 이후 최적화 된 제품 제공한다. 디바이스 마트에 AC, DC, 스텝 모터 등이 다양하게 사용 용도에 맞게 선택하여 사용할 수 있습니다. 또한, 50 % 이상으로 대폭 할인까지 진행하니 서둘러 확인해 보는 것을 추천한다. 한정 수량으로 진행되는 이벤트 신속하게 품절 될 수 있음을 유의하길 다만.
[65호] 블루투스 비콘 기술 기반의 BuildThing(빌드씽) IAQ IoT 공기질 측정기
빌드잇
블루투스 비콘 기술 기반의 BuildThing (빌드 씽) IAQ IoT 공기질 측정기
실내에서 발생하는 유해 물질은 작게는 두통에서 크게는 폐암까지 천차만별의 결과를 초래한다. 철저한 실내 공기질 관리가 중요시 되는 요즘, 멀티 센서 비콘 및 IoT 하드웨어/ 소프트웨어 솔루션 구축 전문 기업 ‘빌드잇’ 에서는 미세먼지 간이 측정기 성능 1등급 공 기질 측정기 BuildThing IAQ를 출시하였다. IAQ는 저전력 블루투스(BLE5.1) 비콘 기술 기반으로 우리에게 가장 친숙한 기기인 스마트폰을 통해 반경 70m에서 실내 공기질 측정 정보를 확인할 수 있으며 미세먼지 외에 눈에 보이지 않는 이산화탄소, 곰팡 이, 휘발성 유기화합물 등 다양한 요소를 하나의 기기로 모두 확인이 가능하다. 또한 가로 5.4cm, 세로 5.4cm, 높이 3.2cm의 초소형 사이즈와 83.5g 무게로 USB-C 타입 연결을 통한 거치형 설치 방식과 전용 브라켓을 사용한 벽면/천장 부착형 설치 방식을 모두 지원한다. 완벽한 정확도로 성능 1등급 인 증이 완료되었으며 LED 램프를 통해 공기질 상태가 표시되고 BuildThing Gateway의 연동을 통해 IAQ가 설치된 공간의 원격 실내 공기질 모니터링이 가능하도록 Admin 관리자용 앱과 Cloud 서비스를 제공한다. IAQ는 기존 IoT 실내 공기질 측정기와 다 르게 디스플레이, 인터넷 통신부, 조작부 등을 제거하여 실내 공기질 모니터링 솔루션으로서 높은 가성비를 제공한다. 공기질 측정기 30대 설치 기준 타사 대비 약 43% 가격 절감을 입증했으며 설치 개수가 많을수록 가격 경쟁력이 돋보인다는 것을 알려줬다.
[65호] AVRISP-MK2와 AVRISP-STK500을 하나로!
제이씨넷
AVRISP-MK2와 AVRISP-STK500을 하나로!
획기적인 AVRISP 듀얼 프로그래머 JTOOL-MK2/STK500
제이씨넷은 AVRISP-MK2 프로그래머와 AVRISP-STK500 프로그래머를 하나로 통합하면서도 매우 작고 편리한 획기적인 듀얼 프로그래머인 JTOOL-MK2/STK500을 출시하였다. JTOOL-MK2/ S T K 500 은 사진에서와 같이 USB 메모리 크기에 위 2가지의 기능을 모두 가지고 있으며, 사용자 편의를 위하여 현재 사용하는 모드가 AVRISP-MK2 모드인지 AVRISP-STK500 모드 인지를 구별할 수 있게 해주는 LED를 내장한다. 또한, 타사의 AVRISP-MK2 프로그래머가 ISP/PDI/TPI 커넥터를 각각 따로 가지고 있지만, JTOOL-MK2는 오직 하나의 만능 커넥터만으로 ISP/PDI/TPI 모두를 겸용으로 사용하므로, 매우 간단하고 편리하다는 장점을 가진다. 더욱더 놀라운 것은, 2개의 프로그래머를 통합하였는데도 일반적인 MK2 프로그래머 1개의 가격으로 출시되어 매우 높은 가성비를 가지고 있다. 한마디로, 명품 AVRISP 프로그래머다. 한편, 제이씨넷은 JTOOL-MK2/STK500 을 출시하면서 자매품으로 저가의 AVRISP S T K 500 전용 프로그래머인 J TO O LAVRISP와 프로그래밍이 가능한 가성비 좋은 저가의 ATmega128 모듈인 JMOD-128- ECO도 함께 출시하였다. 두 가지 제품에 대한 자세한 내용은 디바이스마트에서 확인 가능하다.
[65호]Simple Touch
Simple Touch
글 | 동양미래대학교 박재범, 김도경, 김성곤, 김예빈, 김종혁, 이연수, 이정원, 장여원
1. 심사평
칩센 기술 자체가 어려운 것은 아니지만 아이디어가 매우 눈에 띄는 작품입니다. 특히 기존 시스템을 모두 교체하는 것이 아닌 일부만 대체하는 것만으로도 적용 가능하게 하는 부분은 훌륭한 발상입니다. 동작 방식 자체는 매우 심플하게 이해가 되었고, 실제 적용을 위해서 운용에 대한 방식은 조금 더 고민이 필요할 듯 합니다. 블루투스 보다 와이파이가 더 적절해 보이는 판단을 하고 적용하였으나, 이 또한 실제 적용에는 어려움이 있을 수 있습니다(주변 버스와의 구분등). 이러한 추가적인 고민을 통하면 매우 실용적인 장치가 될듯합니다.
펌테크 실생활에 활용될 수 있는 아이디어와 실용성을 지닌 작품이라고 생각합니다. 전체적으로 시스템을 꼼꼼하게 잘 구성하였고 생각하며 학부 과정의 학생이 구현하기에 적정한 난이도를 가진 작품이라고 생각합니다.
위드로봇 간단하지만 유용한 아이디어를 구현한 작품입니다.
2. 작품 개요
보도자료에 따르면 서울시에서만 하루 500만 명 이상의 시민들이 버스를 이용한다. 서울특별시 인구만으로 따져 보아도 2명 중 1명꼴로 버스를 이용하는 셈이다. 그만큼 버스는 우리에게 떼어놓을 수 없는 교통수단이다.
이렇게 많은 이들이 이용하는 까닭에 버스 이용 시 몇몇 불편함을 느낄 수 있는데, 특히 출퇴근 길 “러시아워” 시간대의 버스를 타기란 고통에 가깝다, 가까스로 버스 승차에 성공하더라도 하차하려는 것이 여간 어려운 것이 아닌데, 사람들로 가득 찬 버스 안에서 사람들 사이를 비집고 나아가 손으로 직접 하차벨을 누르기란 참으로 어려운 일이기 때문이다. 이에 최근 운행하는 신형 버스는 구형 버스보다 하차벨이 증설되었지만, 여전히 사람이 많은 버스에서 손으로 벨을 누르기에 불편한 상황임은 사실이다.
한편 최근 대두되고 있는 전염병 사태에서 직접 접촉에 의한 감염 사례도 나오고 있는 것을 보면 불특정 다수의 손길이 닿은 하차벨을 선뜻 누르기 어려운 경우도 있을 것이다. “직접적인 접촉을 하지 않고 하차벨을 누를 수 있는 방법은 없을까?” 이와 같은 물음에서 작품의 아이디어를 끌어내 보았다.
3. 작품 설명
3.1. 주요 동작 및 특징
3.1.1. 스마트폰 App으로 버스 하차벨 점등
· 통신을 위해 모든 하차벨을 교체할 필요 없이 하나의 하차벨 교체만으로도 시스템에 적용 가능함
· 하차벨을 누르기 위한 불필요한 움직임이 줄어들어 버스 내 안전사고 발생확률 감소를 기대할 수 있음
· 코로나19와 같은 전염병 사태에 불특정 다수가 접촉할 우려가 큰 하차벨에 접촉하지 않음으로서 접촉에 의한 전염병 감염사례 감소의 기대와 버스 이용 승객의 심적 안정을 도모할 수 있음
3.2. 전체 시스템 구성(전체 알고리즘)
3.3. 개발환경
4. 단계별 제작 과정
4.1. 시스템 설계
초기 스케치에서와 같이 일대다 통신 구현을 위해 Bluetooth와 WiFi 통신이 논의 되었으나, Bluetooth 통신 방식의 동시 접속 가능 기기 제한으로 인하여 WiFi 통신 방식을 채택한다.
WiFi 통신 모듈 선정
4.2. 하드웨어 제작
4.3. 하차벨 시스템 소프트웨어 작성
4.4. 스마트폰 App 소프트웨어 작성
4.5. 시스템 구현
4.6. 결론 및 전망
위와 같이 스마트폰 어플리케이션을 통해 버스 벨을 점등하는 테스트를 성공적으로 완료하였다. Java를 처음 다뤄보며 개발한 첫 어플리케이션이었기에 UI나 여러 편의사항 등에서 부족한 부분도 있었지만, 본 목적에 충실하게끔 구현했다는 점에서 큰 만족감을 가질 수 있었다.
본 아이디어는 비용과 시스템 구현 측면에서 여러 이점이 있는데, 제작 시 사용한 Lolin D1 mini를 대체해 ESP8266-01 등의 모듈을 사용한다면 저렴한 비용으로 단 하나의 벨 교체를 통해 무선 하차벨 시스템을 구현 할 수 있을 것으로 보인다.
한편 서울특별시 버스정책과에 문의해본 결과 올해 6월부터는 서울시내 버스 8000여대에서 공공와이파이를 이용할 수 있다고 하였다. 추후 공공와이파이를 이용한 시스템 구축을 통해 더 효율적이고 편리한 무선 하차벨 시스템을 제공할 수 있을 것으로 보인다.
경향신문 기사([‘코로나19’ 확산 비상]버스 하차벨 서로 미루고, 출입문 손 대신 어깨로 열어…‘거리두기’가 바꾼 일상)의 인터뷰에서는 코로나 19로 인한 버스에서의 현 상황이 잘 드러나고 있다. 본 아이디어는 이러한 대규모 전염병 사태에 있어 미약하게나마 감염 전파를 막는 요소가 될 수 있을 것이라 기대한다.
5. 기타
5.1. 회로도
bus_system.ino
int bell_led_1 = 22;
int bell_button_1 = 23;
int bell_test_1 = 24;
int bell_led_2 = 26;
int bell_button_2 = 27;
int bell_test_2 = 28;
int bell_led_3 = 30;
int bell_button_3 = 31;
int bell_test_3 = 32;
int bell_led_4 = 34;
int bell_button_4 = 35;
int bell_test_4 = 36;
int bell_led_5 = 38;
int bell_button_5 = 39;
int bell_test_5 = 40;
int door_button = 42;
boolean bell_signal_1;
boolean bell_signal_2;
boolean bell_signal_3;
boolean bell_signal_4;
boolean bell_signal_5;
boolean door_signal;
boolean total_signal;
boolean state;
void setup() {
pinMode(bell_led_1, OUTPUT);
pinMode(bell_led_2, OUTPUT);
pinMode(bell_led_3, OUTPUT);
pinMode(bell_led_4, OUTPUT);
pinMode(bell_led_5, OUTPUT);
pinMode(bell_button_1, INPUT);
pinMode(bell_button_2, INPUT);
pinMode(bell_button_3, INPUT);
pinMode(bell_button_4, INPUT);
pinMode(bell_button_5, INPUT);
pinMode(bell_test_1, OUTPUT);
pinMode(bell_test_2, OUTPUT);
pinMode(bell_test_3, OUTPUT);
pinMode(bell_test_4, OUTPUT);
pinMode(bell_test_5, OUTPUT);
pinMode(door_button, INPUT);
Serial.begin(115200);
Serial.println(“Setting done….”);
normal_state();
}
void normal_state() {
digitalWrite(bell_led_1, LOW);
digitalWrite(bell_led_2, LOW);
digitalWrite(bell_led_3, LOW);
digitalWrite(bell_led_4, LOW);
digitalWrite(bell_led_5, LOW);
digitalWrite(bell_test_1, HIGH);
digitalWrite(bell_test_2, HIGH);
digitalWrite(bell_test_3, HIGH);
digitalWrite(bell_test_4, HIGH);
digitalWrite(bell_test_5, HIGH);
state = 0;
}
void pushed_state() {
digitalWrite(bell_led_1, HIGH);
digitalWrite(bell_led_2, HIGH);
digitalWrite(bell_led_3, HIGH);
digitalWrite(bell_led_4, HIGH);
digitalWrite(bell_test_1, LOW);
digitalWrite(bell_test_2, LOW);
digitalWrite(bell_test_3, LOW);
digitalWrite(bell_test_4, LOW);
digitalWrite(bell_test_5, LOW);
state = 1;
}
void read_signal() {
bell_signal_1 = digitalRead(bell_button_1);
bell_signal_2 = digitalRead(bell_button_2);
bell_signal_3 = digitalRead(bell_button_3);
bell_signal_4 = digitalRead(bell_button_4);
bell_signal_5 = digitalRead(bell_button_5);
door_signal = digitalRead(door_button);
Serial.println(“b1, b2, b3, b4, b5, Door”);
Serial.print(bell_signal_1);
Serial.print(” “);
Serial.print(bell_signal_2);
Serial.print(” “);
Serial.print(bell_signal_3);
Serial.print(” “);
Serial.print(bell_signal_4);
Serial.print(” “);
Serial.print(bell_signal_5);
Serial.print(” “);
Serial.println(door_signal);
total_signal = bell_signal_1||bell_signal_2||bell_signal_3||bell_signal_4||bell_signal_5;
}
void self_holding () {
if (state==0) {
if (total_signal == 1) {
pushed_state();
Serial.println(“bell pushed….”);
}
}
else if (state == 1) {
if (door_signal == 0) {
normal_state();
Serial.println(“door opend….”);
}
}
}
void loop() {
read_signal();
self_holding();
delay(100);
}
5.2.2. Lolin D1 mini 소스코드
wifi_module.ino
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#ifndef APSSID
#define APSSID “ICT_TEST”
#define APPSK “ict_test”
#endif
const char *ssid = APSSID;
const char *password = APPSK;
ESP8266WebServer server(80);
uint8_t LED_PIN = 4;
bool LED_PIN_STATUS = LOW;
void handleRoot() {
LED_PIN_STATUS = LOW;
server.send(200, “text/html”, “<h1>You are connected</h1>”);
}
void handleRoot_on() {
LED_PIN_STATUS = HIGH;
server.send(200, “text/html”, “<h1> LED ON </h1>”);
}
void setup() {
delay(1000);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 0);
Serial.begin(115200);
Serial.println();
Serial.print(“Configuring access point…”);
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print(“AP IP address: “);
Serial.println(myIP);
server.on(“/”, handleRoot);
server.on(“/on”, handleRoot_on);
server.begin();
Serial.println(“HTTP server started”);
}
void loop() {
server.handleClient();
if(LED_PIN_STATUS) {
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
LED_PIN_STATUS =!LED_PIN_STATUS;
}
else
{digitalWrite(LED_PIN, LOW);}
}
5.2.3. 스마트폰 App 소스코드
AndroidManifest.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.pleaseplease”>
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />
<uses-permission android:name=”android.permission.CHANGE_WIFI_STATE” />
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/>
<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”/>
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.MainActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
tools:context=”.MainActivity”>
<Button
android:id=”@+id/button”
android:layout_width=”match_parent”
android:layout_height=”48dp”
android:text=”STOP”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
</LinearLayout>
MainActivity.java
package com.example.pleaseplease;
import androidx.appcompat.app.AppCompatActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private WebView webview;
private String networkSSID = “ICT_TEST”;
private String networkPass = “ict_test”;
private WifiManager wifiManager;
private WifiConfiguration wifiConfiguration;
private int size = 0;0
private ArrayList<String> arrayList = new ArrayList<>();
private ArrayAdapter<String> adapter;
private static final String TAG = “MyActivity”;
Intent myIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonScan = findViewById(R.id.button);
buttonScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
scanWifi();
if (wifiManager.isWifiEnabled()) {
String url = “http://192.168.4.1/on”;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
}
});
wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
assert wifiManager != null;
if (!wifiManager.isWifiEnabled()) {
Toast.makeText(this, “wifi is disabled… connecting…”, Toast.LENGTH_SHORT).show();
wifiManager.setWifiEnabled(true);
}
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arrayList);
scanWifi();
}
private void scanWifi() {
arrayList.clear();
registerReceiver(wifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
wifiManager.startScan();
Toast.makeText(this, “Scanning…..”, Toast.LENGTH_SHORT).show();
}
BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> results = wifiManager.getScanResults();
Log.i(TAG, String.format(“size—-%d”, results.size()));
unregisterReceiver(this);
for (ScanResult scanResult : results) {
Log.i(TAG, String.format(“—-%s”, scanResult.SSID));
arrayList.add(scanResult.SSID);
adapter.notifyDataSetChanged();
if (scanResult.SSID.equals(networkSSID)) {
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = networkSSID;
wifiConfiguration.preSharedKey = networkPass;
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
int netId = wifiManager.addNetwork(wifiConfiguration);
Log.i(TAG, String.format(“netId = = = =%s”, netId));
if (netId == -1) {
existconnect();
Log.i(TAG, String.format(“connect to %s”, scanResult.SSID));
wifiManager.disconnect();
wifiManager.enableNetwork(netId, true);
wifiManager.reconnect();
} else {
Log.i(TAG, String.format(“connect to %s”, scanResult.SSID));
wifiManager.disconnect();
wifiManager.enableNetwork(netId, true);
wifiManager.reconnect();
}
}
}
}
};
public void existconnect() {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
assert wifiManager != null;
List<WifiConfiguration> wifiConfigurations = wifiManager.getConfiguredNetworks();
if (wifiConfigurations != null) {
for (WifiConfiguration configuration : wifiConfigurations) {
int id = configuration.networkId;
String ssid = configuration.SSID;
Log.i(TAG, String.format(“NW———-%3d %s”, id, ssid));
if (ssid.equals(networkSSID)) {
Log.i(TAG, String.format(“coneect to %3d %s”, id, ssid));
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = ssid;
wifiConfiguration.preSharedKey = networkPass;
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiManager.disconnect();
wifiManager.enableNetwork(id, true);
wifiManager.reconnect();
}
}
}
}
}
5.3. 참고자료
· 서울특별시 보도자료 『2019년 서울 돌아보기(20200211)』
· https://en.wikipedia.org/wiki/Bluetooth_Low_Energy
· https://opengov.seoul.go.kr/mediahub/19181864
· http://news.khan.co.kr/kh_news/khan_art_view.html?art_id=202003052147015
[65호]Cable-driven Anthropomorphic Dexterous Robot hand
2020 ICT 융합 프로젝트 공모전 장려상
Cable-driven Anthropomorphic
Dexterous Robot hand
글 | 서울과학기술대학교 김태욱, 금현주, 민성재, 이동현
1. 심사평
칩센 보고서가 매우 촘촘하게 잘 작성되었고, 기술의 구현 난이도 또한 매우 높아 보입니다. 기획한 작품이 잘 구현 제작된다면 여러 가지 방면에서 사용이 가능할 듯도 합니다. 메카닉적인 부분과 하드웨어/소프트웨어 부분을 모두 포함하고 있어 수준이 높은 작품이라 판단됩니다만, 작품의 최종 결과물에 대하여 동작 등을 확인할 수 없어 얼마나 세밀하고, 정밀하게 관절의 움직임이 있는지는 알 수가 없어 일부 평가 항목에서 손해를 보았습니다. 하지만 지원자(팀)께서 어떤 의도를 가지고 작품을 개발하는지 충분히 이해가 가능하고, 시도만으로도 충분히 좋은 평가가 가능한 작품입니다.
펌테크 출품된 작품은 순수 기계공학적인 부분으로만 구성된 우수한 작품으로 전기, 전자, 소프트웨어 응용제품을 대상으로 하는 본작품전의 기본취지와는 다소 차이가 있는 제품이 아닌가 판단됩니다.
위드로봇 동작 시연 및 손가락의 모션 제어까지 포함되었으면 아주 훌륭한 작품입니다.
2. 작품 개요
18세기 이후, 산업혁명 초기에는 물체를 손으로 잡고 이동하거나 반복적인 일을 수행할 수 있는 팔 형태의 로봇들을 연구했고 그 로봇들이 사람들을 대신하면서 대규모 제조업이 발달하였다.
시간이 흐르면서 점점 정교하고 빠른 로봇 팔이 연구되었고 최근 들어서는 정해진 환경에서 반복적인 일을 하는 것을 넘어 안드로이드나 의수 등으로 인한 여러 가지 환경에서 적응력이 좋은 유연한 로봇 팔에 대한 필요성이 높아지고 있다.
인간의 한 손은 27개의 자유도가 있어 유연하고 세밀한 작업을 할 수 있다. 이에 많은 연구가 인간의 손을 모방한 로봇 팔을 만들려고 시도 하고 있다.
의인화한 손의 기계적 설계 요구사항은 Anthropomorphic features와 Grasping performance로 나눌 수 있다[5].
Anthropomorphic features: 손가락의 구성, 크기 및 역동적인 행동을 포함하여 가능한 사람의 손을 모방해야 한다[5].
Grasping performance: 로봇 손의 속도와 힘이 충분하여 특정되지 않은 물체도 유연하게 잡을 수 있어야 한다[5].
기존의 로봇손에서 모터를 관절에 부착하는 방식으로는 Anthropomorphic features과 Grasping performance을 모두 충족시키기는 어렵다. Anthropomorphic features을 충족시키기 위해서는 로봇손이 작아져야 하지만 작은 모터는 힘이 약하기 때문에 Grasping performance을 충족시킬 수 없다.
이러한 문제점을 해결하기 위해 본 작품은 Cable을 이용하여 관절에 부착된 모터를 다른 곳으로 이동시키고 관절의 움직임을 제어할 수 있게 디자인을 하였다.
기존 로봇손 디자인에서는 관절모터에 이후 달린 관절들의 모터들이 부하로 작용하였지만, Cable을 이용한 본 작품은 이러한 부하를 없애 줌으로써 페이로드를 높였다.
3. 작품 설명
3.1. 주요 동작 및 특징
본 작품은 주요 동작 및 특징이 Cable-driven과 Anthropomorphic이 있다.
Cable-driven:
케이블구동 방식은 당길 수만 있는 단방향성을 가진다. 따라서, 이 방식은 직접 모터를 사용하여 양방향성을 가지는 기존의 로봇손과 중요한 차이가 있다[7][8][9]. 케이블의 단방향 특성 때문에 n자유도 모션을 갖는 매커니즘에서 n+1개의 케이블이 필요하다[1][7][10]. 최근에 연구되는 케이블 구동 로봇손은 기존의 양방향성 로봇손보다 더 많은 모터가 사용된다[1]. 그러나 본 작품에서는 케이블 구동을 이용하여 n개의 모터만으로 자연스러운 움직임을 구현하고 기존 양방향성 로봇손과 비교하여 더 큰 작업공간, 더 높은 적재 중량과 생산비용을 제공할 수 있다.
Anthropomorphic:
인간의 몸은 630개의 근육으로 제어되는 244자유도가 있다[3]. 244개의 자유도 중 사람 손에만 총 54개의 자유도를 제공한다[4]. 많은 자유도에 의해 손은 높은 유연성이 있으며 악력이 강하고 물체를 잡는데 능숙하다[2].
실제 인간의 손은 탄성을 가진 피부로 덮여 있고 뼈와 근육, 힘줄, 인대 등과 결합된 복잡한 매커니즘이 있기 때문에 완전히 일치하게 로봇손을 제작하기는 어렵다[2]. 때문에 본 작품은 실제 인간의 손의 구조를 모방하되 자연스러운 움직임을 구동하는데 영향이 적은 관절은 생략 하도록 한다.
실제 사람의 손가락 모형은 크게 엄지와 나머지 4개의 손가락으로 나눌 수 있다. 엄지는 제 1관절로 TM, 제 2관절 MCP, 제3관절을 IP로 표현할 수 있다. 나머지 4개의 손가락에서의 CMC를 제외하고 제 1관절을 MCP, 제 2관절 PIP, 제 3관절을 DIP로 제작하였다. [그림1]의 D와 같이 TM은 사실 2자유도지만 1자유도로 봐도 자연스러운 움직임이 가능하므로 1자유도로 제작하였다. 5개의 모든 손가락의 제 1관절은 2자유도로, 제2, 3관절은 1자유도로 하였다. 2, 3관절은 각각의 자유도를 가지지만 서로 종속된 움직임을 가지기 때문에 스프링을 이용하여 한 개의 모터로 구동시켰다.
3.2. 전체 시스템
3.2.1. 전체디자인
본 작품의 로봇손은 손가락, 손바닥, 모터 및 보빈, 매니퓰레이터로 나눌 수 있으며, 케이블이 손가락의 각 관절에서부터 시작되어 손바닥을 지나 모터 보빈에 연결된다.
3.2.2. 손가락
신체 구조상 손가락을 크게 뼈, 관절, 근육으로 분리해 생각할 수 있다. 손가락의 뼈는 강도와 전체 손의 무게를 고려해 알루미늄 샤프트를 사용하였다[그림3].
관절부분에는 유니버셜 조인트를 사용하였다[그림4]. 제 2,3관절은 1자유도이기 때문에 물리적 제한을 두어 유니버셜 조인트가 한방향으로만 움직일 수 있게 하였다.
마지막으로 근육 및 인대 역할을 하는 케이블을 연결하기 위하여 실이 지나가거나 고정될 수 있는 플레이트를 제작하여 관절에 부착하였다[그림5]. 이 부분을 앞으로 ‘조인트 플레이트’라고 지칭하겠다.
첫 번째(MCP)와 세 번째 관절(DIP)은 모터에 실제로 연결되어 있기 때문에 모터를 조절하여 각 관절을 움직일 수 있다. 하지만 첫번째 관절과 달리 세번째 관절과 두번째 관절은 두번째 관절이 특정되지 않기 때문에 위치와 각도는 예측할 수 없다.
또한 조인트가 중심 축을 기준으로 회전할 때 회전운동에 대한 실이 묶여 있는 양 끝점의 위치와 지정한 손가락 마디의 길이를 가지고 다음 조인트 플레이트의 위치를 기구학적 해석으로 구할 수 있는데, 중심과 다른 높이에 조인트 플레이트가 있다면 회전운동과 평행이동의 두 단계에 걸친 계산이 필요하다.
유니버셜 조인트의 구조적인 문제로 자연스러운 움직임이 가능한 각도가 정해져 있다. 유니버셜 조인트의 자체 최대각도까지 회전하기도 전에 조인트 플레이트가 조인트에 닿아 회전하는 각도에 제한이 생긴다. 또한 실을 계속해서 당기면 실과 조인트 플레이트가 평행하게 접하는 각도가 있다. 이 각도를 넘어서까지 실을 당길 경우 조인트 플레이트에 실이 걸려 반발력이 발생하고 양쪽 두 실의 변화량이 달라질 수 있다. 이러한 각도 제한들의 안에서 손가락이 구동되어야 하므로 최대각도를 다음과 같이 정했다.
-5° < θ < 60°
3.2.3. 손바닥
손바닥은 손가락을 고정할 수 있는 손바닥 뼈대[그림10], 손바닥에서 실이 지나가는 점을 고정해주는 플레이트[그림11], 손바닥 이후에 모터까지 케이블이 지나가는 튜브를 고정해줄 튜브 고정 플레이트 [그림13]가 있다.
손바닥에서 실이 지나가는 점을 고정해주는 플레이트를 앞으로 ‘손바닥 플레이트’라고 지칭하겠다[그림10].
손바닥에서 튜브를 고정해주는 플레이트를 앞으로 ‘튜브 고정 플레이트’라고 지칭하겠다[그림13].
손가락은 손바닥의 연장된 부분이지만 본 작품은 각각 분리된 형태로 존재한다. 만약 네 손가락이 모두 같은 방향으로 있다면, 손가락을 구부려 물체를 잡을 때 손가락들이 한 점에서 모이지 않기 때문에 불리하게 작용한다. 손가락을 구부렸을 때, 한 점에서 모든 손가락이 모이기 위해서 손바닥 뼈대의 각도와 길이를 조절할 필요가 있는데 그 값 은 [그림14]와 같다.
손가락 현재관절의 위치는 이전 관절의 위치와 관절사이의 거리 및 각도를 통해 계산한다. 케이블을 손바닥 끝에 고정시키면, 손바닥 플레이트의 실 고정점을 손가락의 시작점으로 생각하여 관절의 위치를 구할 때 계산을 단순화할 수 있다. 또한 손가락이 움직일 때 케이블이 손바닥에 접촉되어 일어나는 의도치 않은 길이 변화를 제한할 수 있다.
3.2.4. 보빈 및 액츄에이터
손가락을 움직이기 위해서 실을 밀고 당기는 역할은 보빈과 액츄에이터가 한다. 보빈[그림16]은 실이 감기는 부분이고 액츄에이터[그림17]는 보빈을 돌려준다.
실을 감기 전에 먼저 보빈에 실구멍을 통해 실을 한번 묶어서 고정해준 뒤 실을 감아 실이 헛도는 것 방지한다.
케이블은 특성상 당기는 방향으로만 제어할 수 있고 미는 방향으로의 제어가 불가능하여, 하나의 모터로는 한 가닥의 케이블만 조절할 수 있다. 기존의 로봇손은 1개의 자유도를 제어하기 위해서는 두개의 모터가 필요하다. 그러나 본 작품은 한 개의 자유도를 제어하기 위하여 두 가닥의 케이블을 하나의 보빈에 반대방향으로 감아 더 적은 액츄에이터로 1개의 자유도를 제어할 수 있게 하여 모터의 수를 줄였다.
케이블은 보빈의 가운데 원판[그림15]을 기준으로 한쪽은 순방향, 반대쪽은 역방향으로 감겨 있어, 모터가 회전하면 한쪽은 감기고 반대쪽은 풀린다. 제어하고자 하는 조인트 플레이트의 양쪽 끝에 실을 묶어 손가락의 움직임을 제어할 수 있다.
3.2.5. 매니퓰레이터
매니퓰레이터를 손과 연결하면 사람의 팔과 같은 형태가 되어 더 다양한 방향으로 어플리케이션을 확장할 수 있다. 하지만, 매니퓰레이터를 사용할 경우 매니퓰레이터의 회전과 이동 때문에 몸체사이에 케이블이 걸려 의도치 않은 길이변화가 일어날 수 있다. 이 문제를 해결하기 위해서 손바닥부터 튜브를 연결하여 모터의 전까지 케이블을 감싸고 있으면 위의 문제를 해결할 수 있다.
3.2.6. 기구학
로봇 손의 손가락 끝지점은 4X4 회전행렬을 활용한 동차변환으로 기구학을 풀이하여 해석하였다. 동차 변환 행렬은 각 축에 대한 회전 행렬에서 좌표 이동과 크기 조정에 대한 정보를 추가한 행렬으로, 일반적으로 아래의 행렬과 같이 나타난다.
본 제품에서는 투시 변환의 경우는 존재하지 않으므로, 투시 변환은 0으로 고정시키고, 크기조정 또한 단위 변환이므로 1으로 고정시킨다.
순기구학을 풀이할 시, 동차변환은 각 링크 간의 이동변환과 회전변환의 곱으로 나타난다. 동차 변환에 관한 자세한 원리는 본 서식에서 다루지 않고 참고문헌을 참조한다[14].
매니퓰레이터는 6개의 링크의 조합으로 움직이고 각 관절의 움직임은 다음과 같다.
매니퓰래이터의 동차변환()은 다음 식으로 정리된다.
로봇 손은 시작점(origin)에서 손가락 끝점까지 2개의 조인트와 3개의 샤프트의 조합으로 이루어져 있다. 따라서, 로봇 손의 동차변환은 총 세 링크와 그 변환들의 조합으로 나타난다. 손바닥의 길이를 L_0, 첫 번째 관절부터 세 번째 관절까지의 손가락 길이를 L_1, 세 번째 관절부터 손가락 끝부분까지의 길이를 L_2라 할 때, 각 움직임은 다음과 같다.
로봇 손의 동차변환(DH)는 다음 식으로 정리된다.
3.3. 개발 환경
3.3.1. 디자인
Cable-driven robot hand를 개발하는데 있어서 대부분의 하드웨어를 3D프린터를 사용하여 만들었다. 프린터로 제작한 부품 이외에는 유니버셜 조인트, 샤프트 등 일반적으로 시중에서 쉽게 구입할 수 있는 공학 재료들을 사용하여 제작하였기 때문에 쉽게 제작할 수 있다. 3D프린터를 이용하여 부품을 제작한 만큼 설계하는 CAD 프로그램 선정 또한 중요했다. 설계를 하는데 사용한 프로그램은 AUTODESK사의 INVENTOR이다.
인벤터로 디자인을 하고 이를 프로그램 내장된 3D 프린트 기능으로 STL 파일로 모델을 출력하였다. 아래는 인벤터도 디자인한 윈치 와이어가 감기는 보빈이다. 이렇게 디자인한 각 파트들을 위와 같이 인벤터 안에서 조립해볼 수 있다.
또한 인벤터를 사용함으로써 장점은 디자인한 각 파트를 미리 조립을 해보고 간섭이 있는지 검사하거나 어떤 부분이 약할지 시뮬레이션을 해볼 수 있다.
만들어진 STL파일을 3D 프린터로 출력하기 위해 GCODE로 변환하기 위해 사용한 프로그램은 많은 사용자들이 사용하는 ULTIMAKER CURA를 사용했다.
CURA에 STL파일을 넣고 사용할 프린터에 맞게 설정을 해주면 적절한 GCODE가 생성된다. 프린터에 따라 직접 프린터를 컴퓨터에 연결하여 CURA에서 바로 프린트할 수도 있고 생성한 GCODE파일을 이동식 메모리에 넣어 프린터에 꽂아 사용할 수 있다. 아래는 모델링한 STL파일을 열어 GCODE로 변환한 모습이다.
CURA에는 다양한 설정이 있어 각 부품들의 필요한 속성에 따라 적당한 값으로 조절하여 필요한 특성을 얻을 수 있다.
3.3.2. 시뮬레이션
제작하기에 앞서 모델링 한 부품들에 맞춰 실제로 동작하는지 확인해 볼 필요가 있다. 동작 중에 수치적으로 와이어의 길이 변화가 어떻게 일어나는지 또는 그 과정에서 얼마나 오류가 발생하는지 확인해야 한다. 앞에 3-2. 6)절에서 기구학을 풀었는데 이를 코드로 구현하여 설계한 대로 제작했을 때 각도가 변함에 따라 와이어의 길이를 시뮬레이션한다. 이를 위해 MATLAB을 이용해서 기구학 식을 구현해서 시뮬레이션 해보고 그때의 와이어 길이의 변화를 구했다.
아래 3차원 그래프는 각 손가락 관절을 움직일 때 와이어, 플레이트, 샤프트의 위치를 표시한 그래프이다. 또한 이렇게 손가락을 움직일 때 밑에 그래프처럼 각 와이어가 어떻게 얼마나 변하고, 한 관절에 대해서 늘어나고 줄어드는 양의 오차가 얼마인지 그래서 그 양은 모터의 각도로 얼마나 차이 나는지를 확인할 수 있다.
3.3.3. 로봇 손 구동 펌웨어
기본적인 모터 구동을 포함한 유저 인터페이스, 어플리케이션과의 연결은 C++로 개발하였다. 사용하는 액츄에이터가 ROBOTIS의 DYNAMIXEL이기 때문에 따로 모터와 엔코더를 위한 개별 제어기를 사용하지 않았고, ROBOTIS에서 제공하는 SDK를 사용하여 제어했다. 따라서 모든 기본적인 제어를 포함한 피드백, 다양한 명령에 대해서 주 제어기인 PC가 대부분의 역할을 수행하게 된다. 초기적인 방법으로 C++ 콘솔 응용프로그램을 사용하여 손가락의 구동 및 유저 인터페이스를 구현하였다. 개발자가 필요에 따라 GUI가 가능하도록 C#을 이용하여 개발하거나 OpenCV나 OpenGL을 이용하여 그래픽으로 표시하게 추가할 수 있다.
4. 단계별 제작 과정
4.1. 손가락 한 개 시제작
5. 기타
5.1. 소스코드
5.1.1. 매트랩 시뮬레이션 코드
%% Simulate One Finger
clear, clc;
SimMin = 0;
SimMax = 30;
Res = 20;
BobinR = 5;
PlateR = 11;
PlateHallR = 8;
PlateOffset = [0 0 0 0]+1;
SHAFTLENGTH = [15 50 35 25];
disp(['Simuation from ' num2str(SimMin) ' to ' num2str(SimMax) ', with ' num2str(Res) 'steps']);
sim = linspace(SimMin, SimMax, Res);
counter = 1;
clear LWs;
LWs = zeros(6,length(sim));
for i = 1:length(sim)
[SHAFT, WIRE, LW] = OneFinger(eye(4),[sim(i) sim(i) sim(i) 0 0 0 0 0 0],PlateHallR,PlateOffset,SHAFTLENGTH);
LWs(:,counter) = LW;
figure(1);
plot3(SHAFT(1,:),SHAFT(2,:),SHAFT(3,:),’-o’,'LineWidth’,3), hold on;
plot3(WIRE{1}(1,:),WIRE{1}(2,:),WIRE{1}(3,:),’-.d’,'LineWidth’,2);
plot3(WIRE{2}(1,:),WIRE{2}(2,:),WIRE{2}(3,:),’-.d’,'LineWidth’,2);
plot3(WIRE{3}(1,:),WIRE{3}(2,:),WIRE{3}(3,:),’-.d’,'LineWidth’,2);
plot3(WIRE{4}(1,:),WIRE{4}(2,:),WIRE{4}(3,:),’-.d’,'LineWidth’,2);
plot3(WIRE{5}(1,:),WIRE{5}(2,:),WIRE{5}(3,:),’-.d’,'LineWidth’,2);
plot3(WIRE{6}(1,:),WIRE{6}(2,:),WIRE{6}(3,:),’-.d’,'LineWidth’,2);
plotCircle3D(SHAFT(:,1)’+PlateOffset(1)*(SHAFT(:,2)-SHAFT(:,1))’/norm((SHAFT(:,2)-SHAFT(:,1))’),(SHAFT(:,2)-SHAFT(:,1))’,PlateR,2);
plotCircle3D(SHAFT(:,2)’+PlateOffset(2)*(SHAFT(:,3)-SHAFT(:,2))’/norm((SHAFT(:,3)-SHAFT(:,2))’),(SHAFT(:,3)-SHAFT(:,2))’,PlateR,2);
plotCircle3D(SHAFT(:,3)’+PlateOffset(3)*(SHAFT(:,4)-SHAFT(:,3))’/norm((SHAFT(:,4)-SHAFT(:,3))’),(SHAFT(:,4)-SHAFT(:,3))’,PlateR,2);
plotCircle3D(SHAFT(:,4)’+PlateOffset(4)*(SHAFT(:,5)-SHAFT(:,4))’/norm((SHAFT(:,5)-SHAFT(:,4))’),(SHAFT(:,5)-SHAFT(:,4))’,PlateR,2);
title(‘One Finger’);
axis equal;
hold off;
drawnow;
counter = counter+1;
end
figure(2);
subplot(2,3,1), plot(sim,LWs(1,:)), axis([SimMin SimMax 0 150]),title(‘Wire 1 Length’), grid on;
subplot(2,3,2), plot(sim,LWs(2,:)), axis([SimMin SimMax 0 150]),title(‘Wire 2 Length’), grid on;
subplot(2,3,3), plot(sim,LWs(3,:)), axis([SimMin SimMax 0 150]),title(‘Wire 3 Length’), grid on;
subplot(2,3,4), plot(sim,LWs(6,:)), axis([SimMin SimMax 0 150]),title(‘Wire 6 Length’), grid on;
subplot(2,3,5), plot(sim,LWs(5,:)), axis([SimMin SimMax 0 150]),title(‘Wire 5 Length’), grid on;
subplot(2,3,6), plot(sim,LWs(4,:)), axis([SimMin SimMax 0 150]),title(‘Wire 4 Length’), grid on;
figure(3);
subplot(2,3,1), plot(sim,LWs(1,:)-LWs(6,:)), axis([SimMin SimMax -50 50]),title(‘J2 WL diff’);
subplot(2,3,2), plot(sim,LWs(2,:)-LWs(5,:)), axis([SimMin SimMax -50 50]),title(‘J1_1 WL diff’);
subplot(2,3,3), plot(sim,LWs(3,:)-LWs(4,:)), axis([SimMin SimMax -50 50]),title(‘J1_2 WL diff’);
subplot(2,3,4), plot(sim,LWs(1,:)-LWs(1,1)+LWs(6,:)-LWs(6,1)), axis([SimMin SimMax -15 15]),title(‘J2 WL error’);
subplot(2,3,5), plot(sim,LWs(2,:)-LWs(2,1)+LWs(5,:)-LWs(5,1)), axis([SimMin SimMax -15 15]),title(‘J1_1 WL error’);
subplot(2,3,6), plot(sim,LWs(3,:)-LWs(3,1)+LWs(4,:)-LWs(4,1)), axis([SimMin SimMax -15 15]),title(‘J1_2 WL error’);
disp(['Max J2 wire length error: ' num2str(max(abs(abs(LWs(1,:)-LWs(1,1)+LWs(6,:)-LWs(6,1))))) 'mm']);
disp(['Max J1_1 wire length error: ' num2str(max(abs(abs(LWs(2,:)-LWs(2,1)+LWs(5,:)-LWs(5,1))))) 'mm']);
disp(['Max J1_2 wire length error: ' num2str(max(abs(abs(LWs(3,:)-LWs(3,1)+LWs(4,:)-LWs(4,1))))) 'mm']);
figure(4);
LWCs = LWs-LWs(:,1);
LWCd = rad2deg(LWCs./BobinR);
MotorPos = round(LWCd./360*4096);
subplot(3,3,1), stairs(MotorPos(1,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 1′);
subplot(3,3,2), stairs(MotorPos(2,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 2′);
subplot(3,3,3), stairs(MotorPos(3,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 3′);
subplot(3,3,4), stairs(MotorPos(6,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 6′);
subplot(3,3,5), stairs(MotorPos(5,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 5′);
subplot(3,3,6), stairs(MotorPos(4,:)), axis([0 length(LWCs) -4096 4096]), title(‘Motor Pos by wire 4′);
subplot(3,3,7), stairs(MotorPos(1,:) + MotorPos(6,:)), axis([0 length(LWCs) -256 256]), title(‘J2 Motor Pos Error’);
subplot(3,3,8), stairs(MotorPos(2,:) + MotorPos(5,:)), axis([0 length(LWCs) -256 256]), title(‘J1_1 Motor Pos Error’);
subplot(3,3,9), stairs(MotorPos(3,:) + MotorPos(4,:)), axis([0 length(LWCs) -256 256]), title(‘J1_2 Motor Pos Error’);
MaxPosErr = max([max(abs(MotorPos(1,:) + MotorPos(6,:))) max(abs(MotorPos(2,:) + MotorPos(5,:))) max(abs(MotorPos(3,:) + MotorPos(4,:)))]);
disp(['Max motor position error is: ' num2str(MaxPosErr) ' encoder tics, ' num2str(MaxPosErr/4096) ' rotation, degrees: ' num2str(MaxPosErr*360/4096)]);
5.1.2. 손가락 구동 코드
// CableOneFinger.cpp : This file contains the ‘main’ function. Program execution begins and ends there.
//
#if defined(__linux__) || defined(__APPLE__)
#include <fcntl.h>
#include <termios.h>
#define STDIN_FILENO 0
#elif defined(_WIN32) || defined(_WIN64)
#include <conio.h>
#endif
#include <Windows.h>
#include <stdlib.h>
#include <iostream>
#include “dynamixel_sdk.h”
#include “CableOneFinger.h”
#include “DXL_custom.h”
#include “DXL_application.h”
#include “UserFunction.h”
#include “Robotics.h”
using namespace std;
int main()
{
int dxl_comm_result = COMM_TX_FAIL;
uint8_t dxl_error = 0;
bool dxl_addparam_result = false;
bool dxl_getdata_result = false;
const int DXL_IDs[DXLS] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int DXL_REL_MIN_POSs[DXLS];
int DXL_REL_MAX_POSs[DXLS];
int DXL_REL_GRASP_POSs[DXLS];
fill_n(DXL_REL_MIN_POSs, DXLS, 0);
fill_n(DXL_REL_MAX_POSs, DXLS, 3000);
fill_n(DXL_REL_GRASP_POSs, DXLS, 2500);
int DXL_INIT_POSs[DXLS];
int DXL_ABS_GOAL_POSs[DXLS];
int DXL_ABS_PRE_POS_ERRs[DXLS];
int DXL_ABS_PRESENT_POSs[DXLS];
int DXL_ABS_MIN_POSs[DXLS];
int DXL_ABS_MAX_POSs[DXLS];
int DXL_ABS_GRASP_POSs[DXLS];
int16_t DXL_PRESENT_LOAD[DXLS];
bool DXL_GRASPED[DXLS] = { false };
dynamixel::PortHandler *portHandler = dynamixel::PortHandler::getPortHandler(DEVICENAME);
dynamixel::PacketHandler *packetHandler = dynamixel::PacketHandler::getPacketHandler(PROTOCOL_VERSION);
dynamixel::GroupSyncWrite SyncWriteGoalPos(portHandler, packetHandler, ADDR_GOAL_POSITION, LEN_GOAL_POSITION);
dynamixel::GroupSyncRead SyncReadPresentPos(portHandler, packetHandler, ADDR_PRESENT_POSITION, LEN_PRESENT_POSITION);
dynamixel::GroupSyncRead SyncReadPresentLoad(portHandler, packetHandler, ADDR_PRESENT_LOAD, LEN_PRESENT_LOAD);
if (portHandler->openPort())
cout << “Succeeded to open the dynamixel!” << endl;
else
{
cout << “Failed to open the dynamixel!” << endl;
_getch();
return -1;
}
if (portHandler->setBaudRate(BAUDRATE))
cout << “Set baudrate to ” << BAUDRATE << endl;
else
{
cout << “Failed to change the baudrate!” << endl;
_getch();
return -1;
}
if (!(initializeHand(packetHandler, portHandler, SyncReadPresentPos, SyncWriteGoalPos, &dxl_comm_result, &dxl_error, &dxl_addparam_result, &dxl_getdata_result, DXL_IDs, DXL_INIT_POSs, sizeof(DXL_IDs))))
cout << “Initializing failed!” << endl;
calculateArrayArithmetic(DXL_INIT_POSs, DXL_REL_GRASP_POSs, DXL_ABS_GRASP_POSs, sizeof(DXL_IDs), ‘+’);
calculateArrayArithmetic(DXL_INIT_POSs, DXL_REL_MIN_POSs, DXL_ABS_MIN_POSs, sizeof(DXL_IDs), ‘+’);
calculateArrayArithmetic(DXL_INIT_POSs, DXL_REL_MAX_POSs, DXL_ABS_MAX_POSs, sizeof(DXL_IDs), ‘+’);
cout << “CABLE-DRIVEN ROBOT HAND Initialized!!!” << endl;
grasping(packetHandler, portHandler, SyncReadPresentPos, SyncReadPresentLoad, SyncWriteGoalPos, &dxl_comm_result, &dxl_error, &dxl_addparam_result, &dxl_getdata_result, DXL_IDs, DXL_ABS_GRASP_POSs, DXL_ABS_PRESENT_POSs, DXL_ABS_PRE_POS_ERRs, DXL_PRESENT_LOAD, DXL_GRASPED, sizeof(DXL_IDs), DXL_MOVING_POS_THRESHOLD);
Sleep(5000);
toggleDXLTorque(packetHandler, portHandler, &dxl_comm_result, &dxl_error, DXL_IDs, sizeof(DXL_IDs), TORQUE_DISABLE);
portHandler->closePort();
return 0;
}
// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu
// Tips for Getting Started:
// 1. Use the Solution Explorer window to add/manage files
// 2. Use the Team Explorer window to connect to source control
// 3. Use the Output window to see build output and other messages
// 4. Use the Error List window to view errors
// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file
5.2. 참고문헌
[1] G.Z. Lum, S.K. Mustafa, H.R. Lim, W.B. Lim, G.Yang and S.H. Yeo, “Design and Motion Control of a Cable-driven Dexterous Robotic Arm”, 2010 IEEE Conference on Sustainable Utilization and Development in Engineering and Technology Universiti Tunku Abdul Rahman, pp. 106-111.
[2] Li Tian, Nadia Magnenat-Thalmann, Daniel Thalmann and Jianmin Zheng, “A Methodology to Model and Simulate Customized Realistic Anthropomorphic Robotic Hands”, CGI 2018, June11-14,2018, bintan Island, Indonesia, pp. 153-162.
[3] Zatsiorsky. V. and B. Prilutsky, Biomechanics of skeletal muscles. 2012: Human Kinetics.
[4] Agur, A.M. and A.F. Dalley, Grant’s atlas of anatomy. 2009: Lippincott Williams & Wilkins.
[5] Ming Cheng, Li Jiang, Fenglei Ni, Shaowei Fan, Yuan Liu and Hong Liu, “Design of a Highly Integrated Underactuated Finger towards Prosthetic Hand”, 2017 IEEE international Conference on Advanced Intelligent Mechatronics (AIM), pp. 1035-1040.
[6] Li Tian, Nadia Magnenat-Thalmann, Daniel Thalmann and Jianmin Zheng, “The Making of a 3D-Printed, Cable-Driven, Single-Model, Lightweight Humanoid Robotic Hand”, frontiers in Robotics and AI, Volume4, Article 65, published in 04 December 2017, pp. 1-10.
[7] P. Gholami, M.A. Mohammad and H.D. Taghirad, “On the control of the KNTU CDRPM: A cable driven redundant parallel manipulator”, 2008 IEEE/RSJ International Conference on Intelligent Robots and Systems, Nice, France, pp. 2404-2409.
[8] I. Ebert-Uphoff and P.A. Voglewede, “On the connections between cable-driven robots, parallel manipulators and grasping”. 2004 IEEE International Conference on Robotics and Automation, New Orleans, LA, pp. 4521-4526.
[9] E. Ottaviano, M. Ceccarelli, A. Paone and G. Carbone, “A low-cost easy operation 4-cable driven parallel manipulator”. 2005 IEEE International Conference on Robotics and Automation, Barcelona, Spain, pp. 4019-4024.
[10] A. Ming and T. Higuchi, “Study on multiple degree-of- freedom positioning mechanism using wires (part 1)”, Int. Journal of the Japan Society of Precision Engineering, Vol. 28, No. 2, 1994, pp. 131-138.
[11] W.K. Jung, J.H. Park, I.S. Kwon, J.B. Song, Y.J. Choi, and K.H. Kim, The Experimental RoboticsⅠ: Manipulation and Vision, Institute of Control, Robotics and Systems, Korea Robotics Society (KRoS), pp. 1-335, 2011
[12] Dawei Xu, En li and Zize liang, “Kinematics and Statics Analysis of a Novel Cable-Driven Snake Arm Robot”, Chinese Automation Congress 2017, IEEE 2017, pp. 439-444.
[13] Maria Chlara Carrozza, Bruno Massa, Fabrizio Vecchi and R.Lazzarini, “The SPRING Hand: Development of a Self-Adaptive Prosthesis for Restoring Natural Grasping”, Autonomous Robots 16(2004), pp.125-141.