산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

Arduino 로봇(PS2 컨트롤러 포함)(PlayStation 2 조이스틱)

구성품 및 소모품

Arduino UNO
× 1
SparkFun 듀얼 H-Bridge 모터 드라이버 L298
× 1
DIY 로봇 탱크 키트
× 1
Sony PS2 무선 컨트롤러
× 1

앱 및 온라인 서비스

Arduino IDE

이 프로젝트 정보

이 튜토리얼에서는 무선 PlayStation 2(PS2) 조이스틱을 사용하여 로봇 탱크를 조종하는 방법을 보여 드리겠습니다. 이 프로젝트의 핵심에는 Arduino Uno 보드가 사용되었습니다. 무선 컨트롤러로부터 명령을 받아 모터의 속도를 설정합니다. 다른 개발 보드(NodeMCU, Firebeetle 등)도 사용할 수 있으며 이 튜토리얼에서 제시하는 원칙은 로봇 및 장치의 다른 모델에 적용될 수 있습니다.

저는 이전에 Blynk로 제어되는 로봇 탱크를 설계한 적이 있습니다. Wi-Fi 네트워크에 연결하고 Blynk 서버에서 명령을 받습니다. Blynk 앱을 실행하는 스마트폰을 리모컨으로 사용했으며 푸시 버튼, 슬라이딩 바, 스마트폰의 가속도계 등 다양한 입력 방식을 사용했습니다. 이 프로젝트에 대한 자세한 내용은 https://www.hackster.io/igorF2/wi-fi-controlled-robot-using-wemos-d1-esp8266-and-blynk-464198

에서 확인할 수 있습니다.

또한 음성 명령으로 몇 가지 실험을 했습니다. 손을 사용하지 않고 로봇을 원격으로 제어하려는 경우 또는 움직임이 제한된 사람이 로봇에 액세스할 수 있도록 하려는 경우에 유용할 수 있습니다. 예를 들어 로봇 음성 제어 휠체어를 생각할 수 있습니다. DIY 로봇 키트와 내가 가장 좋아하는 도구인 Adafruit.io, IFTTT 및 Arduino IDE가 사용되었습니다. 전체 지침:

https://www.hackster.io/igorF2/wi-fi-voice-controlled-robot-using-google-assistant-79802c

3D 프린터 및 레이저 절단기와 같은 복잡한 도구를 사용할 필요 없이 간단한 재료를 사용하여 다른 키트를 사용하거나 자체 로봇을 설계할 수도 있습니다. 이전 자습서 중 하나에서 예제를 찾을 수 있습니다.

https://www.hackster.io/igorF2/widc-wi-fi-controlled-fpv-robot-8f1e09

1단계:도구 및 재료

이 프로젝트에 사용된 도구는 다음과 같습니다.

<울>
  • 납땜 인두 및 와이어 (링크/링크). DC 모터는 이미 단자에 납땜된 전선과 함께 제공됩니다. 하지만 결국에는 파손되어 다시 납땜해야 할 수도 있습니다. 따라서 좋은 땜납 인두와 와이어를 가까이에 두는 것을 고려하십시오.
  • EVA 발포 시트 (또는 기타 비전도성 물질). 이번 프로젝트에서 사용한 로봇 섀시는 알루미늄으로 되어 있고, 이 금속 부품에 회로 기판이 설치되어 있습니다. 나는 가능한 합선을 피하기 위해 보드와 금속판 사이에 폼 시트 층을 사용했습니다.
  • 양면 테이프 . 폼 시트를 회로 기판에 접착하고 H-Bridge 모드를 설치하는 데 사용되었습니다.
  • 가위 , 일부 발포 시트 직사각형 절단용.
  • 내 프로젝트에 다음 하드웨어 부품을 사용했습니다.

    <울>
  • Arduino Uno 기반 개발 보드 (링크/링크/링크/링크). 로봇의 메인 컨트롤러로 사용됩니다. Arduino IDE로 사용 및 프로그래밍이 정말 간편하여 전자공학 및 프로그래밍 초보자에게 적합합니다.
  • <울>
  • L298N 듀얼 채널 H 브리지 모듈 (링크/링크/링크/링크). 이 모듈을 사용하면 Wemos(또는 Arduino)의 3.3V 신호를 모터에 필요한 12V로 증폭할 수 있습니다.
  • <울>
  • DIY 로봇 섀시 탱크 (링크). 이 멋진 키트에는 2개의 DC 모터, 기어, 트랙, 볼트, 너트 등 탱크를 만드는 데 필요한 모든 것이 들어 있습니다. 섀시 조립에 필요한 도구가 이미 포함되어 있어 초보자에게 좋습니다!
  • <울>
  • PS2 무선 리모컨 (링크). 이 비디오 게임 컨트롤러는 직렬 통신을 사용하여 마이크로 컨트롤러와 인터페이스할 수 있는 수신기에 무선으로 명령을 보낼 수 있습니다.
  • <울>
  • 18650 3.7V 배터리(x3) (링크/링크). 나는 전체 회로에 전원을 공급했습니다. 이 탱크는 12V 모터를 사용합니다. 전원을 공급하기 위해 직렬로 3개의 3.7V 배터리를 사용했습니다.
  • <울>
  • 3S 18650 배터리 홀더 (링크/링크/링크). 18650 배터리 3개를 직렬로 장착할 수 있으며 탱크 뒷면에 쉽게 부착할 수 있습니다.
  • <울>
  • 18650 배터리 충전기 (링크/링크). 배터리는 결국 전원이 소모됩니다. 그럴 때 배터리 충전기가 당신을 구해줄 것입니다.
  • 점퍼 (링크/링크). 나는 h-bridge와 Wemos 사이의 신호를 위해 6개의 남성-여성 점퍼를 사용하고 5V와 Gnd를 위해 2개의 남성-남성 점퍼를 사용했습니다. 일부 센서를 추가하려는 경우 더 필요할 수 있습니다.
  • B형 USB 케이블 . 코드를 업로드할 때 필요합니다. 대부분의 보드는 이미 자체 케이블과 함께 제공됩니다.
  • 위의 링크는 이 튜토리얼에서 사용된 항목을 찾을 수 있는 위치에 대한 제안일 뿐입니다(그리고 향후 튜토리얼을 지원할 수도 있음). 다른 곳에서 자유롭게 검색하고 좋아하는 지역 또는 온라인 상점에서 구입하십시오.

    2단계:로봇 조립

    이 프로젝트의 첫 번째 부분은 로봇 구조의 조립이었습니다.

    이전 프로젝트에서 나는 쉽게 접근할 수 있는 재료를 사용하여 내 로봇의 구조를 개발했습니다(복잡한 도구, 3D 프린터 또는 레이저 절단 기계가 필요 없음). 이 프로젝트는 아래 링크에서 찾을 수 있습니다.

    https://www.hackster.io/igorF2/widc-wi-fi-controlled-fpv-robot-8f1e09

    나중에 온라인 상점에서 로봇 키트를 구할 기회를 주기로 했습니다. 다음 링크에서 로봇 섀시에 대한 링크를 찾을 수 있습니다. http://bit.ly/2ycI8fP. 키트를 찾으신다면 좋은 선택이라고 생각합니다! 아래 링크에서 볼 수 있듯이 두 개의 다른 자습서에서 사용했습니다.

    https://www.hackster.io/igorF2/wi-fi-controlled-robot-using-wemos-d1-esp8266-and-blynk-464198

    https://www.hackster.io/igorF2/wi-fi-voice-controlled-robot-using-google-assistant-79802c

    처음에는 어셈블리가 복잡하거나 부품 부족(키트를 구성하는 부품의 양을 감안할 때)과 같은 문제가 발생할 것 같았습니다. 하지만 이 키트는 정말 저를 놀라게 했습니다! 모든 부품이 좋은 품질로 보이며 여러 예비 부품이 키트와 함께 제공됩니다. 따라서 작업대 아래에 나사가 없어도 귀하의 프로젝트를 수행하는 것이 불가능하지는 않습니다. 특히 나사 몇 개를 분실한 후에는 훌륭했습니다.

    또 다른 긍정적인 점은 로봇을 장착하는 데 필요한 모든 도구가 키트에 포함되어 있다는 것입니다(2개의 앨런 렌치와 드라이버). 도구가 많지 않은 초보자에게 이 키트가 탁월하다고 생각합니다!

    부정적인 측면으로 나는 문서의 부족을 강조하고 싶습니다. 로봇 조립맨

    ual(중국어 스프레드시트 파일)은 사용자 친화적이지 않으며 해당 탱크에 대한 온라인 자습서가 많지 않습니다. 그래서 위의 비디오에서 로봇을 조립하는 과정을 기록하기로 결정했습니다! 또 다른 주의점은 로봇 구조의 재료에 관한 것입니다. 베이스 전체가 알루미늄으로 되어 있어 인쇄 회로 기판의 핀이 프레임에 닿으면 단락될 수 있습니다.

    사용할 수 있는 다른 온라인 키트가 있습니다. 아래에서 설명한 대로 자신만의 구조를 만들 수도 있습니다.

    3단계:회로 배선

    3개의 18650 배터리로 구성된 파워팩이 로봇 뒷면에 설치되었습니다. 로봇에 11.1V(3 x 3.7V)를 제공합니다. 12V DC 모터에 전원을 공급하기에 충분합니다.

    모터 제어에는 L298N 듀얼 채널 H 브리지가 사용되었습니다. Arduino 보드의 일부 5V 신호를 수신하고 모터에 더 높은 전압을 제공합니다. 또한 입력 신호의 조합에 따라 모터가 양방향으로 작동할 수 있습니다.

    각 장치는 회로도에 따라 연결되었습니다.

    연결해야 하는 핀 목록을 따릅니다.

    Arduino Uno 입력/출력:

    <울>
  • 디지털 핀 3 => H-브리지 ENA 핀
  • 디지털 핀 5 => H-Bridge IN1 핀
  • 디지털 핀 4 => H-Bridge IN2 핀
  • 디지털 핀 8 => H-Bridge IN3 핀
  • 디지털 핀 7 => H-Bridge IN4 핀
  • 디지털 핀 6 => H-Bridge ENB 핀
  • 5V 핀 => H-브리지 5V 핀
  • 접지 핀 => H 브리지 접지 핀
  • 디지털 핀 10 => PS2 수신기 핀 6
  • 디지털 핀 11 => PS2 수신기 핀 2
  • 디지털 핀 12 => PS2 수신기 핀 1
  • 디지털 핀 13 => PS2 수신기 핀 7
  • 3.3V 핀 => PS2 수신기 핀 5
  • 접지 핀 => PS2 수신기 핀 4
  • H-Bridge 입력/출력:

    <울>
  • ENA 핀 => Arduino 디지털 핀 3
  • IN1 핀 => Arduino 디지털 핀 5
  • IN2 핀 => Arduino 디지털 핀 4
  • IN3 핀 => Arduino 디지털 핀 8
  • IN4 핀 => Arduino 디지털 핀 7
  • ENB 핀 => Arduino 디지털 핀 6
  • 5V 핀 => Arduino 5V 핀
  • Gnd 핀 => Arduino Gnd 핀
  • 접지 핀 => 배터리 팩 음극선
  • 12V 핀 => 배터리 팩 양극 와이어
  • OUT1 => 오른쪽 모터 음극선
  • OUT2 => 오른쪽 모터 양극 와이어
  • OUT3 => 왼쪽 모터 양극
  • OUT4 => 왼쪽 모터 음극선
  • PS2 수신기:

    <울>
  • 핀 1(데이터) => Arduino 디지털 핀 12
  • 핀 2(명령) => Arduino 디지털 핀 11
  • 핀 4(접지) => Arduino Gnd 핀
  • 핀 5(전원) => Arduino 3.3V 핀
  • 6번 핀(주의) => Arduino 디지털 핀 10번
  • 핀 7(시계) => Arduino 디지털 핀 13
  • 4단계:Arduino IDE 설정

    이 프로젝트에서는 Arduino 보드를 프로그래밍하기 위해 Arduino IDE를 사용했습니다.

    1. Arduino IDE 최신 버전 다운로드 및 설치

    Arduino 웹사이트:https://www.arduino.cc/en/main/software

    에서 Windows, Linux 또는 MAC OSX용 최신 버전을 찾을 수 있습니다.

    무료로 다운로드하여 컴퓨터에 설치하고 실행하십시오.

    2. 라이브러리 추가

    이 프로젝트에서는 Arduino PS2X 라이브러리를 사용합니다.

    https://github.com/madsci1016/Arduino-PS2X에서 라이브러리를 다운로드하세요. 파일의 압축을 풀고 폴더를 Arduino IDE 라이브러리/도구 폴더에 복사합니다.

    ............

    이제 개발 환경이 준비되었으므로 다음 단계로 넘어갑시다!

    5단계:PS2 무선 컨트롤러 라이브러리 설명

    PS2 무선 컨트롤러는 로봇 공학 프로젝트를 위한 훌륭한 도구입니다. 20개의 디지털 푸시 버튼과 2개의 아날로그 스틱 등 엄청난 수의 버튼이 있습니다. 이런 식으로 로봇을 제어할 수 있는 무한한 가능성이 있습니다.

    Bill Porter의 PS2X 라이브러리(https://github.com/madsci1016/Arduino-PS2X)를 사용하여 컨트롤러를 Arduino Uno 보드에 연결했습니다.

    조이스틱에서 아날로그 및 디지털 입력을 읽는 몇 가지 방법이 포함된 컨트롤러용 PS2X 클래스를 정의합니다. 이 클래스에 대한 개체는 다음 코드를 사용하여 생성됩니다(설정 전 또는 도중):

    <사전><코드>PS2X ps2x;

    객체가 정의되면 다음 기능을 사용하여 Arduino I/O 핀에 연결해야 합니다.

    <사전><코드>오류 =ps2x.config_gamepad(시계, 명령, 주의, 데이터, 압력?, 럼블?);

    이 함수는 연결이나 컨트롤러 자체에 문제가 있는 경우 일부 오류를 반환할 수 있습니다.

    라이브러리에서 사용되는 주요 기능은 디지털 및 아날로그 입력을 읽는 기능입니다. 읽기 및 디지털 입력에는 다음 방법이 사용됩니다.

    <사전><코드>ps2x.Button(버튼);

    어떤 버튼 에서 읽을 버튼의 이름입니다. 각 버튼은 다음과 같이 매핑됩니다.

    방향 버튼:

    <울>
  • PSB_PAD_UP =방향 패드 위로 버튼;
  • PSB_PAD_DOWN =방향 패드 아래쪽 버튼
  • PSB_PAD_LEFT =방향 패드 왼쪽 버튼
  • PSB_PAD_RIGHT =방향 패드 오른쪽 버튼
  • 작업 버튼:

    <울>
  • PSB_GREEN 또는 PSB_TRIANGLE =녹색 삼각형 버튼
  • PSB_RED 또는 PSB_CIRCLE =빨간색 원 버튼
  • PSB_BLUE 또는 PSB_CROSS =파란색 x 버튼
  • PSB_PINK 또는 PSB_SQUARE =​​분홍색 사각형 버튼
  • 트리거:

    <울>
  • PSB_L1 =왼쪽 트리거 버튼 1
  • PSB_R1 =오른쪽 트리거 버튼 1
  • PSB_L2 =왼쪽 트리거 버튼 2
  • PSB_R2 =오른쪽 트리거 버튼 2
  • 스틱:

    <울>
  • PSB_L3 =왼쪽 스틱 푸시 버튼(예, 스틱을 푸시 버튼으로 누를 수 있음)
  • PSB_R3 =오른쪽 스틱 푸시 버튼
  • 메뉴:

    <울>
  • PSB_SELECT =선택 버튼
  • PSB_START =시작 버튼
  • 스틱의 아날로그 값(0에서 255 사이의 정수로 변환기 사용)을 읽으려면 다음 방법을 사용하십시오.

    <사전><코드>ps2x.Analog(stick_axis);

    어느 stick_axis에서 다음과 같이 막대와 읽을 방향을 나타냅니다.

    <울>
  • PSS_LY =왼쪽 스틱의 y-위치
  • PSS_LX =왼쪽 스틱의 x 위치
  • PSS_RY =오른쪽 스틱의 y-위치
  • PSS_RX =오른쪽 스틱의 x 위치
  • 이러한 기본 기능을 통해 프로젝트에서 PS2 컨트롤러를 사용할 수 있습니다! 다음 단계에서는 이러한 버튼을 결합한 몇 가지 Arduino 스케치 예제를 보여줍니다!

    6단계:스케치 #1 - Dpad 버튼

    SEGA Master System, Mega Drive, Nintendo SNES와 같은 오래된 비디오 게임에는 강제 버튼이 없었습니다. 방향 버튼은 온/오프 버튼뿐이었습니다. 이 첫 번째 예에서는 콘솔의 오래된 비디오 게임에 있는 종류의 컨트롤을 에뮬레이트하고 싶었습니다.

    이 첫 번째 스케치에서는 Dpad 버튼을 로봇에 대한 입력으로 사용했습니다. 각 버튼은 로봇이 앞으로 이동, 우회전, 좌회전 또는 뒤로 이동과 같은 기본 동작 중 하나를 수행하는 데 사용되었습니다.

    이러한 버튼에는 바이너리 출력이 있습니다. 이런 식으로 마이크로컨트롤러는 버튼을 눌렀다는 것만 이해하고 얼마나 세게 눌렀는지 이해하지 못합니다. 이렇게 하면 주어진 버튼을 클릭하면 모터가 최고 속도로 작동합니다.

    내 회로도에 따라 모든 것을 연결했다면 다음과 같은 출력이 표시됩니다.

    <울>
  • PSB_PAD_UP =앞으로 이동
  • PSB_PAD_DOWN =뒤로 이동
  • PSB_PAD_LEFT =좌회전
  • PSB_PAD_RIGHT =우회전
  • 앞에서 말했듯이 모터는 고정된 속도로 움직입니다. 좌회전 또는 우회전 시 모터가 반대 방향으로 작동하여 로봇이 축을 중심으로 회전합니다.

    Arduino 코드:

    코드를 다운로드하고 Arduino IDE에서 엽니다. 내가 했던 것과 같은 핀아웃을 사용한다면 아마도 코드에서 아무 것도 변경할 필요가 없을 것입니다(모터의 방향이 다른 경우 필요할 수도 있지만

    코드를 업로드하려면 'Arduino/Genuino Uno' 보드를 선택하고 올바른 COM 포트를 선택하고 보드를 컴퓨터의 USB 포트에 연결하고 코드를 업로드합니다.
    업로드가 완료된 후 USB 케이블을 뽑고 배터리. 코드가 실행되기 시작하고 Arduino 보드가 자동으로 PS2 조이스틱을 연결합니다. 최초 사용 시 시리얼 모니터를 열어 연결 상태를 확인하십시오. 조이스틱과 연결되지 않으면 다음을 수행해야 합니다.

    <울>
  • Arduino Uno 보드를 재설정합니다. 여러 번 시도하십시오(저의 경우 일반적으로 세 번째 재설정 후에 작동함).
  • 조이스틱이 켜져 있고 배터리가 충전되어 있는지 확인합니다. 수신기에는 조이스틱이 페어링되었는지 여부를 나타내는 몇 가지 LED가 있습니다. 또한 Arduino의 RX 및 TX 핀이 새 명령을 수신할 때 깜박이는지 확인합니다(그리고 직렬 포트의 상태 업데이트).
  • 연결을 확인하십시오... 구성 요소 사이의 점퍼에 문제가 있을 수 있습니다.
  • 코드 설명:

    이 프로젝트에서는 PS2X_lib.h 라이브러리만 사용해야 했습니다. I는 코드 시작 부분에 추가됩니다.

    #include   

    h-bridge에 연결된 Arduino의 핀을 정의합니다. 내가 했던 것과 동일한 연결을 사용한다면 다음과 같은 구성을 갖게 될 것입니다. 다른 핀을 사용하려면 코드의 이 부분을 업데이트하세요.

    // 브리지 드라이버의 방향을 설정하는 데 사용됩니다. #define ENA 3 //ENA #define MOTORA_1 4 //IN3 #define MOTORA_2 5 //IN4 #define MOTORB_1 8 //IN1 #define MOTORB_2 7 //IN2 #define ENB 6 //ENB  

    일부 전역 변수는 설정 전에 선언되었습니다(p2sx , 오류 , 유형 진동 ). 첫 번째는 PS2X 클래스의 인스턴스입니다. 연결 중 오류 상태는 error에 저장됩니다. 정수. 유형 진동 컨트롤러의 유형(DualShock 또는 GuitarHero 컨트롤러)과 지정된 명령에 따라 진동해야 하는지 여부를 나타냅니다.

    설정 중에 내가 한 첫 번째 일은 I/O 핀 상태(출력으로)를 구성하고 활성화 핀을 LOW로 설정하는 것이었습니다. 이렇게 하면 시작 시 두 모터를 모두 비활성화하고 나머지 코드를 기다리는 동안 로봇은 무작위로 움직이지 않습니다.

    // 출력 핀 구성 pinMode(ENA, OUTPUT); 핀모드(MOTORA_1, 출력); 핀모드(MOTORA_2, 출력); 핀모드(ENB, 출력); 핀모드(MOTORB_1, 출력); 핀모드(MOTORB_2, 출력); // 두 모터 모두 비활성화 digitalWrite(ENA,0); 디지털 쓰기(ENB,0);  

    그런 다음 직렬 포트 통신을 시작했습니다(코드 디버깅에 유용함). 적절한 전송 속도를 선택하고 직렬 모니터에서 동일한 속도를 설정하십시오.

    // 시리얼 통신 시작 Serial.begin(57600);  

    마지막으로 조이스틱 수신기(각각 시계, 명령, 주의 및 데이터)에 연결된 Arduino의 핀으로 ps2x 개체를 구성합니다. 오류를 반환할 수 있습니다(직렬 모니터에 표시됨). 컨트롤러의 유형도 표시되지 않습니다(이 시점에서 손에 들고 있는 컨트롤러의 유형을 이미 알고 있을 것입니다 :D).

    <사전><코드>오류 =ps2x.config_gamepad(13,11,10,12, 참, 참); // 핀 및 설정 설정:GamePad(clock, command, Attention, data, Pressures?, Rumble?) 오류 확인 // 오류 확인 if(error ==0){ Serial.println("컨트롤러 발견, 구성 성공" ); } else if(error ==1) Serial.println("컨트롤러를 찾을 수 없습니다. 배선을 확인하거나 Arduino를 재설정하십시오."); else if(error ==2) Serial.println("컨트롤러를 찾았지만 명령을 수락하지 않습니다."); else if(error ==3) Serial.println("컨트롤러가 압력 모드 진입을 거부하고 있습니다. 지원하지 않을 수 있습니다."); // 컨트롤러 유형 확인 type =ps2x.readType(); switch(type) { 사례 0:Serial.println("알 수 없는 컨트롤러 유형"); 부서지다; 사례 1:Serial.println("DualShock 컨트롤러 발견"); 부서지다; 사례 2:Serial.println("GuitarHero 컨트롤러를 찾았습니다"); 부서지다; }

    메인 루프 동안 Arduino는 각 D-패드 버튼(UP, DOWN, LEFT 및 RIGTH)을 읽고 휠을 움직이기 위해 출력 핀에 대해 다른 값을 설정합니다. 빠른 지연 후 새 루프가 시작됩니다.

    다음 섹션은 UP 버튼을 눌렀을 때 로봇을 앞으로 이동시키는 방법을 보여줍니다. MOTORA_1 및 MOTORA_2 핀은 모터 A의 회전 방향을 정의합니다. ENA는 이동이 활성화되는지 여부(모터 ON/OFF)를 정의합니다. "아날로그" 출력(실제로는 PWM 출력)임을 주목하십시오. 값은 0에서 1023 사이여야 합니다. 이것은 PWM을 변조하고 h-brigde의 출력에서 ​​평균 전압을 변경합니다(0에서 12V 사이). 평균 전압이 너무 낮으면 모터가 전혀 움직일 수 없습니다.

    // 앞으로 이동 if(ps2x.Button(PSB_PAD_UP)) { digitalWrite(MOTORA_1,LOW); 디지털 쓰기(MOTORA_2,HIGH); 디지털 쓰기(MOTORB_1,HIGH); 디지털 쓰기(MOTORB_2,LOW); analogWrite(ENB, 1023); analogWrite(ENA, 1023); Serial.println("앞으로 이동"); }  

    *ENA 및 ENB 핀에 PWM 출력을 사용했습니다. 이런 식으로 1023은 가장 긴 길이(핀은 항상 켜짐)이고 0은 펄스가 없는(핀은 항상 꺼짐) 펄스를 나타냅니다. 여기에 다른(더 작은) 값을 사용하면 터미널의 평균 전압이 더 작아지기 때문에 모터가 더 느리게 작동할 것으로 예상됩니다.

    7단계:스케치 #2 - 아날로그 스틱 및 디지털 버튼(L2 및 R2)

    주어진 시간에 모든 콘솔은 두 개의 아날로그 스틱이 있는 조이스틱을 채택했습니다. 이러한 새로운 입력은 가변 속도 동작을 생성하고 모든 종류의 비디오 게임에서 대중적인 힘에 민감한 입력을 허용했습니다.

    이 두 번째 예에서는 탱크를 조종하기 위해 아날로그 스위치를 사용하고 스로틀과 브레이크/후진으로 한 쌍의 푸시 버튼을 사용하고 싶었습니다. 이러한 종류의 구성은 예를 들어 Mario Kart와 같은 많은 레이싱 게임에서 매우 인기가 있습니다. 탱크를 가속하려면 R2 버튼을 누르고 후진하려면 L2 버튼을 누르고 왼쪽 스틱의 x 위치를 기준으로 이동 방향을 설정합니다.

    내가 사용한 종류의 로봇 키트에는 관성이 많지 않습니다(모터의 전압이 0V로 설정되어 있으면 한동안 계속 움직이지 않습니다). 이런 식으로 로봇을 제동하는 명령을 정의할 필요가 없었습니다.

    대부분의 코드는 이미 이전 단계에서 설명했습니다. 한 가지 차이점은 각 모터의 속도(0에서 1023까지)를 저장하는 두 개의 변수를 정의했다는 것입니다.

    int motor_right_speed =0;
    int motor_left_speed =0;

    Main 루프에서 Arduino는 왼쪽 스틱의 x 위치를 읽습니다. 0에서 255 사이의 값을 받고 -1023에서 1023 사이로 매핑합니다.

    이 값은 각 모터의 속도에 추가(또는 빼기)되어 각 트랙의 속도 사이에 차이를 만들고 로봇이 왼쪽 또는 오른쪽으로 회전하도록 합니다.

    <사전><코드>int nJoyL =ps2x.Analog(PSS_LX); // 왼쪽 스틱 읽기 nJoyL =map(nJoyL, 0, 255, 1023, -1023); motor_right_speed =1023; motor_left_speed =1023; if (nJoyL <0) { motor_right_speed =motor_right_speed + nJoyL; } if (nJoyL> 0) { motor_left_speed =motor_left_speed - nJoyL; }

    R2 버튼을 누르면 탱크가 앞으로 이동합니다. 이런 식으로 Arduino는 모터 핀에 적절한 값을 설정합니다. ENA와 ENB는 각 모터에 대해 원하는 속도에 비례하는 값으로 각 모터에 '아날로그' 출력을 생성합니다(왼쪽 스틱의 x 위치 기준).

    L2도 비슷한 작업을 수행하지만 모터의 방향을 반대로 합니다.

    <사전><코드>if(ps2x.Button(PSB_R2)) { digitalWrite(MOTORA_1,LOW); 디지털 쓰기(MOTORA_2,HIGH); 디지털 쓰기(MOTORB_1,HIGH); 디지털 쓰기(MOTORB_2,LOW); analogWrite(ENA, motor_right_speed); analogWrite(ENB, motor_left_speed); } if(ps2x.Button(PSB_L2)) { digitalWrite(MOTORA_1,HIGH); 디지털 쓰기(MOTORA_2,LOW); 디지털 쓰기(MOTORB_1,LOW); 디지털 쓰기(MOTORB_2,HIGH); analogWrite(ENA, motor_right_speed); analogWrite(ENB, motor_left_speed); } if(!ps2x.Button(PSB_L2) ​​&&!ps2x.Button(PSB_R2)) { analogWrite(ENA, 0); 아날로그 쓰기(ENB, 0); }

    아무 버튼도 누르지 않으면 모터가 비활성화됩니다.

    8단계:스케치 #3 - 왼쪽 및 오른쪽 아날로그 스틱(차동 드라이브)

    이것은 탱크를 제어하는 ​​내가 가장 좋아하는 방법입니다(리소스 측면에서 최적은 아니지만)! 두 손으로 진짜 큰 기계를 조종하는 것 같은 기분이 들거든요!

    이 코드에서는 두 스틱을 모두 사용하여 각 트랙의 방향과 속도를 설정했습니다. 왼쪽 스틱은 왼쪽 트랙을 제어하고 오른쪽 스틱은 오른쪽 트랙을 제어합니다.

    코드는 앞에서 설명한 것과 거의 동일합니다. 하지만 이번에는 두 막대기의 y-위치를 읽고 싶었습니다.

    <사전><코드>int nJoyL =ps2x.Analog(PSS_LY); // 왼쪽 스틱 읽기 int nJoyR =ps2x.Analog(PSS_RY); // 오른쪽 스틱 읽기 nJoyL =map(nJoyL, 0, 255, 1023, -1023); nJoyR =맵(nJoyR, 0, 255, -1023, 1023);

    스틱의 위치에 따라 코드는 디지털 핀을 설정하여 각 모터의 방향을 설정합니다.

    활성화(1023)에 고정 값을 사용했지만 아날로그 스틱의 실제 값을 기반으로 한 가변 값을 사용합니다. 마이크로컨트롤러가 모터를 비활성화하는 데드밴드(+-50)를 정의했습니다.

    // 두 아날로그 스틱을 기반으로 동작 수행 if(nJoyR>50) { digitalWrite(MOTORA_1,HIGH); 디지털 쓰기(MOTORA_2,LOW); analogWrite(ENA, 1023); } if(nJoyR<-50) { digitalWrite(MOTORA_1,LOW); 디지털 쓰기(MOTORA_2,HIGH); analogWrite(ENA, 1023); } if (abs(nJoyR)<50) { analogWrite(ENA, 0); }  

    9단계:스케치 #4 - 왼쪽 아날로그 스틱만

    이 마지막 Sketch 앱에서는 단일 조이스틱을 사용하여 로봇을 제어했습니다. 리소스 사용 측면에서 최적이지만(다양한 작업을 구성할 수 있는 많은 버튼이 남아 있음) 이해하기가 상당히 어렵습니다.

    각 트랙의 속도는 조이스틱의 x 및 y 위치에 따라 정의됩니다. 이런 식으로 각 트랙에 다른 속도를 적용하고 다른 속도/방향으로 로봇을 움직일 수 있습니다. 여기에 사용된 코드는 다음 자습서에서 파생되었습니다. 확인해 보세요!

    https://www.impulseadventure.com/elec/robot-differential-steering.html

    10단계:최종 고려 사항

    나는 이 프로젝트를 가능한 한 명확하고 교육적으로 만들기 위해 많은 노력을 기울였습니다. 도움이 되셨다면 '좋아요'와 '공유'도 잊지 마세요. 당신의 지원은 나에게 매우 중요합니다! :D

    여전히 내 튜토리얼을 따르지 않는다면 로봇에 대한 다른 튜토리얼을 살펴보십시오! 마음에 드셨으면 좋겠습니다!

    https://www.hackster.io/igorF2/joy-robot-robo-da-alegria-bba54f

    https://www.hackster.io/igorF2/widc-wi-fi-controlled-fpv-robot-8f1e09

    https://www.hackster.io/igorF2/nunchuk-controlled-robotic-arm-with-arduino-b1c0fa

    https://www.hackster.io/igorF2/wi-fi-browser-controlled-robotic-arm-89b63d

    작은 비트코인 ​​기부로 내 미래 프로젝트를 지원하는 것을 고려해 주세요! :D

    BTC 입금 주소:1FiWFYSjRaL7sLdr5wr6h86QkMA6pQxkXJ

    <섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

    코드

    <울>
  • 스케치 #1
  • 스케치 #2
  • 스케치 #3
  • 스케치 #4
  • 스케치 #1Arduino
    // Igor Fonseca의 PS2 Tank @2019// D-패드 버튼을 사용하여 PS2 조이스틱을 사용하여 로봇 탱크를 제어합니다.// Bill Porter 2011의 PS2X 라이브러리를 사용하는 예를 기반으로 합니다.// 위의 모든 텍스트가 포함되어야 합니다. 모든 재배포에서.// 라이브러리 포함#include // 브리지 드라이버의 방향을 설정하는 데 사용됩니다.#define ENA 3 //ENA#define MOTORA_1 4 //IN3#define MOTORA_2 5 //IN4 #define MOTORB_1 8 //IN1#define MOTORB_2 7 //IN2#define ENB 6 //ENBPS2X ps2x; // PS2 컨트롤러 클래스 생성//지금 라이브러리는 핫 플러그형 컨트롤러를 지원하지 않습니다. 즉, //컨트롤러를 연결한 후에는 항상 Arduino를 다시 시작하거나 // 컨트롤러를 연결한 후 config_gamepad(pins)를 다시 호출해야 합니다. 정수 오류 =0; byte type =0;byte vibrate =0;void setup(){ // 출력 핀 구성 pinMode(ENA, OUTPUT); 핀모드(MOTORA_1, 출력); 핀모드(MOTORA_2, 출력); 핀모드(ENB, 출력); 핀모드(MOTORB_1, 출력); 핀모드(MOTORB_2, 출력); // 두 모터 모두 비활성화 digitalWrite(ENA,0); 디지털 쓰기(ENB,0); // 시리얼 통신 시작 Serial.begin(57600); 오류 =ps2x.config_gamepad(13,11,10,12, 참, 참); // 핀 및 설정 설정:GamePad(clock, command, Attention, data, Pressures?, Rumble?) 오류 확인 // 오류 확인 if(error ==0){ Serial.println("컨트롤러 발견, 구성 성공" ); } else if(error ==1) Serial.println("컨트롤러를 찾을 수 없습니다. 배선을 확인하거나 Arduino를 재설정하십시오."); else if(error ==2) Serial.println("컨트롤러를 찾았지만 명령을 수락하지 않습니다."); else if(error ==3) Serial.println("컨트롤러가 압력 모드 진입을 거부하고 있습니다. 지원하지 않을 수 있습니다."); // 컨트롤러 유형 확인 type =ps2x.readType(); switch(type) { case 0:Serial.println("Unknown Controller type"); 부서지다; case 1:Serial.println("DualShock Controller Found"); 부서지다; case 2:Serial.println("GuitarHero Controller Found"); 부서지다; }}// Main loopvoid loop(){ if(error ==1) //skip loop if no controller found return; else { //DualShock Controller ps2x.read_gamepad(false, vibrate); // disable vibration of the controller // Perform movements based on D-pad buttons // MOVE FORWARD if(ps2x.Button(PSB_PAD_UP)) { digitalWrite(MOTORA_1,LOW); digitalWrite(MOTORA_2,HIGH); digitalWrite(MOTORB_1,HIGH); digitalWrite(MOTORB_2,LOW); analogWrite(ENB, 1023); analogWrite(ENA, 1023); Serial.println("Move forward"); } // TURN RIGHT if(ps2x.Button(PSB_PAD_RIGHT)){ digitalWrite(MOTORA_1,HIGH); digitalWrite(MOTORA_2,LOW); digitalWrite(MOTORB_1,HIGH); digitalWrite(MOTORB_2,LOW); analogWrite(ENB, 1023); analogWrite(ENA, 1023); Serial.println("Turn right"); } // TURN LEFT if(ps2x.Button(PSB_PAD_LEFT)){ digitalWrite(MOTORA_1,LOW); digitalWrite(MOTORA_2,HIGH); digitalWrite(MOTORB_1,LOW); digitalWrite(MOTORB_2,HIGH); analogWrite(ENB, 1023); analogWrite(ENA, 1023); Serial.println("Turn left"); } // MOVE BACK if(ps2x.Button(PSB_PAD_DOWN)){ digitalWrite(MOTORA_1,HIGH); digitalWrite(MOTORA_2,LOW); digitalWrite(MOTORB_1,LOW); digitalWrite(MOTORB_2,HIGH); analogWrite(ENB, 1023); analogWrite(ENA, 1023); Serial.println("Move back"); } if (!ps2x.Button(PSB_PAD_DOWN) &&!ps2x.Button(PSB_PAD_UP) &&!ps2x.Button(PSB_PAD_RIGHT) &&!ps2x.Button(PSB_PAD_LEFT)) { analogWrite(ENB, 0); analogWrite(ENA, 0); } 지연(50); } }
    Sketch #2Arduino
    // PS2 Tank by Igor Fonseca @2019// Controls a robotic tank using a PS2 joystick, using D-pad buttons// based on an example using the PS2X library by Bill Porter 2011// All text above must be included in any redistribution.// include libraries#include // These are used to set the direction of the bridge driver.#define ENA 3 //ENA#define MOTORA_1 4 //IN3#define MOTORA_2 5 //IN4#define MOTORB_1 8 //IN1#define MOTORB_2 7 //IN2#define ENB 6 //ENBint motor_right_speed =0;int motor_left_speed =0;PS2X ps2x; // create PS2 Controller Class//right now, the library does NOT support hot pluggable controllers, meaning //you must always either restart your Arduino after you conect the controller, //or call config_gamepad(pins) again after connecting the controller.int error =0; byte type =0;byte vibrate =0;void setup(){ // Configure output pins pinMode(ENA, OUTPUT); pinMode(MOTORA_1, OUTPUT); pinMode(MOTORA_2, OUTPUT); pinMode(ENB, OUTPUT); pinMode(MOTORB_1, OUTPUT); pinMode(MOTORB_2, OUTPUT); // Disable both motors digitalWrite(ENA,0); digitalWrite(ENB,0); // Start serial communication Serial.begin(57600); error =ps2x.config_gamepad(13,11,10,12, true, true); //setup pins and settings:GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error // Check for error if(error ==0){ Serial.println("Found Controller, configured successful"); } else if(error ==1) Serial.println("No controller found, check wiring or reset the Arduino"); else if(error ==2) Serial.println("Controller found but not accepting commands"); else if(error ==3) Serial.println("Controller refusing to enter Pressures mode, may not support it."); // Check for the type of controller type =ps2x.readType(); switch(type) { case 0:Serial.println("Unknown Controller type"); 부서지다; case 1:Serial.println("DualShock Controller Found"); 부서지다; case 2:Serial.println("GuitarHero Controller Found"); 부서지다; }}// Main loopvoid loop(){ if(error ==1) //skip loop if no controller found return; else { //DualShock Controller ps2x.read_gamepad(false, vibrate); // disable vibration of the controller int nJoyL =ps2x.Analog(PSS_LX); // read left stick nJoyL =map(nJoyL, 0, 255, 1023, -1023); int motor_right_speed =1023; int motor_left_speed =1023; if (nJoyL <0) { motor_right_speed =motor_right_speed + nJoyL; } if (nJoyL> 0) { motor_left_speed =motor_left_speed - nJoyL; } if(ps2x.Button(PSB_R2)) { digitalWrite(MOTORA_1,LOW); digitalWrite(MOTORA_2,HIGH); digitalWrite(MOTORB_1,HIGH); digitalWrite(MOTORB_2,LOW); analogWrite(ENA, motor_right_speed); analogWrite(ENB, motor_left_speed); } if(ps2x.Button(PSB_L2)) { digitalWrite(MOTORA_1,HIGH); digitalWrite(MOTORA_2,LOW); digitalWrite(MOTORB_1,LOW); digitalWrite(MOTORB_2,HIGH); analogWrite(ENA, motor_right_speed); analogWrite(ENB, motor_left_speed); } if(!ps2x.Button(PSB_L2) &&!ps2x.Button(PSB_R2)) { analogWrite(ENA, 0); analogWrite(ENB, 0); } 지연(50); } }
    Sketch #3Arduino
    // PS2 Tank by Igor Fonseca @2019// Controls a robotic tank using a PS2 joystick, using D-pad buttons// based on an example using the PS2X library by Bill Porter 2011// All text above must be included in any redistribution.// include libraries#include // These are used to set the direction of the bridge driver.#define ENA 3 //ENA#define MOTORA_1 4 //IN3#define MOTORA_2 5 //IN4#define MOTORB_1 8 //IN1#define MOTORB_2 7 //IN2#define ENB 6 //ENBPS2X ps2x; // create PS2 Controller Class//right now, the library does NOT support hot pluggable controllers, meaning //you must always either restart your Arduino after you conect the controller, //or call config_gamepad(pins) again after connecting the controller.int error =0; byte type =0;byte vibrate =0;void setup(){ // Configure output pins pinMode(ENA, OUTPUT); pinMode(MOTORA_1, OUTPUT); pinMode(MOTORA_2, OUTPUT); pinMode(ENB, OUTPUT); pinMode(MOTORB_1, OUTPUT); pinMode(MOTORB_2, OUTPUT); // Disable both motors digitalWrite(ENA,0); digitalWrite(ENB,0); // Start serial communication Serial.begin(57600); error =ps2x.config_gamepad(13,11,10,12, true, true); //setup pins and settings:GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error // Check for error if(error ==0){ Serial.println("Found Controller, configured successful"); } else if(error ==1) Serial.println("No controller found, check wiring or reset the Arduino"); else if(error ==2) Serial.println("Controller found but not accepting commands"); else if(error ==3) Serial.println("Controller refusing to enter Pressures mode, may not support it."); // Check for the type of controller type =ps2x.readType(); switch(type) { case 0:Serial.println("Unknown Controller type"); 부서지다; case 1:Serial.println("DualShock Controller Found"); 부서지다; case 2:Serial.println("GuitarHero Controller Found"); 부서지다; }}// Main loopvoid loop(){ if(error ==1) //skip loop if no controller found return; else { //DualShock Controller ps2x.read_gamepad(false, vibrate); // disable vibration of the controller int nJoyL =ps2x.Analog(PSS_LY); // read left stick int nJoyR =ps2x.Analog(PSS_RY); // read right stick nJoyL =map(nJoyL, 0, 255, 1023, -1023); nJoyR =map(nJoyR, 0, 255, -1023, 1023); // Perform movements based on both analog sticks if(nJoyR>50) { digitalWrite(MOTORA_1,HIGH); digitalWrite(MOTORA_2,LOW); analogWrite(ENA, 1023); } if(nJoyR<-50) { digitalWrite(MOTORA_1,LOW); digitalWrite(MOTORA_2,HIGH); analogWrite(ENA, 1023); } if (abs(nJoyR)<50) { analogWrite(ENA, 0); } if(nJoyL>50) { digitalWrite(MOTORB_1,HIGH); digitalWrite(MOTORB_2,LOW); analogWrite(ENB, 1023); } if(nJoyL<-50) { digitalWrite(MOTORB_1,LOW); digitalWrite(MOTORB_2,HIGH); analogWrite(ENB, 1023); } if (abs(nJoyL)<50) { analogWrite(ENB, 0); } 지연(50); } }
    Sketch #4Arduino
    // PS2 Tank by Igor Fonseca @2019// Controls a robotic tank using a PS2 joystick, using left analog stick// based on an example using the PS2X library by Bill Porter 2011// All text above must be included in any redistribution.// include libraries#include  //for v1.6// These are used to set the direction of the bridge driver.#define ENA 3 //ENA#define MOTORA_1 4 //IN3#define MOTORA_2 5 //IN4#define MOTORB_1 8 //IN1#define MOTORB_2 7 //IN2#define ENB 6 //ENBint motor_right_speed =0;int motor_left_speed =0;PS2X ps2x; // create PS2 Controller Class//right now, the library does NOT support hot pluggable controllers, meaning //you must always either restart your Arduino after you conect the controller, //or call config_gamepad(pins) again after connecting the controller.int error =0; byte type =0;byte vibrate =0;void setup(){ // Configure output pins pinMode(ENA, OUTPUT); pinMode(MOTORA_1, OUTPUT); pinMode(MOTORA_2, OUTPUT); pinMode(ENB, OUTPUT); pinMode(MOTORB_1, OUTPUT); pinMode(MOTORB_2, OUTPUT); // Disable both motors digitalWrite(ENA,0); digitalWrite(ENB,0); // Start serial communication Serial.begin(57600); error =ps2x.config_gamepad(13,11,10,12, true, true); //setup pins and settings:GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error // Check for error if(error ==0){ Serial.println("Found Controller, configured successful"); } else if(error ==1) Serial.println("No controller found, check wiring or reset the Arduino"); else if(error ==2) Serial.println("Controller found but not accepting commands"); else if(error ==3) Serial.println("Controller refusing to enter Pressures mode, may not support it."); // Check for the type of controller type =ps2x.readType(); switch(type) { case 0:Serial.println("Unknown Controller type"); 부서지다; case 1:Serial.println("DualShock Controller Found"); 부서지다; case 2:Serial.println("GuitarHero Controller Found"); 부서지다; }}// Main loopvoid loop(){ if(error ==1) //skip loop if no controller found return; else { //DualShock Controller ps2x.read_gamepad(false, vibrate); // disable vibration of the controller int nJoyX =ps2x.Analog(PSS_LX); // read x-joystick int nJoyY =ps2x.Analog(PSS_LY); // read y-joystick nJoyX =map(nJoyX, 0, 255, -1023, 1023); nJoyY =map(nJoyY, 0, 255, 1023, -1023); // OUTPUTS int nMotMixL; // Motor (left) mixed output int nMotMixR; // Motor (right) mixed output // CONFIG // - fPivYLimt :The threshold at which the pivot action starts // This threshold is measured in units on the Y-axis // away from the X-axis (Y=0). A greater value will assign // more of the joystick's range to pivot actions. // Allowable range:(0..+127) float fPivYLimit =1023.0; // TEMP VARIABLES float nMotPremixL; // Motor (left) premixed output float nMotPremixR; // Motor (right) premixed output int nPivSpeed; // Pivot Speed float fPivScale; // Balance scale between drive and pivot // Calculate Drive Turn output due to Joystick X input if (nJoyY>=0) { // Forward nMotPremixL =(nJoyX>=0)? 1023.0 :(1023.0 + nJoyX); nMotPremixR =(nJoyX>=0)? (1023.0 - nJoyX) :1023.0; } else { // Reverse nMotPremixL =(nJoyX>=0)? (1023.0 - nJoyX) :1023.0; nMotPremixR =(nJoyX>=0)? 1023.0 :(1023.0 + nJoyX); } // Scale Drive output due to Joystick Y input (throttle) nMotPremixL =nMotPremixL * nJoyY/1023.0; nMotPremixR =nMotPremixR * nJoyY/1023.0; // Now calculate pivot amount // - Strength of pivot (nPivSpeed) based on Joystick X input // - Blending of pivot vs drive (fPivScale) based on Joystick Y input nPivSpeed =nJoyX; fPivScale =(abs(nJoyY)>fPivYLimit)? 0.0 :(1.0 - abs(nJoyY)/fPivYLimit); // Calculate final mix of Drive and Pivot nMotMixL =(1.0-fPivScale)*nMotPremixL + fPivScale*( nPivSpeed); nMotMixR =(1.0-fPivScale)*nMotPremixR + fPivScale*(-nPivSpeed); motor_left_speed =nMotMixL; motor_right_speed =nMotMixR; if (motor_right_speed> 50) { digitalWrite(MOTORB_1,HIGH); digitalWrite(MOTORB_2,LOW); } else if (motor_right_speed <-50) { digitalWrite(MOTORB_1,LOW); digitalWrite(MOTORB_2, HIGH); } else { digitalWrite(MOTORB_1, LOW); digitalWrite(MOTORB_2, LOW); } if (motor_left_speed> 50) { digitalWrite(MOTORA_1, LOW); digitalWrite(MOTORA_2, HIGH); } else if (motor_left_speed <-50) { digitalWrite(MOTORA_1,HIGH); digitalWrite(MOTORA_2,LOW); } else { digitalWrite(MOTORA_1, LOW); digitalWrite(MOTORA_2, LOW); } analogWrite(ENA, abs(motor_left_speed)); analogWrite(ENB, abs(motor_right_speed)); if (abs(motor_left_speed> 50) || abs(motor_left_speed> 50)) { Serial.println("Moving!"); } 지연(50); } }

    회로도


    제조공정

    1. Arduino 및 Raspberry Pi로 인터넷 제어 비디오 스트리밍 로봇 구축
    2. Unopad - Ableton이 포함된 Arduino MIDI 컨트롤러
    3. 서보 모터로 로봇을 피하는 장애물
    4. Arduino 및 Android 기기로 Roomba 로봇 제어
    5. Nunchuk 제어 로봇 팔(Arduino 포함)
    6. Arduino Nano:조이스틱으로 2개의 스테퍼 모터 제어
    7. Arduino 조이스틱
    8. PS2 컨트롤러로 제어되는 배틀 봇 제작
    9. Android 앱으로 Arduino 로봇 팔 제어
    10. Arduino Nano로 먹는 로봇 만들기 | 금 나사