การขับ Stepper Motor 2 phases ให้เรียบ ไม่สะเทือนด้วยโปรแกรม

ในกระทู้นี้เรียกว่ามาแลกเปลี่ยนแนวความคิดเรื่องการขับ Stepper Motor แบบ Microstep ด้วยโปรแกรมกันดีกว่าครับ เผื่อใครจะมีความคิดอะไรใหม่ๆ มาแลกเปลี่ยนกัน สำหรับคนที่พื้นฐานการเขียนโปรแกรมและเข้าใจ Stepper Motor อยู่บ้างแล้วครับ
โดยปกติอย่าที่ทราบเวลาเราขับ Stepper Motor แบบ 2 phase 4 เส้น โดยมี สาย +A ,-A, +B, -B ตามลำดับ เวลาขับมอเตอร์ก็ต้อง ไล่เป็น +A, +B, -A, -B แบบนี้  ใครไม่เข้าใจก็ไปอ่านวิธีขับมอเตอร์นะครับหรือหลังไมค์ ผมก็พอมีบ้างหากอธิบายได้ก็จะบอก แต่จะไม่อธิบายในกระทู้นี้ครับ
ทีนี้ปัญหาที่ทุกคนจะเจอคือเมื่อหมุนช้าๆเวลามันเคลื่อนที่มันจะไม่เรียบมันจะกระโดด สะเทือนตาม Step เลยครับ ยิ่งช้าก็ยิ่งสั่นมากผมเจอตอนจะทำอุปกรณ์ Slide เอาไว้ถ่ายรูป  วิธีแก้คือแบ่ง Step การเคลื่อนที่ ย่อยๆลงไปเรียก Microstep ในบาง Stepper Driver เช่น DRV8825 จะสามารถทำแบบนี้ได้ แต่กระทู้นี้เราจะสร้างมันเองด้วโปรแกรมตรงๆไปแต่ละ Phase เพื่อทดลองแนวคิดเผื่อใครจะเอาไปประยุกต์ใช้แบบอื่น
ผมเลยมาคิดว่าแทนที่จะขับทีละ phase แต่เปลี่ยนมาขับทีละ 2 phase แต่จ่ายไฟไม่เท่ากันเปลี่ยนไปเรื่อยๆ สนามแม่เหล็กในมอเตอร์ก็จะถูกเหนี่ยวนำไปหา phase ต่อไปเหมือนมีคนสองคนดึงเชือกที่มีบอลตรงกลางฝ่ายหนึ่งค่อยๆปล่อยอีกฝ่ายตค่อยๆดึงบองมันก็จะไปหาคนดึงอย่างช้าๆ การจ่ายไฟ Phase +A และ +B เป็นดังรูป จากเริ่มต้นเฟส +A  มีกำลังไฟ 100% และลดลงตามลำดับ ส่วนเฟส +B ก็เพิ่มขึ้นจาก 0 เป็น 100% เมื่อมอเตอร์เริ่มเคลื่อนไปจากจุด 1 ไป 11


ดังนั้นเราจะต้องโปรแกรมแบ่งการสั่งจ่ายไฟให้ได้ตามนั้น โดยจะจ่ายไปตาม Pin ที่เป็นแบบ PWM ของ Micro Controller และ ตัว Driver ก็ต้องรับด้วยครับเช่น


ผมต่อ Driver +A,-A,+B,-B กับ Arduino Uno Pin 3,5,6,9 ตามลำดับ(เช็คด้วยนะครับว่า Controller มี Digital I/O pin ไหนรอรับบ้าง) และต่อ Switch ระหว่าง Pin 2 กับ GND อีกตัว เอาไว้สลับ Mode ระหว่างขับปกติ กับ Microstep


มาดูโปรแกรมครับ
//**************************************************************
// Programmed by Ruth.Pra
// For public knowledge

long  xdelay = 30; //เวลาหน่วงมอเตอร์ ระหว่าง step ค่ายิ่งน้อยยิ่งเร็ว in millisec
byte StepDiv=25;// การแบ่งจำนวน MicroStep should be  10- 25

byte MotorPin1 = 3; // Step Motor Driver pin 1
byte MotorPin2 = 5; // Step Motor Driver pin 2
byte MotorPin3 = 6; // Step Motor Driver pin 3
byte MotorPin4 = 9; // Step Motor Driver pin 4
byte SW = 2; // Switch สลับ ระหว่าขับธรรมดา กับ Micro Step


byte cphase = 1;//เฟสชองมอเตอร์

void RotateMotorMicro(byte xphase)///ขับทีละ Step แบบแยก Micro Step
{
  switch (xphase)
  {
    case 1:
      MicroDive(MotorPin4, MotorPin1,xdelay,StepDiv);
      break;

    case 2:
      MicroDive(MotorPin1, MotorPin3,xdelay,StepDiv);

      break;
    case 3:
      MicroDive(MotorPin3, MotorPin2,xdelay,StepDiv);

      break;
    case 4:
      MicroDive(MotorPin2, MotorPin4,xdelay,StepDiv);

      break;
  }
}

void MicroDive(byte PinA, byte PinB, long Sdelay, byte MicroStepDiv)
{
  int P = 0;
  int PowerA;
  int PowerB;
  long MicroDelay;
  
  for (int i = 0; i <= MicroStepDiv; i++)
  {
    PowerA = 250 - P;
    PowerB = 0 + P;
    analogWrite(PinA, PowerA);
    analogWrite(PinB, PowerB);
    MicroDelay = Sdelay*1000L/(MicroStepDiv+1);
    delay(MicroDelay/1000);
    delayMicroseconds(MicroDelay00);
    P = P + 10;
  }
analogWrite(PinA, 0);
analogWrite(PinB, 255);
}

void RotateMotorNor(byte xphase)//ขับทีละ Step ปกติ
{

analogWrite(MotorPin1, 0);
analogWrite(MotorPin2,0);
analogWrite(MotorPin3, 0);
analogWrite(MotorPin4, 0);
  switch (xphase)
  {
    case 1:
      analogWrite(MotorPin1, 255);
      delay(xdelay);
      break;

    case 2:
      analogWrite(MotorPin3, 255);
      delay(xdelay);
      break;
    case 3:
      analogWrite(MotorPin2, 255);
      delay(xdelay);
      break;
    case 4:
      analogWrite(MotorPin4, 255);
      delay(xdelay);
      break;
  }
}

void setup()
{
  pinMode(SW,INPUT_PULLUP);
  analogWrite(MotorPin1, 0);
  analogWrite(MotorPin2, 0);
  analogWrite(MotorPin3, 0);
  analogWrite(MotorPin4, 0);
}

void loop()
{
  cphase++;
  if (cphase > 4) cphase = 1;
if (digitalRead(SW))
{
  RotateMotorMicro(cphase);
}
else
{
  RotateMotorNor(cphase);
}
}


//**************************************************************

หมายเหตุ ใน คำสั่ง analogWrite นั้นค่าที่ส่งจะไม่ใช่ 0-100% ตรงๆ เพราะ Arduino จะแบ่งเป็น 0-255 คือ 255 เท่ากับ 100% ส่วนใครใช้ ESP นี่จะยากครับเพราะคำสั่ง AnalogWrite จะไม่ทำงาน แต่ก็มีวิธีเขียนที่ยุ่งยากมากกว่าใครใช้ก็หลังไมค์ละกัน
ในโปรแกรมจะมีตัวแปร 2 ตัวคือ xdelay กับ StepDiv ครับ ตัว xdelay คือตัวบอกว่าจะให้มอเตอร์เคลื่อนแต่ละ Step ใช้เวลานานเท่าไรยิ่งเยอะยิ่งช้า ส่วน StepDiv เอาไว้บอกว่าจะแบ่งMicroStep ของแต่ละ Step เป็นย่อยๆเท่าไร เช่น 25 ก็แบ่งเป็น 25ช่วงย่อย (จริงเป็น 26 เพราะ 0-25)
ตัวโปแกรมจะขับแบบ MicroStep แต่หากกดสวิทช์จะขับแบบธรรมดาครับทดลองแล้วดูความแตกต่างครับ

สรุป
1 หากเวลาน้อยๆต้องการ Motor หมุนเร็วๆ เช่น xdelay น้อยกว่า 5 การขับแบบปกติจะดูราบเรียบกว่า คงเพราะมันพอดีต่อการเคลื่นของแต่ละเฟส ส่วนการขับแบบ Microstep จะถูกแบบเยอะมากในเวลาจำกัดจึงแย่ลง แต่หากเวลา xdelay เพิ่มเยอะมากขึ้นเรื่อยๆจะเห็นความแตกต่างอย่างชัดเจน
2 การแบ่ง StepDiv หากแบ่งน้อยก็เรียบน้อย แต่หากมากเกิน ก็เหมือนว่ามันทำงานไม่ทัน ค่าที่ดูเหมือนจะพอดีคือ 25
3 ค่า xdelay กับ StepDiv นี่ดูเหมือนมันจะต้องพอดีกัน แต่อย่างไรนี่ยังไม่รู้
4 ในการขับแบบ MicroStep การกินไฟลดลงไปครึ่ง แต่แรงบิดก็หายไปด้วย แต่ไม่มากนัก

สุดท้ายคือ อันนี้เป็นการทดลองเล่นๆของผมแชร์เอาไว้เผื่อใครเอาไปร่วมด้วยช่วยพัฒนาต่อ บางจุดบางสาเหตุว่าทำไมมันเป็นเช่นนั้นผมก็ไม่รู้เหมือนกัน  แต่การใช้งานคงต้องเขียนโปรแกรมไว้อีกว่าเมื่อความเร็วเท่าไรจะขับแบบไหนครับ หวังว่ากระทู้นี้คงได้ประโยชน์บ้างนะครับ ใครดูแล้วไร้สาระก็ขออภัยด้วยครับ
แก้ไขข้อความเมื่อ
แสดงความคิดเห็น
โปรดศึกษาและยอมรับนโยบายข้อมูลส่วนบุคคลก่อนเริ่มใช้งาน อ่านเพิ่มเติมได้ที่นี่