Joystick Arduino: Baca X/Y dan Kawal Dua Servo Pan-Tilt

Cartoon of a black analog joystick module with a mushroom-dome cap and mint-green potentiometers, rainbow jumper wires arcing across to a pan-tilt assembly of two dark translucent smoky-brown micro servos with pink labels in cream printed brackets, teal and navy arrows showing horizontal and vertical sweep

Joystick analog ialah dua potentiometer pada sudut tepat berserta satu butang tekan, jadi anda membacanya dengan analogRead() pada dua pin analog β€” tetapi satu baris yang semua orang tulis mula-mula, servo.write(map(analogRead(A0),0,1023,0,180)), menghasilkan pan-tilt yang merayap sendiri lalu melompat balik ke tengah. Betulkannya dengan mengukur di mana stick anda benar-benar berehat, dan dengan membiarkan sesaran menetapkan kelajuan, bukan sudut.

Apa yang anda perlukan untuk membina pan-tilt kawalan joystick?

Modul joystick HW-504 ialah board hitam dengan penutup ibu jari hitam matte, dua potentiometer hijau mint di bawah gimbalnya dan satu header male lima pin yang lurus. Dua servo mikro TowerPro MG90S memberikan pan dan tilt; ia tiba sebagai motor berserta wayarnya sahaja, jadi bracket ialah sebahagian daripada binaan ini.

Pilihan β€” kuasa berasingan untuk servo dan kabel USB

ItemPriceQty
AA Battery Holder with Cover On/Off Switch 2/3/4 Slots Battery AA Holder Red/Black Wire - AA BATTERY CASE (4S)AA Battery Holder with Cover On/Off Switch 2/3/4 Slots Battery AA Holder Red/Black Wire - AA BATTERY CASE (4S)AABATS4RM3.55
Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)ATBC100RM4.90

Pemegang 4xAA inilah penyelesaian bila dua servo membuatkan Uno berkuasa USB brownout: empat sel bersiri memberi 6 V nominal, tepat di dalam rating MG90S 4.8–6 V, dan ia membekalkan servo sahaja. Soket pada Uno pula menerima plug USB-B bersaiz penuh (jenis printer) β€” tambah satu kalau bentuk segi empat itu belum ada dalam laci anda.

Dua pek jumper itu bukan berulang: penyambungnya menghadap arah bertentangan. Joystick membawa pin male, jadi ia perlukan hujung female jumper male-to-female untuk sampai ke Uno dan ke breadboard. Wayar setiap servo pula berakhir dengan soket female tiga laluan, jadi ia perlukan jumper male-to-male β€” sama seperti dua sambungan dari Uno ke rel breadboard. Shield keypad joystick atau shield dua joystick melangkau pendawaian itu terus; Uno FT232 pula sesuai untuk PC yang bergelut dengan driver CH340.

Bagaimana sebenarnya modul joystick analog berfungsi?

Dua potentiometer pada HW-504 didawai sebagai pembahagi voltan merentasi +5 V dan GND yang sama yang anda bekalkan kepada modul itu, satu diputar oleh setiap paksi gimbal. Setiap wiper berayun dari hampir 0 V ke hampir 5 V sepanjang perjalanan stick, dan ADC 10-bit ATmega328P menukarkannya menjadi 0–1023 β€” lebih kurang 4.9 mV setiap kiraan.

Butang tekannya ialah suis tactile biasa antara pin SW dengan GND, tanpa perintang pull-up pada modul itu, jadi kalau dibiarkan pin tersebut terapung lalu membaca noise. pinMode(PIN_SW, INPUT_PULLUP) menghidupkan pull-up 20–50 kΞ© milik ATmega328P sendiri: pin itu kemudian duduk pada HIGH dan satu tekanan menariknya ke LOW.

Spring itu memulangkan stick hampir ke pusat elektriknya, bukan tepat kepadanya: toleransi acuan pada yoke dan pada trek pot meletakkan stick yang berehat di mana-mana antara 490-an tinggi hingga 530-an rendah, berbeza pada setiap paksi dan berbeza pada setiap modul. Stick yang berehat pada 507 memang tiada masalah. Menganggapnya 512 itu yang jadi masalah.

Rajah kartun pandangan atas perjalanan bulat sebuah joystick dengan crosshair oren pada pusat geometri sebenar, satu cakera dead zone teal pucat yang tersasar dengan titik navy di tengahnya menandakan kedudukan rehat sebenar stick, dan sebuah modul joystick hitam berkubah cendawan di penjuru
Crosshair oren ialah tempat yang anda anggap stick berehat. Titik navy ialah tempat ia benar-benar berehat. Cakera teal itu ialah dead zone yang terpaksa menelan jurang antara keduanya.

Bagaimana cara mendawai joystick dan dua servo ke Uno?

Uno hanya ada satu soket 5 V sedangkan binaan ini perlukan lebih banyak titik cabang, jadi rel breadboard yang memecahkannya. Bekalkan rel itu dari Uno dahulu. Periksa silkscreen joystick sebelum anda mengira pin: tulisannya GND, +5V, VRx, VRy, SW β€” ground dahulu, bukan susunan VCC-dahulu yang dipakai kebanyakan modul.

Dari Ke Kenapa
Uno 5V dan GND Rel + dan βˆ’ sisi dekat Dua jumper male-to-male. Satu soket menjadi satu rel penuh
Joystick +5V, GND Rel + dan βˆ’ sisi dekat Menghidupkan kedua-dua pembahagi voltan daripada 5 V Uno. Bekalan 6 V di sini akan menolak voltan wiper melebihi rujukan ADC itu sendiri
Joystick VRx, VRy A0, A1 Dua voltan wiper yang dibaca oleh ADC
Joystick SW D2 Suis ke GND; sketch menghidupkan pull-up dalaman
Wayar oren servo pan D9 Isyarat sahaja β€” satu denyut 1–2 ms setiap 20 ms
Wayar oren servo tilt D10 Sama, saluran kedua
Wayar coklat kedua-dua servo Rel βˆ’ sisi jauh Menjauhkan arus balik servo daripada ground joystick
Wayar merah kedua-dua servo Rel + sisi jauh Dibekalkan oleh bateri, tidak sekali-kali oleh Uno β€” lihat bahagian kuasa
Rel βˆ’ sisi dekat Rel βˆ’ sisi jauh Ground sepunya. Satu jumper, dan itu sahajalah wayar yang menghubungkan dua bekalan itu

Kekalkan dua bekalan itu pada tepi bertentangan board. Breadboard 400 titik membawa jalur + dan βˆ’nya sendiri di sepanjang setiap sisi panjang, jadi Uno membekalkan sepasang dan bateri sepasang lagi, dan jumper yang mengikat dua jalur βˆ’ itu ialah satu-satunya wayar yang menyeberang antara keduanya. Sambungkan dua jalur + itu pula dan 6 V akan mendarat pada pin 5 V Uno β€” sambungan yang binaan ini tidak boleh buat sama sekali. Tanpa bateri, jambatan itulah cara servo berjalan daripada Uno, dan itu hanya untuk ujian ringkas tanpa beban.

Pin 9 dan 10 ialah pilihan lalai yang munasabah, bukan satu kemestian. Library Servo menuntut Timer1 pada attach() yang pertama, pin mana pun yang anda guna, dan Timer1 itulah hardware yang menjana PWM pada pin 9 dan 10 β€” jadi analogWrite() di situ berhenti berfungsi walau apa cara sekalipun. Pin 3, 5, 6 dan 11 kekal dengan PWM masing-masing.

Kenapa sketch map() satu baris yang nampak jelas itu hanyut dan menggeletar?

Sketch map() satu baris itu menyasarkan nombor yang salah. Joystick yang berehat pada 507 dipetakan kepada 89Β°, bukan 90Β°, jadi mount duduk tersasar satu darjah. Lebih teruk lagi, mod kedudukan membuatkan mount mengikut stick secara mutlak: lepaskan stick, spring memulangkannya ke tengah, dan kamera yang baru anda halakan tadi berayun balik ke tengah bilik. Itu hukum kawalan yang salah untuk sebuah mount, bukan bug yang boleh ditala sampai hilang.

Dead zone-nya kosong. Bacaan ADC merayau satu dua kiraan, dan map() menyebarkan 1023 kiraan merentasi 180Β°, jadi 5.7 kiraan sudah menjadi satu darjah. Satu darjah arahan itu lebih kurang 10 Β΅s denyut sedangkan dead band MG90S ialah 5 Β΅s β€” jadi gear train boleh bergerak berulang-alik sendiri pada stick yang tiada sesiapa sentuh.

Groundnya bergerak. Suspek yang paling jelas sebenarnya tidak bersalah: ADC mengukur terhadap rel 5 V yang sama yang menghidupkan potentiometer itu, jadi bila rel itu jatuh, voltan wiper turun seiringan dan bacaannya tidak berubah. Yang tidak membatal ialah arus balik. Dua servo menarik ratusan miliampere balik melalui jumper ground yang dikongsi dan sentuhan breadboard, dan 0.2 Ξ© pada laluan itu menukar 300 mA menjadi 60 mV sesaran ground β€” lebih kurang 12 kiraan ADC, muncul dan hilang mengikut gerakan servo.

Bagaimana anda buat stick mengawal kelajuan dan bukan sudut?

Kawalan halaju ialah satu perubahan kecil sahaja pada sketch joystick. Daripada sudut = f(stick) anda menulis sudut = sudut + f(stick): stick menetapkan berapa darjah ditambah setiap frame, melepaskannya menambah sifar, dan mount kekal di tempat yang anda halakan tadi. Ia juga memberi percuma pelicinan yang orang selalu tampal kemudian: sudut arahan hanya boleh bergerak paling banyak MAX_STEP setiap frame, jadi tiada bacaan β€” bising, tersasar atau apa sekalipun β€” yang mampu menggerakkan mount lebih laju daripada itu. Penapis purata menyerang noise itu; had kadar pula membuatkan noise tersebut tidak memudaratkan, sebesar mana pun ia.

Nombor-nombornya datang daripada servo itu sendiri. Ia menyegar semula setiap 20 ms, jadi frame yang lebih pendek hanya mengarahkan gerakan yang tidak mampu dilaksanakannya. Pada langkah stick penuh 1.5Β° setiap frame, mount menyapu 75Β°/s dan merentasi span pan 10°–170Β° dalam sketch ini dalam masa sedikit lebih dua saat β€” terkawal dan sesuai dirakam. MG90S dirating pada 0.08 s setiap 60Β°, iaitu 750Β°/s, jadi langkah 1.5Β° mengambil masa lebih kurang dua milisaat sahaja β€” selesai, dan menganggur sepanjang baki frame itu. Servo tidak pernah terkejar-kejar arahan, dan itulah sebabnya sapuannya nampak licin dan bukan tersentak-sentak. Dead zone 60 kiraan pula lima kali ganda bajet sesaran ground di atas, dan masih meninggalkan 88% perjalanan setiap arah untuk digunakan.

/*
  Joystick analog (HW-504) -> dua servo MG90S, pan-tilt dengan kawalan HALAJU.
  Library: Servo (disertakan bersama Arduino IDE).

  Joystick GND -> rel GND    +5V -> rel 5V
  Joystick VRx -> A0    VRy -> A1    SW -> D2 (pull-up dalaman)
  Wayar oren servo -> D9 (pan) dan D10 (tilt)
  Wayar merah servo -> bateri 6V +    Wayar coklat servo -> GND, dikongsi dengan Uno

  SESARAN stick menetapkan KELAJUAN perubahan sudut, bukan sudut itu sendiri.
  Lepaskan dan mount kekal di tempatnya. Tahan butang untuk membawanya balik ke tengah.
*/
#include <Servo.h>

const uint8_t PIN_VRX  = A0;
const uint8_t PIN_VRY  = A1;
const uint8_t PIN_SW   = 2;
const uint8_t PIN_PAN  = 9;    // pin Timer1 - lihat artikel
const uint8_t PIN_TILT = 10;

const int      DEADZONE = 60;    // kiraan ADC di kiri-kanan nilai rehat yang diukur
const float    MAX_STEP = 1.5;   // darjah digerakkan setiap frame 20 ms pada stick penuh
const uint16_t FRAME_MS = 20;    // satu tempoh segar semula servo

const int PAN_MIN  = 10, PAN_MAX  = 170;   // jauhkan daripada hentian mekanikal
const int TILT_MIN = 30, TILT_MAX = 150;

Servo pan, tilt;

int   restX, restY;                        // diukur semasa board hidup, bukan diandaikan 512
float panAngle = 90.0, tiltAngle = 90.0;
uint32_t lastFrame = 0, lastReport = 0;

// Buang penukaran pertama selepas mux berpindah, kemudian puratakan lapan.
int readAxis(uint8_t pin) {
  analogRead(pin);
  long sum = 0;
  for (uint8_t i = 0; i < 8; i++) sum += analogRead(pin);
  return (int)(sum / 8);
}

// Sesaran stick -> darjah setiap frame. Sifar dalam dead zone, MAX_STEP pada hujung.
float axisRate(int raw, int rest) {
  int offset = raw - rest;
  if (offset > -DEADZONE && offset < DEADZONE) return 0.0;

  int span, past;
  if (offset > 0) { past = offset - DEADZONE; span = 1023 - rest - DEADZONE; }
  else            { past = offset + DEADZONE; span = rest - DEADZONE;        }
  if (span < 1) span = 1;

  float k = (float)past / (float)span;
  if (k >  1.0) k =  1.0;
  if (k < -1.0) k = -1.0;
  return k * MAX_STEP;
}

void setup() {
  Serial.begin(115200);
  pinMode(PIN_SW, INPUT_PULLUP);   // modul ini tiada pull-up sendiri

  // KALIBRASI: baca di mana stick ANDA benar-benar berehat. Jangan sentuh semasa setup.
  restX = readAxis(PIN_VRX);
  restY = readAxis(PIN_VRY);
  Serial.print(F("rest X = "));    Serial.print(restX);
  Serial.print(F("   rest Y = ")); Serial.print(restY);
  Serial.println(F("   (near 512, but neither will be exactly 512)"));

  pan.attach(PIN_PAN);
  tilt.attach(PIN_TILT);
  pan.write((int)panAngle);
  tilt.write((int)tiltAngle);
}

void loop() {
  if (millis() - lastFrame < FRAME_MS) return;
  lastFrame = millis();

  int x = readAxis(PIN_VRX);
  int y = readAxis(PIN_VRY);

  if (digitalRead(PIN_SW) == LOW) {
    // Butang ditahan: bawa balik ke tengah pada had kadar yang sama. Jangan melompat.
    panAngle  += constrain(90.0 - panAngle,  -MAX_STEP, MAX_STEP);
    tiltAngle += constrain(90.0 - tiltAngle, -MAX_STEP, MAX_STEP);
  } else {
    panAngle  = constrain(panAngle  - axisRate(x, restX), PAN_MIN,  PAN_MAX);
    tiltAngle = constrain(tiltAngle + axisRate(y, restY), TILT_MIN, TILT_MAX);
  }

  pan.write((int)(panAngle + 0.5));
  tilt.write((int)(tiltAngle + 0.5));

  // Nombor langsung supaya anda boleh lihat dead zone membuat kerjanya.
  if (millis() - lastReport >= 500) {
    lastReport = millis();
    Serial.print(F("X "));       Serial.print(x);
    Serial.print(F("  Y "));     Serial.print(y);
    Serial.print(F("  pan "));   Serial.print((int)panAngle);
    Serial.print(F("  tilt "));  Serial.println((int)tiltAngle);
  }
}

Pada Uno, ini dikompil menjadi 6,030 bait flash β€” 18% daripada 32 KB β€” dan 252 bait RAM. Serial Monitor pada 115200 mencetak nilai rehat modul anda pada baris pertama. Luaskan DEADZONE kalau mount masih merayap sedangkan tangan anda langsung tidak menyentuh stick; tukar tanda pada satu panggilan axisRate kalau paksi itu bergerak ke arah bertentangan, atau tukar tempat PIN_VRX dengan PIN_VRY kalau pemasangan suku pusingan menyilangkan paksinya.

Bolehkah pin 5 V Arduino menghidupkan dua servo MG90S?

MG90S dirating 4.8–6 V dan menarik lebih kurang 10 mA ketika diam, 120–250 mA semasa bergerak dan sekitar 700 mA pada stall, iaitu angka bench dan bukan rating rasmi TowerPro. Dua yang bergerak serentak menjadi 240–500 mA, Uno sendiri mahu 40–50 mA lagi, dan laluan USB itu dikawal oleh polyfuse boleh set semula yang memutuskan sambungan melebihi 500 mA. Binaan ini duduk tepat pada garisan itu, bukan selesa di dalamnya, dan itulah sebabnya ia nampak menjadi sehinggalah servo tilt menerima beban lalu board reset.

Beri servo bekalan kuasanya sendiri. Empat sel AA bersiri di dalam pemegang bersuis memberi 6.0 V nominal, di dalam rating MG90S, dan set alkali baharu yang bermula pada lebih kurang 6.4 V akan turun ke paras itu di bawah beban. Wayar merahnya pergi ke rel + sisi jauh dan wayar hitamnya ke rel βˆ’ sisi jauh β€” jangan sekali-kali ke pin 5 V Uno. Pin itu ialah output pengatur voltan, bukan input, dan semasa board berjalan atas USB ia duduk pada rel yang sama yang dibekalkan oleh port USB, jadi 6 V yang ditolak masuk di situ akan mengalir ke belakang melalui pengatur voltan dan naik ke kabel menuju port laptop anda. Pulas hujung wayar terdedah pemegang itu ketat-ketat sebelum ia masuk ke lubang breadboard.

Rajah kartun topologi kuasa: kabel USB biru lut sinar membekalkan sebuah board Arduino biru kecil pada satu lorong, pemegang bateri AA empat sel hitam membekalkan dua servo mikro coklat berasap lut sinar gelap pada lorong satu lagi, dua talian bekalan merah dipisahkan oleh pangkah oren, dan satu talian ground hitam tunggal menyambung semuanya dengan tanda betul teal
Dua lorong kuasa, satu ground. Bateri membekalkan servo, USB membekalkan board, dan satu-satunya wayar yang menyeberang antara keduanya ialah ground.

Servo itu dipasang pada apa?

MG90S dihantar sebagai motor berserta wayarnya, dan itu menjadikan bracket satu keputusan reka bentuk. Dalam sebuah pan-tilt, servo bawah menanggung servo atas berserta apa sahaja yang duduk di atasnya, jadi bebannya ialah berat pada lengan tuas. Satu servo tilt dengan bracket ringan β€” 40 g pada lengan 3 cm β€” bersamaan lebih kurang 0.12 kgΒ·cm berbanding 2.2 kgΒ·cm MG90S pada 6 V. Letak pula telefon 200 g pada lengan 5 cm dan anda sudah meminta 1.0 kgΒ·cm: masih dalam rating di atas kertas. Tetapi yang merosakkan gear nylon jarang sekali beban mantap β€” ia adalah kejutan daripada terlanggar sesuatu atau daripada dipandu masuk ke hentian, yang tiba pada satu gigi serentak. Itulah hujahnya untuk gear logam pada paksi yang menanggung berat. Panduan perbandingan servo kami menghuraikan selebihnya pilihan itu.

Bracket itu cetak-sendiri atau buat-sendiri. Output MG90S ialah spline logam 21 gigi lebih kurang 4.7 mm garis pusatnya dengan lubang skru kecil di tengah, jadi soket bercetak ditekan masuk atas spline itu dan satu skru self-tapping M2 ke dalam lubang tersebut menghalangnya memanjat keluar di bawah beban β€” Smith3D membekalkan filamen dan printernya.

Pasang bracket sewaktu servo sudah dipusatkan. Aci servo duduk di mana ia ditinggalkan kali terakhir, jadi jalankan sketch dahulu dan biarkan setup() memandu kedua-dua servo ke 90Β°, kemudian tekan setiap bracket lurus ke bawah atas splinenya β€” lurus ke bawah, bukan diputar, kerana servo sedang menahan sudut itu. Pasangkannya pada aci yang tidak berpusat dan tengah mekanikal mount itu boleh mendarat di mana-mana: stick kehabisan sapuan pada satu arah dan melanggar hentian pada arah yang satu lagi.

Untuk demo di atas meja, kad tebal dan gam panas pun sudah memadai; tiada apa dalam sketch ini yang ambil kisah β€” dan sebaik mount itu berdiri diam, ESP32-CAM ialah benda yang paling jelas untuk diletakkan di atasnya. Video pemasangan ini menunjukkan gandingan itu dalam tangan:

Kartun pemasangan pan-tilt yang sudah siap dengan dua servo mikro coklat berasap lut sinar gelap berlabel merah jambu di dalam bracket cetakan 3D berwarna krim, wayar oren-merah-coklat menjulur ke plug hitam, dan inset dekat aci spline perak dengan skru kecil dijatuhkan ke dalam lubang tengahnya
Satu tapak bercetak, satu bracket U bercetak dan satu skru ke dalam lubang tengah spline. Inset itu menunjukkan gandingan yang menghalang bracket memanjat keluar di bawah beban.

Kesilapan biasa yang kami nampak daripada pelanggan sebenar

Menghidupkan kedua-dua servo daripada Uno, kemudian menyalahkan joystick. Bacaan melompat-lompat, mount tersekat-sekat, board restart β€” ketiga-tiganya ialah bajet arus di atas tadi, bukan stick yang rosak.

Membuat kalibrasi sambil tangan masih pada stick. Sketch ini mengukur nilai rehat sekali sahaja, di dalam setup(), dan setiap bacaan selepas itu ialah sesaran daripada nilai tersebut. Pegang stick semasa board boot β€” atau reset board di tengah-tengah anda mengemudi β€” dan mount mewarisi pusat yang palsu, jadi ia merayap walaupun stick sudah dilepaskan: itulah tepat-tepat hanyutan yang kalibrasi ini wujud untuk hapuskan. Lepaskan tangan, reset, dan perhatikan dua nombor rehat itu duduk pada pasangan yang sama.

Melawan servo secara mekanikal. Menulis 0Β° atau 180Β° ke dalam servo yang bracketnya menghentikannya lebih awal membuatkan ia menolak hentian itu lalu duduk di situ menarik arus stall sehingga ada sesuatu yang mengalah β€” itulah gunanya PAN_MIN dan TILT_MAX. Atas sebab yang sama, jangan sekali-kali memulas output servo yang bertenaga dengan tangan.

FAQ

Kenapa joystick Arduino saya baca 512 dan bukan 0?

Sebab ia memang berfungsi. Kedudukan tengah meletakkan setiap wiper di pertengahan treknya, jadi ia menghantar separuh daripada 5 V β€” lebih kurang 512 daripada 1023 kiraan ADC.

Kenapa joystick saya tidak baca tepat 512 semasa berehat?

Toleransi mekanikal pada gimbal dan pada trek pot. 490-an tinggi hingga 530-an rendah itu normal, dan dua paksi itu berbeza. Ukur kedua-duanya semasa board mula hidup.

Boleh tak saya jalankan dua servo daripada pin 5 V Arduino?

Untuk ujian ringkas tanpa beban sahaja. Dua MG90S yang bergerak serentak menarik 240–500 mA sedangkan bekalan USB Uno terputus melebihi 500 mA. Empat sel AA, ground diikat bersama, itulah susunan yang boleh diharap.

Kenapa library Servo merosakkan analogWrite pada pin 9 dan 10?

Ia mengambil alih Timer1, iaitu hardware yang menghasilkan PWM pada dua pin itu, pada attach() yang pertama β€” pin mana pun yang digunakan servo. Pin 3, 5, 6 dan 11 kekal dengan PWM mereka.

Perlukah saya guna shield joystick dan bukan modul biasa?

Tidak β€” kedua-duanya potentiometer pada pin analog, jadi codenya sama sahaja. Shield menjimatkan pendawaian; modul kosong pula boleh duduk di dalam enclosure pegang tangan.

Kemas kini terakhir Ogos 2026. Tersangkut? Chat dengan kami di WhatsApp.

Leave a Reply

Your email address will not be published. Required fields are marked *