در این مقاله آموزشی نحوه کار سیستمهای الکترومکانیکی mems ، ژیروسکوپ و مغناطیس سنج و نحوه استفاده از آنها را با بورد آردوینو یاد خواهیم گرفت. همچنین با پردازش IDE با استفاده از سنسورها برخی کاربردهای عملی را ایجاد خواهیم کرد. می توانید فیلم زیر را تماشا کنید یا آموزش کتبی زیر را بخوانید.
توضیح مختصر
سیستمهای الکترومکانیکی mems دستگاه های بسیار کوچکی هستند که از اجزای میکرو متغیر از 0.001 میلی متر تا 0.1 میلی متر تشکیل شده اند. این اجزا از سیلیکون ، پلیمرها ، فلزات و یا سرامیک ساخته شده اند و معمولاً برای تکمیل سیستم با CPU (میکروکنترلر) ترکیب می شوند. اکنون به طور خلاصه نحوه عملکرد هر یک از این سنسورهای Micro-Electro-Mechanical-Systems (MEMS) را توضیح خواهیم داد. در بخش های بعدی آموزش رباتیک ما از این سنسورها به کرات استفاده خواهیم نمود.
شتاب سنج MEMS
شتاب را با اندازه گیری تغییر در ظرفیت اندازه گیری می کند. ساختار آن تقریباً به این شکل است. این شتاب سنج یک توده متصل به یک فنر است که برای حرکت در امتداد یک جهت و صفحات بیرونی ثابت محدود شده است. بنابراین هنگامی که یک شتاب در جهت خاص اعمال می شود ، جرم حرکت می کند و ظرفیت بین صفحات و جرم تغییر می کند. این تغییر ظرفیت اندازه گیری و سپس پردازش می شود و با یک مقدار شتاب خاص مطابقت دارد.
ژیروسکوپ MEMS
ژیروسکوپ با استفاده از اثر کوریولیس میزان زاویه را اندازه گیری می کند. هنگامی که جرمی در یک جهت خاص با سرعت خاصی حرکت می کند و هنگامی که از یک زاویه خارجی استفاده می شود ، همانطور که با فلش سبز نشان داده می شود ، نیرویی ایجاد می شود ، همانطور که با فلش قرمز آبی نشان داده می شود ، که باعث جابجایی عمود جرم می شود. بنابراین مشابه شتاب سنج ، این تغییر مکان باعث تغییر ظرفیت می شود که اندازه گیری ، پردازش می شود و با یک سرعت خاص زاویه ای مطابقت دارد.
ساختار کوچک ژیروسکوپ چیزی شبیه به این است. جرمی که دائماً در حال حرکت است ، یا در حال نوسان است ، و هنگامی که نرخ زاویه خارجی اعمال می شود ، یک قسمت انعطاف پذیر از جرم حرکت می کند و باعث تغییر مکان عمود می شود.
مغناطیس سنج MEMS
این میدان مغناطیسی زمین را با استفاده از Hall Effect یا Magneto Resistive Effect اندازه گیری می کند. در واقع تقریباً 90٪ سنسورهای موجود در بازار از اثر هال استفاده می کنند و نحوه کارکرد آن در اینجا است.
اگر صفحه ای رسانا مانند آنچه در عکس نشان داده شده است داشته باشیم و جریان را تنظیم کنیم تا از آن عبور کند ، الکترون ها مستقیماً از یک طرف دیگر صفحه می روند. حال اگر مقداری میدان مغناطیسی به صفحه نزدیک کنیم ، جریان مستقیم را مختل می کنیم و الکترون ها به یک طرف صفحه و قطب های مثبت به سمت دیگر صفحه منحرف می شوند. این بدان معناست که اگر یک ولت متر را بین این دو طرف قرار دهیم ، مقداری ولتاژ بدست خواهیم آورد که به قدرت میدان مغناطیسی و جهت آن بستگی دارد.
10٪ دیگر سنسورهای موجود در بازار از اثر مقاومت مگنتو استفاده می کنند. این سنسورها از موادی استفاده می کنند که به میدان مغناطیسی حساس هستند و معمولاً از آهن (Fe) و نیکل (Ne) تشکیل شده اند. بنابراین وقتی این مواد در معرض میدان مغناطیسی قرار بگیرند مقاومت آنها را تغییر می دهند.
چنانچه علاقمند به کسب درآمدی سرشار از علاقمندی های خود در حوزه فناوری و رباتیک میباشید میتوانید با کسب نمایندگی رباتیک از صنایع آموزشی چالیک در این حوزه مشغول فعالیت شده و آینده شغلی خود را تضمین نمایید.
آردوینو و سنسورهای MEMs
خوب اکنون اجازه دهید این سنسورها را به برد Arduino متصل کرده و از آنها استفاده کنیم. به عنوان مثال من از برد GY-80 استفاده می کنم که دارای سنسورهای زیر است: ADXL345 3 Axis Accelerometer، L3G4200D 3 Axis Gyroscope، MC5883L 3 Axis Magnetometer و همچنین یک فشارسنج و دماسنج که ما در این مقاله استفاده نمی کنیم.
قطعات مورد نیاز را از لینک زیر تهیه کنید
شتاب دهنده 3 محوره ADXL345
2 در 1: ژیروسکوپ و شتاب سنج MPU6050 6-Axis
3 در 1: ژیروسکوپ شتاب دهنده میدان مغناطیسی 9 محور GY-80
3 در 1: ماژول GY-86 10DOF MS5611 HMC5883L MPU6050
این مدار از پروتکل ارتباطی I2C استفاده می کند یعنی ما فقط با دو سیم می توانیم از همه سنسورها استفاده کنیم. بنابراین برای برقراری ارتباط بین آردوینو و سنسورها باید آدرس دستگاه منحصر به فرد آنها و آدرس رجیستر داخلی آنها را برای خارج کردن داده ها بدانیم. این آدرس ها را می توان از دیتا شیت سنسورها یافت:
ارتباط I2C چیست
گذرگاه ارتباطی I2C بسیار محبوب است و در بسیاری از دستگاه های الکترونیکی بسیار مورد استفاده قرار می گیرد زیرا به راحتی در بسیاری از طراحی های الکترونیکی که نیاز به برقراری ارتباط بین یک اصلی و چندین دستگاه فرعی یا حتی چندین دستگاه اصلی دارد ، به راحتی قابل اجرا است. پیاده سازی آسان با این واقعیت همراه است که برای برقراری ارتباط بین تقریباً 128 (112) دستگاه هنگام استفاده از آدرس دهی 7 بیتی و تقریباً 1024 (1008) دستگاه هنگام استفاده از آدرس دهی 10 بیتی ، فقط دو سیم مورد نیاز است.
چگونه امکان برقراری ارتباط بین بسیاری از دستگاه ها فقط با دو سیم وجود دارد؟ خوب هر دستگاه دارای یک ID از پیش تعیین شده یا یک آدرس دستگاه منحصر به فرد است بنابراین دستگاه اصلی می تواند انتخاب کند که با کدام دستگاه ها ارتباط برقرار شود.
به دو سیم Serial Clock (یا SCL) و Serial Data (یا SDA) گفته می شود. خط SCL سیگنال ساعتی است که انتقال داده را بین دستگاه های موجود در گذرگاه I2C هماهنگ کرده و توسط دستگاه اصلی تولید می شود. خط دیگر خط SDA است که داده ها را حمل می کند.
این دو خط “تخلیه آزاد” هستند و این بدان معناست که مقاومتهای کششی باید به آنها متصل شوند تا خطوط بالا باشند زیرا دستگاههای ورودی I2C کم فعال هستند. مقادیر معمولاً مورد استفاده برای مقاومت ها از 2K برای سرعت بالاتر در حدود 400 kbps تا 10K برای سرعت پایین در حدود 100 kbps است.
نوشتن کد سنسورهای mems در آردوینو
حال بیایید کدهای دریافت داده از حسگرها را مشاهده کنیم. ما با شتاب سنج شروع خواهیم کرد و قبل از هر کد توضیحاتی ارائه می شود ، همچنین توضیحات اضافی در توضیحات کد وجود دارد.
کد شتاب سنج آردوینو
ابتدا باید کتابخانه Wire Library را وارد کرده و آدرس های ریجستری سنسور را تعریف کنیم. در بخش راه اندازی ، ما باید Wire Library را شروع کرده و ارتباط سریال را شروع کنیم زیرا ما از نمایشگر سریال برای نمایش نتایج استفاده خواهیم کرد. همچنین در اینجا ما باید سنسور را فعال کنیم یا اندازه گیری را با ارسال بایت مناسب به Power_CTL فعال کنیم و نحوه انجام این کار را در اینجا نشان می دهیم. با استفاده از تابع Wire.beginTransmission () ما انتخاب می کنیم که با کدام سنسور صحبت کنیم ، شتاب سنج 3 محوره. سپس با استفاده از تابع Wire.write () می گوییم با کدام رجیستر داخلی صحبت خواهیم کرد. پس از این بایت مناسب را برای فعال کردن اندازه گیری ارسال خواهیم کرد. با استفاده از تابع Wire.endTransmission () انتقال را پایان می دهیم و این داده ها را به رجیسترها منتقل می کند.
در بخش حلقه باید داده های هر محور را بخوانیم. ما با X – Axis شروع خواهیم کرد. بنابراین ابتدا ما انتخاب خواهیم کرد که با کدام ریجستری ها ارتباط برقرار کنیم ، در این حالت دو ریجستری داخلی X – Axis انتخاب شده. سپس با استفاده از تابع Wire.requestFrom () داده های ارسالی یا دو بایت را از دو ارسال کننده درخواست می کنیم. تابع Wire.available () تعداد بایت های موجود برای بازیابی را برمی گرداند و اگر این تعداد با بایت های درخواست شده ما مطابقت داشته باشد ، در مورد ما 2 بایت ، با استفاده از تابع Wire.read () ما بایت ها را از دو رجیستر می خوانیم در اینجا محور X
داده های خروجی از ریجستری ( محور X ) مکمل هستند ، با X0 به عنوان کم اهمیت ترین بایت و X1 به عنوان مهمترین بایت ، بنابراین ما باید این بایت ها را به مقادیر شناور از -1 به 1 تبدیل کنیم بسته به جهت نسبت X – Axis به شتاب زمین یا گرانش. ما این روش را برای دو محور دیگر تکرار می کنیم و در پایان این مقادیر را روی مانیتور سریال چاپ می کنیم.
#include <Wire.h> //--- Accelerometer Register Addresses #define Power_Register 0x2D #define X_Axis_Register_DATAX0 0x32 // Hexadecima address for the DATAX0 internal register. #define X_Axis_Register_DATAX1 0x33 // Hexadecima address for the DATAX1 internal register. #define Y_Axis_Register_DATAY0 0x34 #define Y_Axis_Register_DATAY1 0x35 #define Z_Axis_Register_DATAZ0 0x36 #define Z_Axis_Register_DATAZ1 0x37 int ADXAddress = 0x53; //Device address in which is also included the 8th bit for selecting the mode, read in this case. int X0,X1,X_out; int Y0,Y1,Y_out; int Z1,Z0,Z_out; float Xa,Ya,Za; void setup() { Wire.begin(); // Initiate the Wire library Serial.begin(9600); delay(100); Wire.beginTransmission(ADXAddress); Wire.write(Power_Register); // Power_CTL Register // Enable measurement Wire.write(8); // Bit D3 High for measuring enable (0000 1000) Wire.endTransmission(); } void loop() { // X-axis Wire.beginTransmission(ADXAddress); // Begin transmission to the Sensor //Ask the particular registers for data Wire.write(X_Axis_Register_DATAX0); Wire.write(X_Axis_Register_DATAX1); Wire.endTransmission(); // Ends the transmission and transmits the data from the two registers Wire.requestFrom(ADXAddress,2); // Request the transmitted two bytes from the two registers if(Wire.available()<=2) { // X0 = Wire.read(); // Reads the data from the register X1 = Wire.read(); /* Converting the raw data of the X-Axis into X-Axis Acceleration - The output data is Two's complement - X0 as the least significant byte - X1 as the most significant byte */ X1=X1<<8; X_out =X0+X1; Xa=X_out/256.0; // Xa = output value from -1 to +1, Gravity acceleration acting on the X-Axis } // Y-Axis Wire.beginTransmission(ADXAddress); Wire.write(Y_Axis_Register_DATAY0); Wire.write(Y_Axis_Register_DATAY1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Y0 = Wire.read(); Y1 = Wire.read(); Y1=Y1<<8; Y_out =Y0+Y1; Ya=Y_out/256.0; } // Z-Axis Wire.beginTransmission(ADXAddress); Wire.write(Z_Axis_Register_DATAZ0); Wire.write(Z_Axis_Register_DATAZ1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); if(Wire.available()<=2) { Z0 = Wire.read(); Z1 = Wire.read(); Z1=Z1<<8; Z_out =Z0+Z1; Za=Z_out/256.0; } // Prints the data on the Serial Monitor Serial.print("Xa= "); Serial.print(Xa); Serial.print(" Ya= "); Serial.print(Ya); Serial.print(" Za= "); Serial.println(Za); }
کد ژیروسکوپ برای آردوینو
برای بدست آوردن داده ها از ژیروسکوپ ، کدی مشابه کد قبلی خواهیم داشت. بنابراین ابتدا باید آدرس های ثبت و برخی متغیرها را برای داده ها تعریف کنیم. در بخش تنظیمات باید سنسور را با استفاده از CTRL_REG1 در حالت عادی قرار دهیم و همچنین حساسیت سنسور را انتخاب کنیم. برای این مثال حالت حساسیت 2000dps را انتخاب می کنم.
در قسمت حلقه مشابه شتاب سنج ، ما داده های مربوط به محور X ، Y و Z را می خوانیم. سپس داده های خام باید به مقادیر زاویه تبدیل شوند. از صفحه داده سنسور می بینیم که برای حالت حساسیت 2000dps مربوط به یک واحد 70 mdps / رقم است. این بدان معناست که برای بدست آوردن نرخ زاویه ای بر حسب درجه در ثانیه ، باید داده های خروجی خام را در 0.07 ضرب کنیم. اگر نرخ زاویه ای را در زمان ضرب کنیم مقدار زاویه را به ما می دهد. بنابراین ما باید فاصله زمانی هر بخش حلقه را محاسبه کنیم و می توانیم این کار را با استفاده از تابع millis () در بالا و پایین قسمت حلقه انجام دهیم ، و مقدار آن را در این متغیر “dt” ذخیره خواهیم کرد. بنابراین برای هر حلقه اجرا شده ما زاویه را محاسبه کرده و به مقدار نهایی زاویه اضافه می کنیم. همین کار را برای دو محور دیگر انجام خواهیم داد و در پایان نتایج را در مانیتور سریال چاپ خواهیم کرد.
#include <Wire.h> //--- Gyro Register Addresses #define Gyro_gX0 0x28 #define Gyro_gX1 0x29 #define Gyro_gY0 0x2A #define Gyro_gY1 0x2B #define Gyro_gZ0 0x2C #define Gyro_gZ1 0x2D int Gyro = 0x69; //Device address in which is also included the 8th bit for selecting the mode, read in this case. int gX0, gX1, gX_out; int gY0, gY1, gY_out; int gZ0, gZ1, gZ_out; float Xg,Yg,Zg; float angleX,angleY,angleZ,angleXc,angleYc,angleZc; unsigned long start, finished, elapsed; float dt=0.015; void setup() { Wire.begin(); Serial.begin(9600); delay(100); Wire.beginTransmission(Gyro); Wire.write(0x20); // CTRL_REG1 - Power Mode Wire.write(15); // Normal mode: 15d - 00001111b Wire.endTransmission(); Wire.beginTransmission(Gyro); Wire.write(0x23); // CTRL_REG4 - Sensitivity, Scale Selection Wire.write(48); // 2000dps: 48d - 00110000b Wire.endTransmission(); } void loop() { start=millis(); //---- X-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gX1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gY1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ0); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ0 = Wire.read(); } Wire.beginTransmission(Gyro); // transmit to device Wire.write(Gyro_gZ1); Wire.endTransmission(); Wire.requestFrom(Gyro,1); if(Wire.available()<=1) { gZ1 = Wire.read(); } //---------- X - Axis // Raw Data gX1=gX1<<8; gX_out =gX0+gX1; // From the datasheet: 70 mdps/digit Xg=gX_out*0.07; // Angular rate // Angular_rate * dt = angle angleXc = Xg*dt; angleX = angleX + angleXc; //---------- Y - Axis gY1=gY1<<8; gY_out =gY0+gY1; Yg=gY_out*0.07; angleYc = Yg*dt; angleY = angleY + angleYc; //---------- Z - Axis gZ1=gZ1<<8; gZ_out =gZ0+gZ1; Zg=gZ_out*0.07; angleZc = Zg*dt; angleZ = angleZ + angleZc; // Prints the data on the Serial Monitor Serial.print("angleX= "); Serial.print(angleX); Serial.print(" angleY= "); Serial.print(angleY); Serial.print(" angleZ= "); Serial.println(angleZ); delay(10); // Calculating dt finished=millis(); elapsed=finished-start; dt=elapsed/1000.0; start = elapsed = 0; }
کد مغناطیس سنج آردوینو
باز هم ما از تکنیکی مشابه روش قبلی استفاده خواهیم کرد. ابتدا باید آدرس ریجستری را تعریف کنیم و بخش تنظیمات سنسور را در حالت اندازه گیری مداوم تنظیم کند. در بخش حلقه ، داده های خام هر محور را با همان روش سنسورهای قبلی بدست خواهیم آورد.
سپس باید داده های خام را به مقدار میدان مغناطیسی یا واحدهای گاوس تبدیل کنیم. از صفحه داده سنسور می بینیم که حالت حساسیت پیش فرض 0.92mG / رقم است. این بدان معناست که برای بدست آوردن میدان مغناطیسی زمین در واحدهای گاوس ، باید داده های خام را در 0.00092 ضرب کنیم. در پایان مقادیر را روی مانیتور سریال چاپ خواهیم کرد.
#include <Wire.h> //I2C Arduino Library #define Magnetometer_mX0 0x03 #define Magnetometer_mX1 0x04 #define Magnetometer_mZ0 0x05 #define Magnetometer_mZ1 0x06 #define Magnetometer_mY0 0x07 #define Magnetometer_mY1 0x08 int mX0, mX1, mX_out; int mY0, mY1, mY_out; int mZ0, mZ1, mZ_out; float Xm,Ym,Zm; #define Magnetometer 0x1E //I2C 7bit address of HMC5883 void setup(){ //Initialize Serial and I2C communications Serial.begin(9600); Wire.begin(); delay(100); Wire.beginTransmission(Magnetometer); Wire.write(0x02); // Select mode register Wire.write(0x00); // Continuous measurement mode Wire.endTransmission(); } void loop(){ //---- X-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX1 = Wire.read(); } //---- Y-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ1 = Wire.read(); } //---- X-Axis mX1=mX1<<8; mX_out =mX0+mX1; // Raw data // From the datasheet: 0.92 mG/digit Xm = mX_out*0.00092; // Gauss unit //* Earth magnetic field ranges from 0.25 to 0.65 Gauss, so these are the values that we need to get approximately. //---- Y-Axis mY1=mY1<<8; mY_out =mY0+mY1; Ym = mY_out*0.00092; //---- Z-Axis mZ1=mZ1<<8; mZ_out =mZ0+mZ1; Zm = mZ_out*0.00092; //Print out values of each axis Serial.print("x: "); Serial.print(Xm); Serial.print(" y: "); Serial.print(Ym); Serial.print(" z: "); Serial.println(Zm); delay(50); }