원격으로 키클봇 조종하기

지금까지 Python에 대해서 여러가지를 공부하고 있는데요.
이번에는 배운것들을 이용해서 작은 프로젝트를 수행해 볼까해요.
소켓(Socket)을 이용해서 멀리 있는 키클봇을 원격 조종하는 프로그램을 만들어 봐요!


강의들은 모두 무료입니다. 단, 저작권은 키클 코딩랩 에 있으며, 무단 복제 및 배포를 엄금합니다.
이를 어길 시, 본 사이트의 서버가 미국에 있으므로, 미국법에 의해 처벌될 수도 있습니다.




명령 클라이언트(Client) 구현하기

이번 강의에서는 지금까지 공부한 내용들과 작성한 코드들을 모아서 작은 프로젝트를 수행해 보려고해요.
고로, 새롭게 공부하는 내용은 없구요, 여러가지 코딩 기법들이 모여서 어떤 방식으로 실제 프로젝트에 적용될 수 있는지 감을 잡기 바래요.
프로젝트의 큰 줄기는 소켓(Socket)을 이용해서 멀리 있는 키클봇을 원격 조종하는 프로그램을 만들어 보는 것이예요. 실제로, 미국에는 이와 유사한 방식으로 자율주행차를 원격 조종하는 기술을 개발하는 신생 기업들이 있답니다.

먼저, 키클봇에게 원격으로 명령을 내리는 클라이언트를 구현해 볼게요.
키보드의 정해진 키를 누르면, 그 키값을 네트워크 통신으로 서버에게 보내는 기능을 할거예요.
원하는 폴더에 새로운 Python파일을 만들고, "lec12_cmd.py"라고 파일 이름을 지어 보지요.
그리고, 아래와 같이 코딩을 시작해 볼까요?

위 코드들은 강의 간단한 로봇 게임 만들기에서 작성했던 것이니, 간단하게 설명할게요.
줄1에서는, 우리가 원격 통신을 위해서 소켓(Socket)을 사용할 것이기 때문에 socket라이브러리를 import하고 있어요.
줄2는 키보드 명령을 받는 함수 작성을 위해서 import했던 것들이고, 줄4~줄9는 키보드의 명령을 받는 함수인데, 어떤 키가 눌렸는지 리턴해 주는 함수이지요. 줄2~줄9의 내용은 강의 간단한 로봇 게임 만들기에서도 언급했듯이, 우리가 지금까지 공부한 내용들 외에 알아야 할 것들이 너무 많으니 자세한 설명은 생략할게요.
사실, 실제로 프로젝트를 수행하다보면, 처음부터 끝까지 모든 함수의 코드를 이해하거나 본인이 작성하는 경우는 드물어요. 핵심이 되는 부분은 프로젝트를 수행하는 연구원/공학자들이 직접 코딩을 하지만, 부품 역할을 하는 부분은 이미 작성되어 있는 코드를 사용하는 경우가 많지요. 예를 들어, 줄1의 socket 라이브러리도 그 속이 어떻게 코딩되어 있는지 모른채 사용하고 있지요? 그 안의 객체들을 어떻게 조합하고 사용하는지만 알면 네트워트 프로젝트를 수행하는데 문제가 없는 것이지요.

다음 코딩을 이어서 아래 줄11~줄29와 같이 해보지요.

위 코드들도 강의 네트워크 통신하기에서 작성했던 것이라 익숙할 거라 믿어요.
줄11은 그저 주석으로 구분선을 그려 넣은 것이예요.
줄13은 클라이언트를 위한 소켓 객체를 생성하는 것이죠.
줄14에서는 서버의 IP주소를 적는 것인데, 클라이언트와 같은 컴퓨터에서 돌릴 것이기 때문에, "localhost" 혹은 "127.0.0.1"을 이용해야 한다고 강의 네트워크 통신하기에서 이야기 했지요? 이번 강의에서는 "127.0.0.1"을 사용해 볼게요.
줄15에서는, 서버 프로그램의 Port번호를 설정하는 것인데요. 나중에 서버의 Port를 5000을 사용할 것이기 때문에, 여기에서도 5000을 port변수에 할당하지요.
줄16은 소켓의 connect()메서드를 이용하여, host주소에서 작동하고 있는 port의 포트번호를 갖고 있는 서버에게 연결요청을 하는 코드이지요. 강의 네트워크 통신하기에서 설명했듯이 host와 port를 튜플로 묶어서 전달한답니다. 튜플에 대해서 복습이 필요하면 여기에서 꼭 확인하세요.

줄18에서는 while을 이용한 무한루프 코딩을 시작하고 있어요. 키보드 명령을 한 번으로 끝낼 것이 아니고, 계속 전송할 것이기 때문에 무한루프를 코딩한답니다.
줄19에서는 키보드 명령을 받고 있는데요. 앞서 줄4~줄9에서 작성한 getKey()함수를 이용하고 있지요.
줄21에서는 소켓의 send()메서드로 키보드에서 눌린 키값을 서버로 보내는 것인데, utf-8 포맷으로 인코딩하여 전송하고 있지요. 인코딩에 대하여 복습이 필요하다면 여기로가서 꼭 확인하세요.
줄23에서는, 서버로부터 전송된 최대 1024 byte 크기의 메세지를 recv()메서드를 이용해서 받고 있어요. 받은 메세지는 msg 변수에 할당하구요.
줄24에서는 utf-8로 인코딩되어서 온 그 메세지를 디코딩하여 출력하는 것이죠.
줄26~줄27에서는, 눌린 키가 "q"일 경우 break를 이용하여 무한루프를 탈출하고 있어요.
줄29에서는 무한루프를 탈출한 후 통신이 더이상 필요없으니 소켓을 닫고 있지요.




키클봇 서버(Server) 구현하기

원격으로 키보드 명령을 내리는 클라이언트를 코딩했으니, 전송된 키보드 명령을 해석하여 키클봇에 전달하는 서버를 구현해 보지요.
원하는 폴더에 새로운 Python파일을 만들고, "lec12_kiklebot.py"라고 파일 이름을 지어 보지요. 그리고 아래와 같이 서버 코딩을 시작할게요.

줄1은 socket라이브러리를 import하는 것이죠.
줄2에서는, matplotlib의 pyplot클래스를 plt라는 별명으로 import하고 있어요. 클라이언트에서 전송된 명령에 따라 움직이는 키클봇(KikleBot)을 figure에서 보여주기 위함이예요.
줄3은 numpy라이브러리를 np라는 별명으로 import하고 있어요.
줄5~줄15에서는 키클봇 클래스를 코딩했어요. 강의 키클봇 코딩하기에서 코딩했던 것을 그대로 복사해 온 것이니, 자세한 설명으로 복습이 필요하면 꼭 가서 확인하세요.
줄6에서는 속성들을 초기화 하기 위해, __init__() 메서드를 선언하고 있구요. 로봇의 처음 위치를 사용자에게서 init_coord라는 전달인자로 받고 있어요.
줄7에서는, 클래스의 속성 coord에 사용자에게서 받은 로봇의 처음 위치좌표 init_coord를 할당(저장)하면서 초기화 작업을 하고 있지요.
줄8에서는, 로봇이 지나온 경로(위치좌표들)를 저장하기 위해 속성 traj를 선언하면서, 속성 coord에 저장된 위치좌표를 traj의 첫 번째 경로 정보로 저장하고 있어요.
줄9에서는, 로봇이 이동 가능한 방향들을 속성 move라는 이름으로 초기화 하고 있지요.
줄11에서는, move_bot이라는 이름의 메서드 코딩을 시작하고 있구요, self외에 선택된 이동방향을 idx_move 전달인자로 받고 있답니다.
줄12~줄13에서는, 현재의 위치를 갖고 있는 coord을 선택된 이동 방향으로 이동시킨후, 새로운 위치좌표를 다시 coord에 저장하고 있지요.
줄15는 새로 변경된 위치좌표 coord를 이동 경로를 저장하는 traj에 쌓아놓고 있지요.

이어서, 아래의 줄17~줄27처럼 계속 코딩을 해볼게요. 강의 네트워크 통신하기에서 작성했던 코드들이라 눈에 익을 것이라 믿어요.

줄19는 socket라이브러리 내에 있는 socket클래스를 아무런 전달인자 없이 생성하는 것이랍니다. 라이브러리 내에 있는 대상을 이용할 때에도, 클래스의 속성이나 메서드를 이용할 때와 같이 "."을 이용하는 점 기억해 두세요.
줄20에서는 네트워크 상에서 서버가 갖는 호스트명 혹은 주소를 변수 host에 할당하는 것이지요. 서버와 클라이언트를 같은 컴퓨터에서 돌릴 것이기 때문에 "127.0.0.1"나 "localhost"를 사용해야 되지요. 클라이언트에서 "127.0.0.1"를 사용했으니, 서버에서는 "localhost"라고 해볼게요.
줄21에서는 Port번호를 변수 port에 저장하고 있는데, 클라이언트를 코딩할 때도 언급했듯이, 5000을 사용할게요.
줄22에서는, bind() 메서드를 이용하여, 위에서 생성한 소켓에 IP주소와 Port번호를 설정하는 코드예요. 이제 이 소켓은 "localhost"라는 주소를 갖는 컴퓨터에서 돌아가는 "5000" 포트번호를 갖는 서버가 되는 것이죠.
줄23은 소켓에게 외부에서의 연결을 받을 수 있다는 것을 알리는 코드예요. 전달인자로 5를 부여함으로써, 서버 소켓은 최대 5개까지 연결 요청을 대기상태에 둘 수 있게 된답니다. 5개 넘어서 들어오는 연결 요청은 거부하게 되지요.
줄24에서는, accept()메서드로 서버 소켓에게 클라이언트로부터 연결 요청을 기다리라고 명령하는 코드예요. 줄24가 실행이 되면, 더이상의 실행을 멈추고 클라이언트에게서 연결 요청이 올때까지 기다리게 된답니다. 메서드 accept()는 클라이언트와의 연결이 이루어지면, 해당 클라이언트와 연결을 이루는 새로운 소켓 객체를 생성하고, 클라이언트의 주소 정보와 함께 리턴하지요. 이 두개의 리턴값들을 각각 conn과 addr이라는 변수에 저장할게요.
클라이언트와 연결이 이루어지면, 줄26으로 실행이 넘어가게 되지요.
줄26에서는 키클봇 객체를 생성하고 있어요. Numpy의 배열을 이용하여, 로봇의 시작 위치를 (0, 0)으로 부여하고 있답니다.
줄27에서는 키클봇의 움직임을 보여줄 figure를 생성하고 있지요.
지금까지 해오고 있는 코드들을 보면 알겠지만, 모두 이전 강의들에서 작성했었던 코드들이지요? 혹여, 기억이 명확하지 않다면, 꼭 이전 강의들을 복습해 보세요.

자, 이제 무한루프에서 클라이언트의 명령을 계속적으로 받아 키클봇에 전달하는 코딩을 해보지요.
역시, 이전 강의들에서 공부했던 내용들을 적용하고 있답니다. 줄29~줄55가 추가된 코드들이예요.

줄29는 무한루프의 시작을 알리고 있지요.
줄30에서는 figure를 깨끗이 지우고 있구요.
줄31에서는 로봇의 가장 최근 위치인 키클봇의 coord 속성을 파랑색 사각형 마커로 보여주지요.
줄32에서는 로봇이 지금까지 지나온 경로인 키클봇의 traj 속성을 파란선으로 보여주고 있구요.
줄33~줄34은 그래프의 축 범위를 정하고 것인데요. 이동하는 키클봇을 따라갈 수 있도록, 키클봇의 현재 (x, y)위치에서 -10과 10을 더한 지점을 각각 범위의 최소값과 최대값으로 설정했답니다.
줄35에서는 키클봇의 위치를 볼 수 있도록 0.01초 동안 잠시 멈추고 있지요.

줄37에서는 클라이언트에게서 명령을 받기를 기다리고 있다는 것을 보여주기 위해 "wating..."이라고 터미널에 출력하고 있어요.
줄38은 클라이언트에서 전송하는 키보드 명령을 받기 위한 코드예요. 소켓의 recv()메서드를 사용했는데요, 전달인자로는 5를 부여함으로써 한 번에 최대 5 byte 크기의 메세지를 받을 수 있게 하였답니다.
줄39는 클라이언트로부터 키보드 명령을 받자마자, 잘 받았다고 다시 클라이언트에게 메세지를 전송하는 코드예요. 소켓의 send()을 이용해서 클라이언트에게 전송하는데, utf-8으로 인코딩을 하고 있지요.
줄40에서는, 줄38에서 key변수에 받아둔 클라이언트의 키보드 명령이 utf-8로 인코딩 된 상태로 오기 때문에 디코딩을 해서 다시 key변수에 할당하고 있어요.
줄42에서는 명령(이동방향)의 종류를 저장하는 변수 idx_move를 0으로 초기화 하고 있어요.
줄43~줄44에서는 변수 key가 가지고 있는 값이 문자 'l'과 같다면, 첫번째 이동방향인 오른쪽을 명령받은 것으로 인식하는 것이죠.
줄45~줄46은 위쪽('i'키), 줄47~줄48은 왼쪽('j'키), 줄49~줄50은 아래쪽('k'키) 이동 명령을 인식하는 것이랍니다.
줄51~줄53은 변수 key가 'q'를 가지고 있을 때, 클라이언트와 연결을 유지하는 소켓 conn을 close()메서드로 닫고, break를 이용하여 무한루프를 탈출하는 것이랍니다.
줄55에서는 클라이언트의 키보드 명령을 통해 정해진 이동방향 idx_move를 키클봇의 move_bot() 메서드에 전달하면서 호출하고 있지요. 이를 통해 키클봇 객체는 자신의 위치를 idx_move의 방향으로 이동시키게 되는 것이구요.




원격 조종 실행하기

이제, 키클봇의 원격 조종을 실행 해볼까요?
서버와 클라이언트 Python파일들을 저장하고, 터미널에서 "python3 lec12_kiklebot.py"로 키클봇 서버를 실행해 보세요.
그리고, 아래 보이는 것처럼 터미널 창 위의 아무 지점에서나 마우스 오른쪽 버튼을 누르세요. 그러면 새로 열리는 팝업 메뉴에서 "Open Terminal"을 클릭해서 새 터미널 창을 여세요.

그리고, "python3 lec12_cmd.py"로 클라이언트를 실행해 보세요. (실행 방법에 대해서 복습이 필요하면 여기로)
실행하면, 강의 키클봇 코딩하기에서도 봤듯이, 아래와 같이 클라이언트를 실행한 터미널 창 위에 figure창이 생성되지요.
이렇게 Figure창이 활성화되면, 키보드 명령을 내릴 수가 없으니, 아래 보이는 화살표 표시 처럼, 클라이언트를 실행한 터미널 창을 마우스 왼쪽버튼 클릭하여 활성화 상태로 만드세요.

그리고, 'i', 'j', 'k', 'l' 키들을 누르면, figure 창에 움직이는 키로봇이 보인답니다. 즉, 클라이언트를 통해 원격으로 명령을 보내고, 그 명령을 받은 서버가 키클봇에게 전달하여 키클봇이 움직이는 것이지요.
원격 조종을 멈추고 싶다면, 'q'를 눌러서 무한루프 탈출해야 하는 것 잊지 마세요!

강의에서 작성된 소스 코드 (source code)를 다운받으려면, 다음 링크를 클릭하세요: 키클봇 서버 소스 코드 다운로드, 명령 클라이언트 소스 코드 다운로드
혹시, 이해가 잘 안되는 부분에 대한 질문이 있거나 다루어 줬으면 하는 주제가 있으면, 화면 오르쪽 하단에 "질문하기" 버튼을 이용해 주세요.






발자취

2019-09-11 "키클 코딩랩 - 미국 공학박사 아빠의 코딩 연구소"로 이름 변경
2019-06-28 코딩 교실 공개
2019-03-18 코딩 교실 제작 시작

바로가기
Python 배움터
C/C++ 배움터
About
Contact
Privacy Policy
강의목록
질문하기
처음으로