Çok eksenli hareket sistemlerinde step motor kullanımı

Başlatan bektaskutlan, 23 Kasım 2019, 14:45:43

bektaskutlan

Herkese iyi çalışmalar diliyorum. Siz değerli üstadlarıma bazı konular hakkında danışmak istiyorum.

Bir süredir çok eksenli sistemlerin hareket kontrollerini anlamak için kaynak bulup o kaynaklara bakarak bu konularda bir şeyler öğrenip kendimi geliştirmeye çalışıyorum. Microdenetleyici olarak Atmel firmasına ait 8 bitlik işlemciler ile çalışıyorum. Arm ve STM işlemciler hakkında bilgi sahibi olmadığımdan üzülerek söylüyorum ki şuan o alanda bir çalışmam olmadı.

Bilgi almak istediğim konu hakkında Örnek verecek olursam  X ve Y eksenlerinde step motorla hareket eden bir sistem var. Bu iki eksenin cnc veya XYPlotter benzeri mekanizmalar gibi ortak bir şekilde çalışarak geometrik şekiller çizmesini istiyorum. Bu konuları araştırırken  öğrendiğin yada öğrendiğimi zannettiğim bazı bölümleri sizlerle paylaşacak olursam.

Örnek bir senaryo ile anladıklarımı size anlatmaya çalışayım.

örneğin X ve Y motoru current_positionXY =(0,0) başlayarak target_postion=(600,250) gibi bir pozisyona doğru giderek düz bir çizgi çizecek olsun.

Bu değerlere göre 
//  Bulunduğumuz koordinat
current_positionXY =(0,0)
// Gidilecek koordinat
target_postionXY=(600,250)

**Hedeften bulunduğumuz yeri çıkardığımızda yön vektörümüzü bulmuş oluruz
direction_vectorXY = tatget_position - current_position 

direction_vectorXY =(600 - 0 ,250 - 0)
direction_vectorXY=(600,250)


direction_vector_distance = sqrt(direction_vectorX ^ 2 + direction_vectorY ^ 2) // vectorun uzunluğu pisagor teoremi ile buldum.   

direction_vector_distance = 650 birim


direction_unit_vectorXY = (direction_vectorX / direction_vector_distance , direction_vectorX /direction_vector_distance)
Bu hesaplama ile birim vektörünü hesaplamış olduk

direction_unit_vectorXY = ( 600 /650 , 250/650 )
direction_unit_vectorXY =(0.923 ,  0.384)

Birim zamanda X 0.923 birim hareket ettiğinde Y de birim zamanda 0.384 hareket ettiğinde ikisi beraber doğrusal bir hareket yapmış olurlar

Örneğin ( Not bu hız sabit bir hızda harekettir. Zamana göre ivmelenme yoktur.)

fx(t) = t * 0.923 birim zamandaki x hareketinin fonksiyonudur.
fy(t) = t * 0.384 birim zamandaki y hareketinin fonksiyonudur.
toplam hareket = fx(t) + fy(t)
toplam hareket süresi = t

Yukarıdaki  gibi   birbirine eşit olmayan X Y pozisyonları ile  hareket hesaplamaları genelde bu tür kesirli işlemlerle yapılıyor.  Oysa Step motorlarla hareket ederken hareketin tam sayılarla
yapılması gerekiyor. Yani adım adım gitmesi gerekiyor.

Yukarıdaki hesaplamalarda mm veya cm cinsinden birim kullanılmamıştır. Yani motorun attığı bir adım bir birime denk gelmektedir. Arada dönüşüm yoktur.

Burada kafamı karıştıran nokta yukarıdaki kesirli işlemlerin nasıl step motor adımlarına dönüştürülmesi gerektiğidir. Bu tür sistemlerde hareket sistemi matematiği nasıl çalışıyor. Bu konuları anlamakta
zorluk çekiyorum. Temelim iyi olmadığından nereden nasıl başlayacağımı pek anlayamadım. Bilen üstadlarımdan bu konuda açıklama yaparak forumdaki bizim gibi bu konulara meraklı olan arkadaşlara rehber olmalarını
umut ediyorum.

GRBL  ve Marlin gibi açık kaynak kodlu yazılımlar var ama içeriğini ve hareketi matematiksel olarak nasıl oluşturduğunu pek anlayamadım. Bu tür sistemlerde interpolarasyon yapılır deniyor ama motor step adımıyla
interpolarasyonu nasıl birleştireceğim pek anlayamadım.

Bazı algoritma denemelerim oldu ama doğru yoldamıyım onu bile bilmiyorum.
Örneğin yukardaki 600 ,250 xy pozisyonu için şöyle bir yöntem düşündüm.
Anladığım kadarıyla bu tip hareket sistemlerinde her iki ekseninde tüm hareketi aynı sürede tamamlaması gerekiyorki doğrusal bir hareket çizgisi oluşsun.

İki ekseninde aynı sürede hareketi tamamlayabilmesi için
X exseni 600 adımı 1 saniyede atmış olsun.
O zaman X ekseni bir adımı 1 / 600 saniyede atmış olur = yaklaşık 1/600 = 0,000160 saniye  = 160 us ( mikrosaniyede 1 adım atar )
Y ekseni 250 adımı 1 saniyede atmış olsun
O zaman Y ekseni bir adımı 1/250 saniyede atmış olur = yaklaşık = 1/250 = 0,0,004 saniye = 4 ms  (milisaniye de 1 adım atar)

GRBL va Marlin gibi açık kaynak yazılımlar Bu tür hesaplamaları Timer ISR kullanarak yapıyorlar. Ama ben bunu nasıl yaptığını pek anlayamadım. Buradaki adım atma zaman süreleri
Timer kullanarak nasıl dönüştürülür. GRBL ve Marlinde Bresenham Line Drawing Algorithmasını  kullandığı yazıyor. Ama ben aradaki dönüşümün nasıl yapıldığını henüz kavrayamadım. Bu sistemlerde kullanılan matematiksel hesaplamalar nasıl yapılıyor öğrenmek istiyorum.
Çok arama yapmama rağmen Türkçe kaynak bulamadım.
Burada bulunan çok bilgili üstadlar var.
Eğer açıklama ve örnek vererek konuyu biraz olsun aydınlatırlarsa çok kişiye faydası olur diye düşünüyorum.

Tekrardan Herkese iyi çalışmalar ve başarılar diliyorum.

bektaskutlan

Değerli üstadlar bu konuyu araştırırken bazı şeyleri ezbere değilde , anlayarak öğrenmeye çalışıyorum.
Öğrendiğim bir hareket yapısını kendimce koda yansıtmaya ve açıklamaya çalıştım. Ama ne kadar doğru bir yol izlemişim yada anlamışmıyım tam emin değilim. izlediğim yöntem sizce doğru mudur? .
Eğer doğru ise başka ne gibi yöntemler kullanırsam daha güzel ve hatasız hareket elde edebilirim. İşin en çok matematik ve fizik kısımlarını öğrenmek istiyorum. Hangi konularda bilgi sahibi olmam ve ne gibi konuları araştırmam gerektiği konusunda sizlerden tavsiye istiyorum. 
Açıklamaya ve kendimce  anladığım kadarıyla yazmaya çalıştığım Arduino kodu aşağıdaki gibidir.
Herkese iyi çalışmalar kolay gelsin.   
----------------------------------
// Kartezyen koordinat sisteminde o onda bulunulan X ve Y pozisyonunu saklayan değişkenler
float CURRENT_POSITION_X = 0.0f; 
float CURRENT_POSITION_Y = 0.0f; 
//------------------------------------------------------------------------------

// X ve  Y ekseninde  bulunan motorun 1 mm hareket edebilmesi için gerekli adım sayısı miktarını saklayan değişkenler
// Bu değerler uzunluk birimini step adım değerine dönüştürmek için kullanılacaktır.
// Bu değerler sistemin yapısna göre değişiklik gösterecektir.
// Buradaki örnekte standart olmayan keyfi bir rakam kullanılmıştır.
// Tam vaya yaklaşık değerlerin bulunabilmesi için çeşitli kalibrasyon yöntemleri denenebilir.  
float XAXIS_STEP_PER_MM = 100; 
float YAXIS_STEP_PER_MM = 100; 
 // --------------------------------------------------------

// Eksenleri hareket ettiren motorların yönlerini belirleyen değişkenler
int DIRECTION_X = 1; 
int DIRECTION_Y = 1;
// -----------------------------------------------------------------------

// Step motor hızını belirleyen 2 step adım arasındaki bekleme süresini saklayan değişken
long STEP_DELAY_TIME = 400;  // Microsaniye 
// ---------------------------------------------------------------------------------

// Eğer eksenlerdeki toplam adım sayıları en küçük değer olarak belirlediğimiz epsilon_x ve epsilon_y değerlerinden küçük ise 0 olduğunu kabul ediyoruz. 
// Bu işlemi ondalıklı sayılardan kaynaklı oluşabilecek hataları en aza 
// indirebilmek için yapıyoruz. epsilon değerini belirlerken step (adım) çözünürlüğüne göre hesaplama yapmak gerekiyor.
// Buda sistemde kullanılan fiziksel hareket yapılarına göre değişkenlik göstermektedir. Örneğin hareketi aktarmak için Vidalı mil
// kullanıyorsak  ( Vidalı mil 360 derece dönüş hareket uzunluğu ) /  ( motor tam dönüş sayısı X microstep sayısı ) ile bulabilirsiniz.
// Örneğin  Motorumuz tam dönüşü 200 adımda tamamlamış olsun  sürücü devremiz 32 microstep destekleyen bir devre olsun
// Hareket aktarımı için 8mm sonsuz vida ve somun kullanılmış olsun bu durumda standarda göre hatve yani 360 derece döndüğünde 1,25 mm lik bir hareket 
// elde etmiş oluruz. Bu durumda  step adım çözünürlüğümüz
//  [ 1,25 mm / 200 x 32  = 1,25 mm /6400 = 0,0001953125 mm ] olur. Buradaki hesaplamaya göre tek bir step adımında 0,0001953125 mm hareket etmiş oluruz
// Bu sayı bizim için en küçük hareket miktarını yani step çözünürlüğünü temsil etmektedir. Bu örnekte bunun altındaki hareketler 0 adım olarak kabul edilebilir. 
float epsilon_x = 0.0001f; 
float epsilon_y = 0.0001f;
//---------------------------------------------------------------------------------

// Bu değişkenin ne işe yaradığı biraz aşağıdaki bölümlerde açıklanmıştır.
float STEP_COUNTER_XY = 0.0f;

// MoveTypeXY ile eksenlerin nasıl hareket edeceği bulunacaktır.
// Aşağıdaki yapı incelendiğinde kullanım amacı daha detaylı anlaşılacaktır.
enum MoveTypeXY {	
					MoveX,       // Yalnızca X ekseni hareket eder
					MoveY,      // Yalnızca Y ekseni hareket eder
					MoveXY,    // X ve Y ekseni birlikte hareket eder. X ekseni ana eksen , Y ekseni ikinci eksendir.
					MoveYX,   // X ve Y ekseni birlikte hareket eder. Y ekseni ana eksen , X ekseni ikinci eksendir.
					DontMove // İki eksende hiç hareket etmez...	
};

MoveTypeXY MoveType = DontMove;

void oneStepX(){
	//X eksenindeki motorun 1 adım atması için gerekli işlemler buraya yazılacaktır.
	// örneğin 
	// digitalWrite(xstep_pin , HIGH);
	// digitalWrite(xstep_pin , LOW );
	// delay( STEP_DELAY_TIME );
	// gibi
}
	
void oneStepY(){	
	//Y eksenindeki motorun 1 adım atması için gerekli işlemler buraya yazılacaktır.
	// örneğin 
	// digitalWrite( ystep_pin , HIGH);
	// digitalWrite( step_pin , LOW );
	// delay( STEP_DELAY_TIME );
	// gibi
}


void MoveXY(float target_position_x ,float target_position_y)  // Hareketi gerçekleştirecek fonksiyonumuz.. 
{		
	// Gidilecek pozisyondan bulunduğumuz pozisyonu çıkartıp bunu 1 mm gidebilmek için gerekli adım sayısıyla çarparak toplam motor step ( adım )  miktarını buluyoruz.
	float total_step_x = ( target_position_x - CURRENT_POSITION_X ) *  XAXIS_STEP_PER_MM ;
	float total_step_y = ( target_position_y - CURRENT_POSITION_Y ) *  YAXIS_STEP_PER_MM ;
	//---------------------------------------------------------------------------------------	
		
	// Eksen adım sayılarının negatif yada pozitif sayı olma durumuna göre motor dönüş yönü belirleniyor.
	
	DIRECTION_X = ( total_step_x < 0 ? -1 : 1 );
	DIRECTION_Y = ( total_step_y < 0 ? -1 : 1 );
	// ( DIRECTION_X == 1 ? digitalWrite( xdir_pin ,LOW ) : digitalWrite( xdir_pin , HIGH ) );
	//( DIRECTION_Y == 1 ? digitalWrite(ydir_pin ,LOW ) : digitalWrite( ydir_pin , HIGH ) );
	
	//--------------------------------------------------------------------------------------------------
	
	// Ana eksen  ve diğer ekseni belirlemek , büyüklükleri karşılaştırmak  için  eksen adım sayıları negatif ise pozitif sayı olması için  ( -1 )sayısı ile çarpılarak mutlak değeri bulunuyor. 
	// Bu yöntemdeki amaç en çok adım atan ekseni bulup ,  az adım atan diğer ekseni bu eksene göre orantılayarak adım atmasını sağlamaktır.
		
	float total_step_x_abs  = total_step_x * ( total_step_x < 0? -1 : 1) ;	
	float total_step_y_abs =  total_step_y * (total_step_y < 0? -1 : 1) ;
	
	//---------------------------------------------------------------------------------------	
	
	// Eğer toplam adım sayıları en küçük değer olarak belirlediğimiz epsilon_x ve epsilon_y değerlerinden küçük ise 0 olduğunu varsayıp 
	 // total_step_x ve total_step_y değerlerini sıfıra eşitliyoruz. Bu işlemi ondalıklı sayılardan kaynaklı oluşabilecek hataları en aza 
	 // indirebilmek için yapıyoruz. epsilon değerini belirlerken step (adım) çözünürlüğüne göre hesaplama yapmak gerekiyor.
	 // Buda sistemde kullanılan fiziksel hareket yapılarına göre değişkenlik göstermektedir. Örneğin hareketi aktarmak için Vidalı mil
	 // kullanıyorsak  ( Vidalı mil 360 derece dönüş hareket uzunluğu ) /  ( motor tam dönüş sayısı X microstep sayısı ) ile bulabilirsiniz.
	 // Örneğin  Motorumuz tam dönüşü 200 adımda tamamlamış olsun  sürücü devremiz 32 microstep destekleyen bir devre olsun
	 // Hareket aktarımı için 8mm sonsuz vida ve somun kullanılmış olsun bu durumda hatve yani 360 derece döndüğünde 1,25 mm lik bir hareket 
	 // elde etmiş oluruz. Bu durumda  step adım çözünürlüğümüz
	 //  [ 1,25 mm / 200 x 32  = 1,25 mm /6400 = 0,0001953125 mm ] olur. Buradaki hesaplamaya göre tek bir step adımında 0,0001953125 mm hareket etmiş oluruz
	 // Bu sayı bizim için en küçük hareket miktarını yani step çözünürlüğünü temsil etmektedir. Bu örnekte bunun altındaki hareketler 0 adım olarak kabul edilebilir. 
	 
	if( total_step_x_abs < epsilon_x )
	{
		total_step_x = 0.0f;
	
	}
	if(total_step_y_abs < epsilon_y )
	{
		total_step_y = 0.0f;
	
	}	
	//--------------------------------------------------------------------------------------------------------------------
	
	// Eğer her iki ekseninde toplam motor step( adım ) sayısı 0 ise Herhangi bir adım atmadan fonksiyondan çıkılıyor.  
	
	if( ( total_step_x == 0 ) && (total_step_y == 0 )
	{
		MoveType = DontMove;
		return;
	
	}
			
	//----------------------------------------------------------------------------------------
		
	// Bu değişkenlerin kullanımı  aşağıda daha net bir şekilde açıklamıştır.
	float deltaxy_increment = 0;
	float deltaxy_over = 0;	
	STEP_COUNTER_XY = 0.0f;
	
	//Eğer sadece X ekseni adım atma sayısı 0 ise yalnızca Y ekseninde hareket etmek gerekiyor. X ekseninin hareket etmesine gerek yok 	
	if( total_step_x_abs == 0 ) // 
	{		
		MoveType = MoveY;	
		deltaxy_over = 0;	
		deltaxy_increment = 0;
		STEP_COUNTER_XY = 0.0f;
		
	}	
	//Eğer sadece Y ekseni adım atma sayısı 0 ise yalnızca X ekseninde hareket etmek gerekiyor. Y ekseninin hareket etmesine gerek yok 
	else if ( total_step_y_abs == 0 )
	{
		MoveType = MoveX;
		deltaxy_over = 0;	
		deltaxy_increment = 0;
		STEP_COUNTER_XY = 0.0f;
			
	}
	// Her iki ekseninde hareket edeceği için Ana Master eksen ile diğer eksen için karşılaştırmalar yapılıyor.
	else
	{		
		//Eğer X ekseninin adım sayısı Y ekseninkinden fazla ise ana eksen X ekseni olarak belirleniyor.
		if( total_step_x_abs > total_step_y_abs )
		{
			// Buradaki hesaplama ile  iki eksen arasındaki oran bulunmaya çalışılıyor.
			// Örneğin X adım sayısı 100 Y adım sayısı 50 olan bir durumda 
			//  100/50 den oran 2 olarak bulunacaktır. 
			// Yani x ekseni 2 adım attığında Y ekseni 1 adım atarsa ikisi aynı sürede belirlenen noktalara ulaşarak
			// doğrusal bir hareket , şekil oluşturmuş olurlar.
			// Bu yapıya göre  deltaxy_over değişkeni en büyük eksen adım sayısına eşitleniyor.
			// deltaxy_increment değeri ise daha az adım sayısı olan  diğer eksenin adım sayısına eşitleniyor
			// STEP_COUNTER_XY değerine sürekli deltaxy_increment değeri ekleniyor
			// Eğer STEP_COUNTER_XY değeri deltaxy_over değerine eşit veya büyük olursa 
			// ikinci eksen adım attırılıyor. Bu sayede iki eksen arasındaki orana göre farklı iki eksen doğrusal olarak
			// hareket ettirilmiş oluyor. 
			
			// Buradaki değişkenler  aşağıdaki switch case yapısında işleme alınacaktır. Burada sadece değerleri belirlenmektedir.
		
			MoveType = MoveXY ; // Bu tanımlama ile hareket türü seçilmiştir. Bu hareket türünde X ana eksen Y ise ikinci eksendir.
			deltaxy_over = total_step_x_abs ;   // İkinci eksen yani Y ekseni için adım atma taşma değeri belirleniyor. 
			deltaxy_increment = total_step_y_abs; // X ekseni her adım attığında bu değer 	STEP_COUNTER_XY değişkenine eklenecek.
			// STEP_COUNTER_XY değişkenindeki değer deltaxy_over değerine eşit veya büyük olması durumunda İkinci eksen yani Y ekseni 1 adım atacaktır
			STEP_COUNTER_XY = 0; // ikinci eksen step control değişkeni sıfırlanıyor.
			
		}
		//Eğer Y ekseninin adım sayısı X ekseninkinden fazla ise ana eksen Y ekseni olarak belirleniyor.
		else if( total_step_y_abs > total_step_x_abs )
		{
			MoveType = MoveYX ;  // Bu tanımlama ile hareket türü seçilmiştir. Bu hareket türünde Y ana eksen X ise ikinci eksendir.
			deltaxy_over = total_step_y_abs;
			deltaxy_increment = total_step_x_abs;
			STEP_COUNTER_XY = 0;
		
		}
		//Her iki eksen adım sayısı da birbirine eşit ise keyfi olarak X ekseni ana eksen olarak seçilmiş oluyor. 
		else
		{
			
			MoveType = MoveXY ; // Bu tanımlama ile hareket türü seçilmiştir. Bu hareket türünde X ana eksen Y ise ikinci eksendir.
			deltaxy_over = total_step_x_abs ;  
			deltaxy_increment = total_step_y_abs;
			STEP_COUNTER_XY = 0;
		
		}
	
	}	
	
	// Aşağıdaki switch case yapısında hareket türüne göre step motorlar hareket ettiriliyor.
	
	switch(MoveType)
	{
		case MoveXY:
		
		// Toplam Step adımlarının  ondalık kısımlarının yok edilmesi için yuvarlama işlemi yapılıyor. 
		long total_xstep = (long) (total_step_x_abs + 0.5 );
		
		for(long xstep = 0 ; xstep <  total_xstep ; xstep ++ )
		{
			
			STEP_COUNTER_XY += deltaxy_increment ;
			if( STEP_COUNTER_XY >= deltaxy_over )
			{
				oneStepY(); // Y ekseni 1 Adım attıtılıyor
				 // STEP_COUNTER_XY değeri ana eksen deltaxy_over değerine eşit veya büyük olması durumunda sayacı sıfırlamak için deltaxy_over çıkarılıyor. 
				STEP_COUNTER_XY -= deltaxy_over ; 
			
			}
			
			oneStepX();// X ekseni 1 Adım attıtılıyor
			delayMicroseconds( STEP_DELAY_TIME);
			
		
		}
		
		
		
		break;
		case MoveYX:		
		
		// Toplam Step adımlarının  ondalık kısımlarının yok edilmesi için yuvarlama işlemi yapılıyor. 
		long total_ystep = (long) (total_step_y_abs + 0.5 );
		
		for(long ystep = 0 ; ystep <  total_ystep ; ystep ++ )
		{
			STEP_COUNTER_XY += deltaxy_increment ;
			if( STEP_COUNTER_XY >= deltaxy_over )
			{
				oneStepX(); // X ekseni 1 Adım attıtılıyor
				 // STEP_COUNTER_XY değeri ana eksen deltaxy_over değerine eşit veya büyük olması durumunda sayacı sıfırlamak için deltaxy_over değeri çıkarılıyor. 
				STEP_COUNTER_XY -= deltaxy_over ; 
			
			}
			oneStepY();// Y ekseni 1 Adım attıtılıyor
			delayMicroseconds( STEP_DELAY_TIME);
		}		
		
		
		
		break;
		case MoveX:		
		// Burada yalnızca X ekseni hareket ettiği için Y ekseni için herhangi bir işlem veya hesaplama yapmaya gerek yoktur
		long total_xstep = (long) (total_step_x_abs + 0.5 );
		
		for(long xstep = 0 ; xstep <  total_xstep ; xstep ++ )
		{
			oneStepX();
			delayMicroseconds( STEP_DELAY_TIME);
		
		}
		
		break;
		
		case MoveY;
		// Burada yalnızca Y ekseni hareket ettiği için X ekseni için herhangi bir işlem veya hesaplama yapmaya gerek yoktur
		
		long total_ystep = (long) (total_step_y_abs + 0.5 );
		
		for(long ystep = 0 ; ystep <  total_ystep ; ystep ++ )
		{
			oneStepY();
			delayMicroseconds( STEP_DELAY_TIME);
		
		}
		
		break;
		
		case DontMove:
		// İki eksen adım sayısıda 0 olduğu için herhangi bir işlem yapmadan fonksiyondan çıkış yapılıyor.
		return;
		
		break;		
	
	
	}

}