مقدمه
آنچه در این آموزش یاد می گیرید
- • آشنایی با ماژول جوی استیک و نحوه عملکرد آن
- • نحوه راه اندازی ماژول جوی استیک
- • بهینه سازی داده های جوی استیک
- • راه اندازی سرووموتور با جوی استیک
- • برقرای ارتباط با Processing با استفاده از جوی استیک
جوی استیک چیست و چگونه کار می کند؟
جوی استیک احتمالا بیشترین وسیله است که گیمر ها با آن سرو کار دارند، به زبان ساده جوی استیک وسیله ای است که حرکات دست شما را به سیگنال الکتریکی تبدیل می کند. راحتی استفاده از جوی استیک باعث شده هنگام کار با آن واقعا حس کنید درون بازی یا درون رباتی که کنترل می کنید هستید.
ساختار ماژول جوی استیک درست مانند جوی استیک های دسته PS2 می باشد، به صورت عادی قسمت سر ماژول در وسط قرار دارد و وقتی آنرا جا به جا می کنید و سپس رها می کنید مجددا با استفاده از فنر مرکزی (self-centering spring)، به حالت وسط باز می گردد، حرکت جوی استیک بسیار نرم بوده و یک کلید نیز روی ماژول تعبیه شده است.
اساس کار جوی استیک بر تغییر مقاومت دو پتانسیومتر معمولا 10 کیلویی استوار است، دو پتانسیومتر برای محور x و y قرار داده شده که با تغییر مقاومت این پتانسیومتر ها و تبدیل این مقاومت به ولتاژ الکتریکی و خواندن ولتاژ توسط آردوینو (یا هر پردازنده دیگری) می توان موقعیت x و y را تخمین زد. برای این کار، پردازنده باید واحد ADC داشته باشد تا مقدار آنالوگ خوانده شده توسط جوی استیک را به مقدار دیجیتال تبدیل کند و بر اساس این مقدار دیجیتال عملیات مورد نظر را انجام دهد.
برد آردوینو 6 کانال ADC ده بیتی دارد، یعنی 5 ولت مرجع را به 1024 بخش تقسیم می کند، وقتی جوی استیک را مثلا در محور x از ابتدا به انتهای مسیر خود می بریم مقدار ADC آن از 0 تا 1023 تغییر می کند و وقتی در موقعیت رها در مرکز قرار دارد مقدار آن 512 خواهد بود. شکل زیر مقدار تقریبی ADS را باتوجه به موقعیت سر جوی استیک نشان میدهد.
این ماژول دارای 5 پایه می باشد.
- پایه زمین برای اتصال به زمین مدار.
- پایه Vcc برای تغذیه مدار که می توانید به 5 ولت متصل کنید.
- پایه VRx خروجی آنالوگ محور X، معمولا محور X معرف حرکت چپ و راست است.
- پایه VRy خروجی آنالوگ محور y، معمولا محور Y ومعرف حرکت بالا و پایین است.
- پایه SW برای کلید که بصورت pull-up داخلی قرار گرفته است، یعنی با فشردن کلید مقدار 0 روی پایه SW قرار می گیرد.
لوازمی که به آن احتیاج دارید
قطعات مورد نیاز
**سروو موتور برای انجام مثال کنترل سروو با جوی استیک می باشد.
نرم افزار های مورد نیاز
راه اندازی جوی استیک با آردوینو
استفاده از ماژول جوی استیک و آردوینو کار ساده ای است، فقط کافیست مقدار ADC را از پایه های VRx و VRy بخوانید.
سیم بندی
ماژول را طبق سیم بندی زیر به آردوینو متصل کنید.
کد
برای برنامه نویسی ماژول جوی استیک نیازی به کتابخانه ندارید، کافیست کد زیر را روی آردوینو خود آپلود کنید و خروجی را در Serial monitor مشاهده کنید.
/*
Joystick and Arduino
modified on 21 Jul 2019
by Saeed Hosseini @ Electropeak
Home
*/
const int SW = 2; // SW
const int X = 0; // VRx
const int Y = 1; // VRy
void setup() {
pinMode(SW, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
Serial.print("Switch: ");
Serial.print(digitalRead(SW));
Serial.print("\t\t");
Serial.print("VRx: ");
Serial.print(analogRead(X));
Serial.print("\t\t");
Serial.print("VRy: ");
Serial.println(analogRead(Y));
Serial.println("____________________________________________________________");
delay(500);
}
فقط کافیست با دستور analogRead(Pin) مقدار آنالوگ را خوانده و از آن استفاده کنید.
چگونه داده های جوی استیک را بهینه کنیم؟
یکی از بزرگترین مشکلات ماژول های جوی استیک غیر خطی بودن آن است، یعنی مقدار ADC بصورت خطی با حرکت سر جوی اسیتک تغییر نمی کند، این موضوع می تواند در پروژه هایی که به دقت بالا نیاز دارد، مانند کنترل ربات های پرنده ، کمی آزار دهنده باشد.
می توان با روش های میانگین گیری، داده های جوی استیک را نرم تر کرد تا این میزان غیرخطی بودن زیاد آزاردهنده نباشد.
کد زیر را روی آردوینو خود آپلود کنید و با مشاهده خروجی در Serial Monitor، داده ها را با حالت راه اندازی ساده مقایسه کنید.
کد
/*
Joystick Smoothing
modified on 21 Jul 2019
by Saeed Hosseini @ Electropeak
Home
This code is based on https://www.arduino.cc/en/Tutorial/Smoothing
*/
const int X = 0;
const int Y = 1;
const int MaxReadings = 10;
int Xreadings[MaxReadings];
int XreadIndex = 0;
int Xtotal = 0;
int X_Pos = 0;
int Yreadings[MaxReadings];
int YreadIndex = 0;
int Ytotal = 0;
int Y_Pos = 0;
void Smoother(int x_pin, int y_pin)
{
Xtotal = Xtotal - Xreadings[XreadIndex];
Ytotal = Ytotal - Yreadings[YreadIndex];
delay(1);
Xreadings[XreadIndex] = analogRead(x_pin);
Yreadings[YreadIndex] = analogRead(y_pin);
delay(1);
Xtotal = Xtotal + Xreadings[XreadIndex];
Ytotal = Ytotal + Yreadings[YreadIndex];
delay(1);
XreadIndex = XreadIndex + 1;
YreadIndex = YreadIndex + 1;
if (XreadIndex >= MaxReadings) XreadIndex = 0;
if (YreadIndex >= MaxReadings) YreadIndex = 0;
delay(1);
X_Pos = Xtotal / MaxReadings;
Y_Pos = Ytotal / MaxReadings;
}
void setup() {
Serial.begin(9600);
for (int i = 0; i < MaxReadings; i++) {
Xreadings[i] = 0;
Yreadings[i] = 0;
}
}
void loop() {
Smoother(X,Y);
Serial.print("VRx: ");
Serial.print(X_Pos);
Serial.print("\t\t");
Serial.print("VRy: ");
Serial.println(Y_Pos);
Serial.println("____________________________________________________________");
delay(100);
}
متغیر MaxReading تعداد دفعات میانگین گیری را مشخص می کند، هر چه این مقدار بیشتر باشد داده ها بصورت نرم تر و خطی تر تغییر می کنند اما درعوض سرعت پاسخ دهی ماژول افت می کند، بنابراین با توجه به دقت مورد نیاز برای پروژه خود باید بین پارامتر سرعت و دقت مصالحه برقرار (trade off) کنید.
کنترل سروو موتور با جوی استیک
در این پروژه برای درک بهتر عملکرد ماژول جوی استیک، یک سرووموتور را به کمک این ماژول کنترل می کنید.
سیم بندی
کد
/*
Joystick and servo motor
modified on 21 Jul 2019
by Saeed Hosseini @ Electropeak
Home
This code is based on https://www.arduino.cc/en/Tutorial/Smoothing
*/
#include <Servo.h>
Servo myservo;
const int X = 0;
const int Y = 1;
const int MaxReadings = 10;
int Xreadings[MaxReadings];
int XreadIndex = 0;
int Xtotal = 0;
int X_Pos = 0;
int Yreadings[MaxReadings];
int YreadIndex = 0;
int Ytotal = 0;
int Y_Pos = 0;
int Servo_Pos = 0, Pos = 0;
void Smoother(int x_pin, int y_pin)
{
Xtotal = Xtotal - Xreadings[XreadIndex];
Ytotal = Ytotal - Yreadings[YreadIndex];
delay(1);
Xreadings[XreadIndex] = analogRead(x_pin);
Yreadings[YreadIndex] = analogRead(y_pin);
delay(1);
Xtotal = Xtotal + Xreadings[XreadIndex];
Ytotal = Ytotal + Yreadings[YreadIndex];
delay(1);
XreadIndex = XreadIndex + 1;
YreadIndex = YreadIndex + 1;
if (XreadIndex >= MaxReadings) XreadIndex = 0;
if (YreadIndex >= MaxReadings) YreadIndex = 0;
delay(1);
X_Pos = Xtotal / MaxReadings;
Y_Pos = Ytotal / MaxReadings;
}
void setup() {
Serial.begin(9600);
myservo.attach(9);
for (int i = 0; i < MaxReadings; i++) {
Xreadings[i] = 0;
Yreadings[i] = 0;
}
}
void loop() {
Smoother(X, Y);
Servo_Pos = map(X_Pos, 0, 1023, 0, 180);
myservo.write(Servo_Pos);
Serial.println(Servo_Pos);
delay(20);
}
map(X_Pos, 0, 1023, 0, 180);
استفاده می کنیم. ارتباط جوی استیک و Processing
Processing مجموعه ای از کتابخانه های جاوا بهمراه یک محیط برنامه نویسی برای آموزش ساخت محیط تعاملی بصری است که در سال 2001 توسعه داده شد و امروزه بخاطر راحتی ارتباط آن با بردهایی نظیر آردوینو و ساخت محیط های گرافیکی و تعاملی، محبوبیت زیادی پیدا کرده است. Processing کاملا رایگان بوده و می توانید آنرا از این لینک دانلود کرده و نصب کنید.
برای آشنایی بیشتر با Processing، نحوه برنامه نویسی، توابع و … به مرجع کامل خود نرم افزار مراجعه کنید.
Processing می تواند از سه طریق ماوس، کیبورد و پورت سریال کامپیوتر داده ورودی بگیرد، در این پروژه داده های جوی استیک از طریق پورت سریال به کامپیوتر وارد شده و Processing این داده ها را دریافت و پردازش می کند.
پس به دو کد برای این بخش نیاز داریم:
استخراج موقعیت جوی استیک و ارسال به Processing
کد زیر را روی آردوینو خود آپلود کنید:
/*
Joystick and Processing
modified on 21 Jul 2019
by Saeed Hosseini @ Electropeak
Home
*/
const int X = 0;
const int Y = 1;
int b = 0;
int x = 0;
int y = 0;
const int MaxReadings = 10;
int Xreadings[MaxReadings];
int XreadIndex = 0;
int Xtotal = 0;
int X_Pos = 0;
int Yreadings[MaxReadings];
int YreadIndex = 0;
int Ytotal = 0;
int Y_Pos = 0;
void Smoother(int x_pin, int y_pin)
{
Xtotal = Xtotal - Xreadings[XreadIndex];
Ytotal = Ytotal - Yreadings[YreadIndex];
delay(1);
Xreadings[XreadIndex] = analogRead(x_pin);
Yreadings[YreadIndex] = analogRead(y_pin);
delay(1);
Xtotal = Xtotal + Xreadings[XreadIndex];
Ytotal = Ytotal + Yreadings[YreadIndex];
delay(1);
XreadIndex = XreadIndex + 1;
YreadIndex = YreadIndex + 1;
if (XreadIndex >= MaxReadings) XreadIndex = 0;
if (YreadIndex >= MaxReadings) YreadIndex = 0;
delay(1);
X_Pos = Xtotal / MaxReadings;
Y_Pos = Ytotal / MaxReadings;
}
void setup()
{
Serial.begin(9600) ;
pinMode(2, INPUT_PULLUP) ;
for (int i = 0; i < MaxReadings; i++) {
Xreadings[i] = 0;
Yreadings[i] = 0;
}
}
void loop()
{
Smoother(X,Y);
x = map(X_Pos, 0, 1023, 0, 512);
y = map(Y_Pos, 0, 1023, 0, 512);
b = digitalRead(2);
Serial.print(x, DEC);
Serial.print(",");
Serial.print(y, DEC);
Serial.print(",");
Serial.print(!b);
Serial.print("\n");
delay(10);
}
برای تمییز داده ها از هم در Processing باید داده ها را با استفاده از “, ” از هم جدا کنید، در انتها نیز برای بیان اتمام داده ها از “\n ” استفاده کنید.
دریافت و پردازش داده های جوی استیک توسط Processing
کد زیر را در Processing کپی کرده و آنرا اجرا کنید:
/*
Joystick and Processing
modified on 21 Jul 2019
by Saeed Hosseini @ Electropeak
Home
Thanks to http://learningprocessing.com/examples/
*/
import processing.serial.*;
Serial myPort;
int VRx;
int VRy;
int but;
String val;
int maxImages = 5;
int imageIndex = 0;
int i = 1;
PImage[] img = new PImage[maxImages];
PImage image;
void setup() {
size(512, 512);
myPort = new Serial(this, "COM5", 9600);
myPort.bufferUntil('\n');
background(0);
}
void draw() {
game();
}
void serialEvent( Serial myPort)
{
// read the data until the newline n appears
val = myPort.readStringUntil('\n');
if (val != null)
{
val = trim(val);
// break up the decimal and new line reading
int[] vals = int(splitTokens(val, ","));
// we assign to variables
VRx = vals[0];
VRy = vals[1] ;
but = vals[2];
}
}
void game() {
img[i] = loadImage( "p" + i + ".jpg" );
image = img[i];
loadPixels();
img[i].loadPixels();
for (int x = 0; x < image.width; x++ ) {
for (int y = 0; y <image.height; y++ ) {
// Calculate the 1D pixel location
int loc = x + y*image.width;
// Get the R,G,B values from image
float r = red (image.pixels[loc]);
float g = green(image.pixels[loc]);
float b = blue (image.pixels[loc]);
// Calculate an amount to change brightness
// based on proximity to the mouse
float distance = dist(x, y, VRy, VRx);
// The closer the pixel is to the mouse, the lower the value of "distance"
// We want closer pixels to be brighter, however, so we invert the value using map()
// Pixels with a distance of 50 (or greater) have a brightness of 0.0 (or negative which is equivalent to 0 here)
// Pixels with a distance of 0 have a brightness of 1.0.
float adjustBrightness = map(distance, 0, 50, 1, 0);
r *= adjustBrightness;
g *= adjustBrightness;
b *= adjustBrightness;
// Constrain RGB to between 0-255
r = constrain(r, 0, 255);
g = constrain(g, 0, 255);
b = constrain(b, 0, 255);
// Make a new color and set pixel in the window
color c = color(r, g, b);
pixels[loc] = c;
if (but == 1) i++;
if (i>4) i = 1;
}
}
updatePixels();
}
با فشردن کلید وسط عکس بعد نمایان می شود.
در دستور float adjustBrightness = map(distance, 0, 50, 1, 0); با افزایش و یا کاهش مقدار 50 می توانید بازی را آسان تر یا سخت تر کنید.
یک گام جلوتر
- سعی کنید با استفاده از دو ماژول جوی استیک یک دسته بازی بسازید.
- سعی کنید به کمک یک ماژول NRF24L01 دسته بازی که در مرحله قبل ساختید را بصورت بیسیم در بیاورید.
Comments (9)
با تشکر از آموزش خوبتون.
من قصد دارم با 3 عدد ماژول جوی استیک چندتا خروجی کیبورد بگیرم جهت ساخت دسته بازی
امکانش هست راهنمایی کنید؟
با سلام
در صورتی که به عنوان جوی استیک (دسته بازی) کامپیوتری میخواهید استفاده کنید باید از سری برد های Arduino Pro Micro یا Arduino Leonardo استفاده کنید.
مابقی برنامه نویسی شما می باشد و میکرو را در مود HID باید قرار بدهید تا به عنوان دسته بازی در سیستم شناخته شود.
با تشکر
امکان داره با جوی استیک خروجی کیبورد هم گرفت؟
و اگر امکان داره چگونه؟
خیلی ممنون از آموزش خوبتون .
من هم میخوام سروو موتور را با جوی استیک کنترل کنم فقط زمانی که سوویچ جوی استیک زده شد ، سروو موتور در همان جا بایستد و زمانی که دوباره سوییچ زده شد سیستم بفهمد که کاربر میخواهد فرمان جدیدی دهد
با سلام
کافی است 2 شرط برای دکمه قرار بدهید که یک شرظ وضعیت دکمه را مشخص کند. و شرط دوم اینکه هر بار دکمه زده شد مقدار جوی استیک را در یک متغییر بریزد.
به این صورت در زمان هایی که شرط اول برقرار باشد مقدار های ذخیره شده را به سروو بدهد.
سلام وقت بخیر
من میخوام با استفاده از ماژول جوستیک دسته بازی یک موتور چرخش یک موتور Dcرا کنترل کنم ایا امکانش هست؟
با سلام
چرا که نشود، خروجی ماژول جوی استیک یک سیگنال آنالوگ می باشد که به میکرو کنترلر میرود و با برنامه نویسی این مقدار را به یک PWM تبدیل میکند و آن را به درایور موتوری که میخواهید به موتور وصل کنید وصل میکنید تا بتوانید موتور خود را حرکت بدهید.
توجه داشته باشید که ماژول را به صورت مستقیم نمی توانید به موتور یا درایور موتور وصل کنید.
سلام
ممنون از آموزش خوب و کاملتون
من میخوام یک فرمون بازی برای کامپیوتر بسازم
چجوری دیتا تحلیل شده رو به بازی برای واکنش خودرو شبیه سازی شده بدم
با سلام
برای اینکه دیتا های سنسور ها و ماژول ها را به سیستم بدهید تا داخل سیستم استفاده کنید باید میکرو شما مود HID را ساپورت کند، برای این موضوع فقط برد هایی که USB داخلی دارند را میتوانید استفاده کنید. مانندArduino Leonardo یا Pro Micro یا ESP32S3 و…
حتما بررسی کنید که مود HID را داشته باشند و بتوانید به صورت joystick به سیستم بشناسانید.