Bresenham Algoritma (takıldığım noktalar var yardımcı olurmusunuz ?)

Başlatan computerboy, 12 Şubat 2013, 13:25:28

computerboy

Arkadaşlar kodu internette buldum 2-3 gündür irdelemeye çalışıyorum anlayamadığım noktalar var neden böyle olmuş çözemedim. yardımcı olursanız sevinirim aşama aşama buraya soracağım tecrübeli arkadaşların yardımını bekliyorum. Saygılar.

Anlayamadığım yada anladığım :) yada anladığımı sandığım noktaları kodun içine gömdüm.

#define SL signed short long 

#define MAX(a,b) (((a)>(b))?(a):(b))

#define ABS(a) (((a)<0) ? -(a) : (a))

#define ZSGN(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0)

extern void point3d(SL x, SL y, SL z);
void line3d(int step,long end,SL  x1, SL  y1, SL  z1, signed long  x2, signed  long  y2, signed  long  z2);

SL 	x=0,       			// SL = Signed short Long = 24bits
		y=0, 
		z=0,
 	oldX=0, 
		oldY=0, 
		oldZ=0, 
		t=0;

signed char sx=0, 			
		sy=0, 
		sz=0;  			// 8bit= -127 to 128       
							

signed long x2,
		y2,
		z2;

long  		enddelay,
 		stepcount;	

int 		stepdelay;





void line3d(int step,long end,SL  x1, SL  y1, SL  z1, signed long  x2, signed  long  y2, signed  long  z2)
   
{	

    SL  x, y, z; 		// signed shot long tanımlamalar
    SL  xd, yd, zd;
    SL  ax, ay, az;
    SL  dx, dy, dz;

	stepdelay=step;		// integer
	enddelay=end;		// long

    dx = x2 - x1;		// x2 den x1 çıkartılıyor sonuç dx aktarılıyor.
    dy = y2 - y1;
    dz = z2 - z1;

    ax = ABS(dx) << 1;	// sola 1 bit kaydırılıyor (burdaki amacı anlamadım sonuçta çarpma işlemi olmuyormu bu ?)
    ay = ABS(dy) << 1;
    az = ABS(dz) << 1;

    sx = ZSGN(dx);		// gelen rakam küçükse sadece -1 değer alıyor büyükse sadece +1 değer alıyor
    sy = ZSGN(dy);
    sz = ZSGN(dz);

    x = x1;
    y = y1;
    z = z1;
	stepcount=0;

    if (ax >= MAX(ay, az))            	// ax ay ve az den büyükmü diye bakılıyor.
    {
        yd = ay - (ax >> 1);		// burda ise 1 bit sağa kaymış(kısacası bölmüş niye ?) 
        zd = az - (ax >> 1);
        for (;;)
        {
            point3d(x,y,z);		// step motoru sürmek için gönderilen step bilgisi.	
            if (x == x2)                              // x değeri x2 ile eşit olana kadar döngüden çıkılmayacak
            {
                return ;
            }

            if (yd >= 0)                            // yd sıfırdan büyük yada eşitse y değerine pozitif veya negatif değer ata.
            {
                y += sy;
                yd -= ax;                           // yd değerine ax değerini çıkar ve ata.
            }

            if (zd >= 0)
            {
                z += sz;
                zd -= ax;
            }

            x += sx;
            yd += ay;
            zd += az;
        }
    }                                                 
}



Salih


computerboy

Arkadaşlar çok özür konu başlığını yanlışlıkla CCS' ye açmışım PIC C kısmına taşınabilir.

Programı C18 ile çalışıyorum.

computerboy


iyildirim

Aslında açıklamaların dışında sorduğun tek soru neden iki ile çarpılıp bölündüğü..
Nedeni ise mcu üzerinde 0.5 gibi float ve üzerinde işlem yapması yorucu bir sayı ile uğraşmak yerine 2 ile çarparak integer sayılarla çalışması. Daha sonra ikiye bölünmesi de önceden 2 ile çarpıldığı için.. 
Yani float yerine integer sayı kullanmak için scale işlemi yapılmış..


computerboy

Ordaki yazdığım açıklamalar doğrumu üstad. ordaki algoritma mantığını çözmeye çalışıyorum amacı tam anlamıyla kavrayamadım. mesela x_step 500 y_step 1400 z_step 600 bu döngüyü kurarken en büyük rakamı buluyor ve diğer değerleri 1400 rakamı içinde eşit bölüp step sayısını geçen zamana göre uygun değerdemi gönderiyor tam anlamıyla sormak istediğimide ifadede edemedim. kodun mantığını çözsem yeter. sonra timer2 yada timer3 ile uygun frekans uygulaması yapacağım.

iyildirim

24 bit ve -127 +128 (-128 + 127 olmalı) dışında hatalı birşey gözüme çarpmadı.
Algoritmanın float ve integer kullanımı için dökümana bir bak.
http://www.cs.toronto.edu/~smalik/418/tutorial2_bresenham.pdf
Bulduğun-bulacağın hazır kod parçaları büyük ihtimalle birbirleri veya yapmak isteğin şeyle, kafanda kurduğun algoritma ile uyumlu olmayacak. Öncelikle algoritmayı anlamaya ve uygulamana nasıl implemente edeceğine odaklanmalısın.

CNCiçin kullanacaksan zamanlamalar önemli.. Hem dökümanda hemde verdiğin kodda zamanlama ile ilgili bir şey yok.. point3d nin altında bekleme varsa bilemem..
Örnekler daha çok LCD veya grafik bellekte çizgi çizmekle ilgili. putpixel, point3d fonksiyonlarının ne zaman çağrıldıkları cnc için önemli. Ne zaman çağırıldıkları da hız ve hızlanmaya bağlı..

Fikir vermesi açısından; Algoritmaya göre daha uzun yol kateden eksende fonksiyon her çağrıldığında hareket olur diye düşünürsek yapabileceğin;
ya önceden hesaplayıp her adımı bufferlamak ve uzun yol kateden eksene göre zamanını ayarladığın bir timer kesme ile bufferı pinlere aktarmak,
yada  hareket anında hesaplamak ve gerekli gecikmeleri de hesaplayıp beklemek..
Bresenham kullanacaksan bileşke hızdan eksen hızına dönmen ve timerı buna göre kurman gerekir.


Bresenham kullanmak istemezsen şöyle bir şeyde yapabilirsin doğrusal enterpolasyon için..
İstediğimiz aslında;
Her eksenin aynı anda harekete başlayıp, gidecekleri yol farklı bile olsa yolu aynı zaman aralığında katetmeleri.
Önce bileşke hızı hesaplayıp, istenen hız, hızlanma değerine göre yolun ne kadar zamanda gidileceğini hesaplarsın. 
Her bir eksen yolu x saniyede gidecekse ve gidecekleri yolda belli ise geriye kalan tek şey, her bir eksen için hızı hesaplayıp timer kurmak olur.. Eğer hız aralıkların uygunsa (ki uygun prescaler değerleri kullanarak uydurabilirsin) timer kesmelerinde darbeleri üretebilirsin. Hatta sadece uzun yol kateden eksene ait timer kesmelerini saymak diğerlerini mcu da uygunsa PWM modunda bırakmak da olabilir. Tabii sadece enterpolasyon modunda iken ve sabit hız için.   Hızlanma aşamasında diğer eksenlerin hızları (timer değerleri) en hızlı eksene (master eksen) göre başta hesapladığımız orana göre tekrar hesaplanmalı

G-code'u-komutu alınca biraz hesap dan sonra bir daha başında beklemek gerekmediği için bu yöntem bana daha uygun görünüyor. Timer değerleri bir kaç komut veya makul bir süre için hesaplanırsa cnc hiç duraklamadan çalışabilir. 
Dairesel enterpolasyon için bu yöntemi küçük adımlar için çalıştırabilir veya Bresenham kullanabilirsin..

computerboy

Teşekkürler hocam anladım algoritmayı kurdum şimdide timer üzerinde çalışıyorum yalnız timeri aktif edince usb çalışması duruyor nette araştırdım ama nasıl yapacağımı çözemedim şu şekilde bir hata ile karşılaşıyorum.

Error - section 'high_vector' can not fit the absolute section. Section 'high_vector' start=0x00000008, length=0x00000732

verdiğim vektör adreside bu

#pragma code high_vector=0x08

18f4550.lkr dosyasından vectors  adresi yazan yerde nasıl bir düzenleme yapılması lazım bilgisi olan varmı ?

18f4550.lkr

// $Id: 18f4550.lkr,v 1.3 2004/08/23 18:08:22 curtiss Exp $
// File: 18f4550.lkr
// Sample linker script for the PIC18F4550 processor

LIBPATH .

FILES c018i.o
FILES clib.lib
FILES p18f4550.lib

CODEPAGE   NAME=vectors    START=0x0            END=0x29           PROTECTED 
CODEPAGE   NAME=page       START=0x2A           END=0x7FFF
CODEPAGE   NAME=idlocs     START=0x200000       END=0x200007       PROTECTED
CODEPAGE   NAME=config     START=0x300000       END=0x30000D       PROTECTED
CODEPAGE   NAME=devid      START=0x3FFFFE       END=0x3FFFFF       PROTECTED
CODEPAGE   NAME=eedata     START=0xF00000       END=0xF000FF       PROTECTED

ACCESSBANK NAME=accessram  START=0x0            END=0x5F
DATABANK   NAME=gpr0       START=0x60           END=0xFF
DATABANK   NAME=gpr1       START=0x100          END=0x1FF
DATABANK   NAME=gpr2       START=0x200          END=0x2FF
DATABANK   NAME=gpr3       START=0x300          END=0x3FF
DATABANK   NAME=usb4       START=0x400          END=0x4FF          PROTECTED
DATABANK   NAME=usb5       START=0x500          END=0x5FF          PROTECTED
DATABANK   NAME=usb6       START=0x600          END=0x6FF          PROTECTED
DATABANK   NAME=usb7       START=0x700          END=0x7FF          PROTECTED
ACCESSBANK NAME=accesssfr  START=0xF60          END=0xFFF          PROTECTED

SECTION    NAME=CONFIG     ROM=config

STACK SIZE=0x100 RAM=gpr3

Goo

Kesme bölümünü ben aşağıdaki şekilde kullanıyorum ve şimdiye kadar bir problemle karışlaşmadım. Tabii kullanılan kesmenin fonksiyon prototipini(void tmr0_kesmesi(void)) yukarıda tanımlamayı unutmamak lazım.

#pragma code high_vector_section=0x08
void high_vector(void)
{
  if(INTCONbits.TMR0IF==1)
  {
   _asm GOTO tmr0_kesmesi _endasm
  }
}
#pragma code
#pragma interrupt tmr0_kesmesi
void tmr0_kesmesi(void)
{
  INTCONbits.TMR0IF=0;
}

computerboy

usb yi devre disi birakinca timer calisiyor kesme vektorunu usbde kullaniyor tahminimce yada baska bir nedeni var arastirdim lkr dosyasindaki page ayarini uygun hale getirilmesi lazimmis ama nasil yapacagimi cozemedim

computerboy

timer kullanmaktan şimdilik vazgeçtim. hocam şu şekilde bir algoritma kurdum putmotor kısmında step sayısı fazla olunca step sayısı az olan motorda hafif titreme oluyor onu nasıl bir algortima mantığı ile çözerim. mesela x_step 5000 y_step 6000 olunca x_step motorunda titreme oluyor.  aradaki step farkı 1000 her motor için step farkına göre  gecikme yaratsam sorun çözülürmü acaba.

/***** Bresenham Algoritma ******************************************************/

#include "plazmaport.h"
#include "bresenham.h"
#include "delays.h"


SL			x=0,       	   // Signed Long Tanımlama
			y=0, 
			z=0,
 			oldX=0, 
			oldY=0, 
			oldZ=0, 
			x2,
			y2,
			z2;

signed char sx=0, 			// Signed Char Tanımlama
			sy=0, 
			sz=0;  			  
						
long  		stepcount=1;
	
int 		x_h;
int 		y_h;
int 		z_h;

int 		x_h_s=30;
int 		y_h_s=30;
int 		z_h_s=30;


void putMotor(SL x, SL y, SL z )
{

if(x-oldX==1) DIR_X = 1;else DIR_X = 0; 
if(y-oldY==1) DIR_Y = 1;else DIR_Y = 0; 
if(z-oldZ==1) DIR_Z = 1;else DIR_Z = 0;  

if(x_h>x_h_s) x_h=x_h--;
if(y_h>y_h_s) y_h=y_h--;
if(z_h>z_h_s) z_h=z_h--;


if(x-oldX==1 || x-oldX==-1) STP_X = 0;
Delay100TCYx(x_h);
if(y-oldY==1 || y-oldY==-1) STP_Y = 0; 
Delay100TCYx(y_h);
if(z-oldZ==1 || z-oldZ==-1) STP_Z = 0; 
Delay100TCYx(z_h);

STP_X =1;
Delay100TCYx(x_h);
STP_Y =1;
Delay100TCYx(y_h);
STP_Z =1;  
Delay100TCYx(z_h);
         				
stepcount=stepcount+1;

	oldY = y;
	oldX = x;
	oldZ = z;

}

//**********************************************************
void line3d(int s_d, int e_d, SL  x1, SL  y1, SL  z1, SL  x2, SL  y2, SL z2)
{	

	SL  x, y, z;		//Signed Long Tanımlamalar
    SL  xd, yd, zd;
    SL  ax, ay, az;
    SL  dx, dy, dz;

    dx = x2 - x1;		//x2 değerinden x1 değerini çıkar ve dx değerine ata.
    dy = y2 - y1;
    dz = z2 - z1;

    ax = ABS(dx) << 1;	// sola 1 bit kaydırılıyor (bir nevi çarpma işlemi)
    ay = ABS(dy) << 1;
    az = ABS(dz) << 1;

    sx = SGN(dx);		// gelen rakam küçükse -1 değer, büyükse +1 değer alıyor.
    sy = SGN(dy);
    sz = SGN(dz);

    x = x1;				// atamalar yapılıyor.
    y = y1;
    z = z1;

	stepcount=0;		// step count sıfırlanıyor.

	x_h = x_h_s*2;		// Yavaşlama hızlanma
	y_h = y_h_s*2;
	z_h = z_h_s*2;


    UsbVeriGonder((rom char*)"2D Kesim İşlemi");
    if (ax >= MAX(ay, az))            	/* x axis hesaplaması */
    {
        yd = ay - (ax >> 1);		 	// (ax 1 bit sağa kaydır. (bölme işlemi sayılır) ay değerinden çıkar ve yd değerine ekle.
        zd = az - (ax >> 1);
        for (;;)						// döngü kuruluyor
        {
            putMotor(x, y, z);
            if (x == x2)
            {
                return ;
            }

            if (yd >= 0)
            {
                y += sy;
                yd -= ax;
            }

            if (zd >= 0)
            {
                z += sz;
                zd -= ax;
            }

            x += sx;
            yd += ay;
            zd += az;
        }
    }
    else if (ay >= MAX(ax, az))            /* y dominant */
    {
        xd = ax - (ay >> 1);
        zd = az - (ay >> 1);
        for (;;)
        {
            putMotor(x, y, z);
            if (y == y2)
            {
                return ;
            }

            if (xd >= 0)
            {
                x += sx;
                xd -= ay;
            }

            if (zd >= 0)
            {
                z += sz;
                zd -= ay;
            }

            y += sy;
            xd += ax;
            zd += az;
        }
    }
    else if (az >= MAX(ax, ay))            /* z dominant */
    {
        xd = ax - (az >> 1);
        yd = ay - (az >> 1);
        for (;;)
        {
            putMotor(x, y, z);
            if (z == z2)
            {
                return ;
            }

            if (xd >= 0)
            {
                x += sx;
                xd -= az;
            }

            if (yd >= 0)
            {
               y += sy;
                yd -= az;
            }

            z += sz;
            xd += ax;
            yd += ay;
        }
    }
}





Goo

Eğer aynı vektörü kullanıyorlarsa; usb kesme bağrağını(eğer varsa bilmiyorum) aynı yöntemle test edip ilgili fonksiyonuna dallandırabilirsiniz.
#pragma code high_vector_section=0x08
void high_vector(void)
{
  if(INTCONbits.TMR0IF==1)
  {
   _asm GOTO tmr0_kesmesi _endasm
  }
  if(usb_kesme_bayragi==1)
{
  _asm GOTO usb_kesmesi _endasm
}
}
#pragma code
#pragma interrupt tmr0_kesmesi
void tmr0_kesmesi(void)
{
  INTCONbits.TMR0IF=0;
}
void usb_kesmesi(void)
{
  usb_kesme_bayragi=0;
}

computerboy

evet hocam dediğiniz gibi usbde aynı vektörleri kullanıyor. şimdilik timer işini rafa kaldırdım algoritma üzerine çalışıyorum hızlanma ve yavaşlama olayını çözsem nasıl bir algoritma ile çözerim acaba

iyildirim

Alıntı yapılan: computerboy - 16 Şubat 2013, 22:01:57
evet hocam dediğiniz gibi usbde aynı vektörleri kullanıyor. şimdilik timer işini rafa kaldırdım algoritma üzerine çalışıyorum hızlanma ve yavaşlama olayını çözsem nasıl bir algoritma ile çözerim acaba

C18'i de 18F serisini de detaylı bilmiyorum. O konuda yorum yapamam..

Timer işini rafa kaldırman doğru değil.  Delay gibi fonksiyonlarla yapacağın bekleme araya girecek kesme kodları vs. nedeniyle her zaman aynı süreyi vermeyecek. Üstelik usb de kullanıyorsan her 1 ms de kesme çalıştırıyorsun..
Timer kullanmak için illa da kesme kullanmak zorunda değilsin. bekleme fonksiyonunda TMR registerinin istediğin değere gelmesini beklemen yeterli. Üstelik bu şekilde kullanmak yazdığın koda daha uygun. 

Alıntı yapılan: iyildirim - 14 Şubat 2013, 18:03:57
Fikir vermesi açısından; Algoritmaya göre daha uzun yol kateden eksende fonksiyon her çağrıldığında hareket olur diye düşünürsek yapabileceğin;
ya önceden hesaplayıp her adımı bufferlamak ve uzun yol kateden eksene göre zamanını ayarladığın bir timer kesme ile bufferı pinlere aktarmak,
yada  hareket anında hesaplamak ve gerekli gecikmeleri de hesaplayıp beklemek..
Demiştim.
Uzun yol kateden eksende putMotor her çağrıldığında hareket olacaksa, putMotor içinde en başta TMR registerinin istediğin değere gelmesini bekleyip sonra ne yapmak istiyorsan  yapabilirsin. Tabii birde TMR registerini sıfırlayacaksın.

void putMotor(SL x, SL y, SL z )
{

    while(TMR1 < delayTime);
    TMR1 = 0;
 /* minPeriod , hız parametresine göre en başta hesapladığın master eksenin hızı..periodu 1 / STEP_sn den hesaplanacak */
    if (delayTime > minPeriod)             
        delayTime -= hızlama_miktarı        
    

.......

gibi. Burada minPeriod max hıza göre timer periodu, delayTime ise o anki hızına göre period. delayTime başta büyük bir değere set edilip azaltılırsa basit bir hızlanma işlevi de yapmış olursun.   Diğer bekleme fonksiyonlarını  iptal edip bu şekilde bir dene derim.

DIR değişkenlerini komutu aldığında set edersen putMotor içinde her seferinde hesaplamak zorunda kalmazsın. Bu şekilde hareket etmeyen motorda bir yönde DIR pininde STEP ile uyumlu sürekli bir darbe olacak.

Titreme demişsin. Bu ileri geri bir titrememi, ilerle dur gibi titrememi anlamak, pozisyon hatası oluyormu ona bakmak lazım. Sürücü olarak ne kullanıyorsun.?

-------------------------------------------
edit: while(TMR1 >= delayTime);

computerboy

titreme usb kesmesinden kaynaklanıyormuş hocam usb devre dışı kalınca sorun kalmıyor. birde sizin dediğiniz şekilde kodları düzenlemeye çalıştım ama tam anlayamadım bi inceleseniz çok öğreneceğim var sizden ayrıca DIR ayarlarınıda bresenham koda gömdüm.

void putMotor(SL x, SL y, SL z )
{


if(x-oldX==1 || x-oldX==-1) STP_X = 0;
while(TMR1 < x_h); TMR1 = 0;
if(x_h>x_h_s) x_h-=x_h;

if(y-oldY==1 || y-oldY==-1) STP_Y = 0; 
while(TMR1 < y_h); TMR1 = 0;                 // TMR1  usb kesmesini sayıyor.
if(y_h>y_h_s) y_h-=y_h;

if(z-oldZ==1 || z-oldZ==-1) STP_Z = 0; 
while(TMR1 < z_h); TMR1 = 0;
if(z_h>z_h_s) z_h-=z_h;

STP_X =1;
while(TMR1 < x_h); TMR1 = 0;
if(x_h>x_h_s) x_h-=x_h;

STP_Y =1;
while(TMR1 < y_h); TMR1 = 0;
if(y_h>y_h_s) y_h-=y_h;

STP_Z =1;  
while(TMR1 < z_h); TMR1 = 0;
if(z_h>z_h_s) z_h-=z_h;
         				
stepcount=stepcount+1;

    oldY = y;
    oldX = x;
    oldZ = z;

}