매트랩에서는 Java api를 이용해서 쉽게 socket 통신을 할 수 있다.
이전 포스팅에서 썼듯이 시리얼 통신도 쉽게 할 수 있는데, 결국 매트랩으로 여러 대의 로봇을 제어하는 서버를 만들 수 있다는 점이다.
다른 언어에 비해 매우 빠른 속도로 프로토타이핑이 가능한 매트랩에 이런 IO기능까지 더해지니 정말 쓸모가 많은 것 같다.
매트랩이 서버가 될 것을, 여러 대의 리눅스 환경의 노트북들이 클라이언트가 될 것을 가정한다.
1. 리눅스 쪽 C 소스 (이클립스에서 빌드)
1. main.cpp (나머지 파일들은 아래 첨부)
#include "wifi.h"
#include <stdio.h>
#include <sys/time.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include <opencv/cv.h>
#include <opencv/cvaux.h>
#include "opencv_sjlib.h"
#include <signal.h>
#include <math.h>
#include <stdlib.h>
#define NUMBER_OF_CAMERA 6
#define First_capture 101
#define Second_capture 102
#define Send_angle 103
int Imageready = 0;
int Imagename = 0;
int s_socket;
int ImageCapture();
void *CaptureThread(void *threadparam)
{
ImageCapture();
}
void exit_handler(int sig)
{
printf("exit_handler closing socket \r\n");
close(s_socket);
exit(0);
}
int main(int argc, char** argv)
{
// Set exit Handle
signal(SIGINT, exit_handler);
// Get IP and Port number from input args
char IP[80];
strcpy(IP, argv[1]); // indicates IP address
long channel ;
channel = atoi(argv[2]); // indicates IP address
// Try to make connection to server
struct sockaddr_in server_addr;
printf("Try making connection to %s : %ld \r\n", IP, channel);
ServerSelector(IP, channel, &server_addr);
//int s_socket; <= It's a global variable.
s_socket = -1;
while (s_socket == -1)
{
s_socket = MakeConnectionFC(&server_addr);
sleep(1);
}
printf("Connection complete to %s : %ld \r\n", IP, channel);
// <=== Thread ON for TESTING !!!
pthread_t thread;
int pthreadRet = pthread_create(&thread, NULL, &CaptureThread, NULL); // thread on : image capture
// Set buffer for socket communication
char RXbuffer[1024];
char TXbuffer[1024];
int RXdata = 0;
int RXdata_prev = 0;
while(1)
{
// Wait until single RX operation
int len = read(s_socket, RXbuffer, 1);
// When RX is successfully done,
if( len > 0)
{
RXdata = (int)(RXbuffer[0]);
printf("1. Socket rx data: %d \n", RXdata);
if (RXdata != RXdata_prev)
{
printf("2. Distinct Socket rx data: %d \n", RXdata);
RXdata_prev = RXdata;
}
}
double TXdata = (double)RXdata/10;
printf("3. Sending %.1f to Server \n", (double)TXdata );
int digit100 = (int)( (double)TXdata/100);
int digit10 = (int)( ((double)TXdata - 100*digit100)/10);
int digit1 = (int)( ((double)TXdata - 100*digit100 - 10*digit10)/1);
int digit_1 = (int)( 0.5 + 10*((double)TXdata - 100*digit100 - 10*digit10 - 1*digit1));
TXbuffer[0] = '0'+13; // <= 12면 음수, 그 외는 양수
TXbuffer[1] = '0' + digit100;
TXbuffer[2] = '0' + digit10;
TXbuffer[3] = '0' + digit1;
TXbuffer[4] = '0' + digit_1; // <= 123.4 를 의미한다.
printf(" %d %d %d %d \n",
digit100, digit10, digit1, digit_1);
write(s_socket, TXbuffer, 5*sizeof(char));
printf("4. Let's read again \n\n");
}
}
int ImageCapture()
{
IplImage* Image[NUMBER_OF_CAMERA];
CvCapture* capture[NUMBER_OF_CAMERA];
char filename[80];
int Save;
double prop_exposure = 0.005; // orig: 0.01
double prop_brightness = 0.01; // orig: 0.01
for (int i = 0; i<NUMBER_OF_CAMERA; i++)
{
capture[i] = cvCaptureFromCAM(i);
cvSetCaptureProperty(capture[i], CV_CAP_PROP_EXPOSURE, prop_exposure);
cvSetCaptureProperty(capture[i], CV_CAP_PROP_BRIGHTNESS, prop_brightness);
}
printf("ImageCapture thread start \n");
while (1)
{
for (int i = 0; i<NUMBER_OF_CAMERA; i++)
Image[i] = cvQueryFrame(capture[i]);
cvWaitKey(10);
if (Imageready == 1)
{
for(int j=0;j<NUMBER_OF_CAMERA;j++)
{
//printf("inside\t");
sprintf(filename, "image%d_%d.bmp",Imagename, j+1);
Save = cvSaveImage(filename, Image[j], 0);
}
while(1)
{
if(Save)
{
printf("thread\t");
break;
}
}
Imageready = 0;
printf("image saved\n");
}
}
for (int i = 0; i<NUMBER_OF_CAMERA; i++)
cvReleaseCapture(&(capture[i]));
return 0;
}
2. 서버용 매트랩 파일
1. demo_socket_multi.m
%%
clc;
clear all;
close all;
import java.net.ServerSocket
import java.io.*
%% 변수를 설정한다.
nr_agents = 3; % <== 전체 agent의 수
nr_connected = 0; % <== 전체 연결된 agent의 수
global sockets;
sockets = cell(nr_agents, 1);
% socket통신을 위한 포트의 번호를 정한다.
init_port_num = 4010; % <==================== 이 값부터 차레로 1씩 커진다.
for ai = 1:nr_agents
sockets{ai}.client_port = ai-1+init_port_num;
sockets{ai}.client_connected = 0;
sockets{ai}.client_output = [];
sockets{ai}.client_socket = [];
end
address = java.net.InetAddress.getLocalHost;
IPaddress = char(address.getHostAddress);
%% 클라이언트가 접속하기를 기다린다.
retry = 0;
max_retry = 1000;
while 1
% 클라이언트가 접속하기를 기다린다.
try
fprintf(1, ' Waiting %s : %d for client %d/%d to connect %d/%d \n'...
, IPaddress, sockets{nr_connected+1}.client_port...
, nr_connected+1, nr_agents, retry, max_retry);
if nr_connected < nr_agents
sockets{nr_connected+1}.client_socket = ServerSocket(sockets{nr_connected+1}.client_port);
sockets{nr_connected+1}.client_socket.setSoTimeout(1000);
sockets{nr_connected+1}.client_output = sockets{nr_connected+1}.client_socket.accept;
sockets{nr_connected+1}.client_connected = 1;
fprintf(1, 'Client%d connected ! \n', nr_connected+1);
nr_connected = nr_connected + 1;
end
catch
for ai = 1:nr_agents
if sockets{ai}.client_connected == 0
if ~isempty(sockets{ai}.client_socket), sockets{ai}.client_socket.close, end;
if ~isempty(sockets{ai}.client_output), sockets{ai}.client_output.close, end;
end
end
% pause before retrying
pause(1);
end
% 종료조건을 추가한다.
retry = retry + 1;
if retry > max_retry
fprintf(1, 'Too many retries -> break! \n');
break;
end
if nr_connected+1 > nr_agents
fprintf(1, 'All clients are connected! End trying. \n');
break;
end
end
%% 모두 연결이 되었다면!
while nr_connected+1 > nr_agents
%% 일단 멈추고
fprintf(1, '\n1. Press any key to continue.. \n');
pause(1);
for ai = 1:nr_agents
% 1. 보낼 데이터
send_data = randi([1 127]);
% 2. 실제로 보내는 부분
socket_send(sockets{ai}.client_output, send_data);
% 3. 데이터를 받는 부분
read_data = socket_read(sockets{ai}.client_output);
end
end
%% 종료하자.
for ai = 1:nr_connected
sockets{ai}.client_socket.close;
sockets{ai}.client_output.close;
end
%%
2. socket_read.m
function val = socket_read(output_socket)
%
% output_socket: 소켓 정보
% cmd: 무엇을 받을지 나타내는 명령 (숫자)
% 101: First Image Capture
% 102: Second Image Capture
% 103: 각도를 내놔
%
import java.net.Socket
import java.io.*
% 3. 받는 부분
input_stream = output_socket.getInputStream;
data_input_stream = DataInputStream(input_stream);
tick_rx_start = clock;
wait_period_ms = 500;
exit_period_ms = 10000;
while true
% 1. 받기 시작한 이후로 시간을 계산한다.
pause(wait_period_ms/1000);
tick_elapsed_ms = round(etime(clock, tick_rx_start) * 1000);
% 2. 일정 시간 이상 기다렸으면 에러이다.
if tick_elapsed_ms > exit_period_ms
fprintf('[RX error] Tick elapsed too much: %.1f [s] break !! \n'...
, tick_elapsed_ms/1000);
break;
end
% 3. 읽어본다.
bytes_available = input_stream.available;
if bytes_available > 0
rx_message = zeros(1, bytes_available, 'uint8');
for i = 1:bytes_available
% char로 들어온 것을 숫자로 바꾸기 위해서 이러한 작업을 한다.
rx_message(i) = data_input_stream.readByte - char('0');
end
if bytes_available == 1
% 들어온 것이 하나라면 그대로 쓰고
val = rx_message;
else
% 여러 데이터가 들어왔다면 하나의 숫자로 바꾼다.
rx_message = double(rx_message);
% 2,3,4 번째가 세 자리 숫자를 의미하고 5번째가 소수점이하 수를 나타낸다.
val = rx_message(2)*100+rx_message(3)*10+rx_message(4)*1+rx_message(5)*0.1;
val = double(val);
% 첫번째로 들어온 char가 부호를 나타낸다. (12이면 음수, 나머지는 양수)
if rx_message(1) == 12
val = -val;
end
end
fprintf('3. Socket rx_data: %.2f \n', val);
break;
end % if bytes_available > 0
% unit peroid에 한번씩 .을 찍는다.
fprintf('.');
end % while true
3. socket_send.m
function val = socket_send(output_socket, int_tx_data)
%
% output_socket: 소켓 정보
% cmd: 무엇을 받을지 나타내는 명령 (숫자)
% 101: First Image Capture
% 102: Second Image Capture
% 103: 각도를 내놔
%
import java.net.Socket
import java.io.*
fprintf(1, '2. Sending %d via socket \n'...
, int_tx_data);
output_stream = output_socket.getOutputStream;
data_output_stream = DataOutputStream(output_stream);
data_output_stream.writeBytes(char(int_tx_data)); % 보낼 때는 int에서 char로 변환해서 보낸다.
data_output_stream.flush;
3. 사용법
먼저 서버를 실행시킨다. 이 때 클라이언트가 접속할 수 있는 포트를 설정해 줘야 한다.
nr_agent라는 변수는 총 몇대의 클라이언트가 접속할 지를 정해주는 변수이다.
클라이언트는 다음과 같이 터미널에서 실행한다.
./test_opencv 192.168.1.62 2001
즉 첫번째 인자가 서버의 ip이고, 후자가 포트번호이다. 이는 서버를 실행시키면 나오니 그것을 이용하자.
현재 구현되어 있는 것은 서버에서 1~127 사이의 수를 보내면 client 쪽에서 그 값을 10으로 나누어서 다시 보내는 것이다.
통신은 가장 간단한 char를 주고 받는 것으로 했기 때문에 허접한 프로토콜을 만들어서 세 자리 숫자를 각 digit 별로 나눠서 보내게 해 두었다. double 로 버퍼를 만들면 될 것 같은데 귀찮다.
사용된 C 파일
사용된 매트랩 파일
'Enginius > Matlab' 카테고리의 다른 글
Check Point in Polygon (inpolygon) (0) | 2013.05.13 |
---|---|
Field를 만들고, 특정 Field에서 원하는 값 가져오기 (interp2) (0) | 2013.04.24 |
multi-colum legend (0) | 2013.02.18 |
fminsearch 를 사용한 optimization 예제 (0) | 2013.02.03 |
Extremely useful Surface Plot (0) | 2012.12.19 |