פרוייקט ארדו-רכב, שלב ג’ - שליטה מרחוק

    שלב (כמעט) אחרון בפרוייקט, הרובוט מקבל שליטה מרחוק!

ראשית חיברתי את רכיב הבלותות לרובוט ווידאתי שהוא עובד. האמת שזה היה די פשוט, עבדתי לפי התרשים המצורף וזה הלך כמעט חלק, החלק היחיד שלקח לי שעתיים לגלות הוא שהמודול הזה מקונפג לקצב תקשורת ( Baud rate ) של 115200, ולא 9600 כפי שניסיתי בהתחלה.

 

לאחר שוידאתי שהתקשורת עובדת, ישבתי עם גיסי, ליאור בקר, להכין את האפליקציה לאנדרואיד (יותר נכון הוא כתב את האפליקציה, אני שיגעתי אותו בשינויים כל הזמן). 

כזכור, הרובוט מצפה ל- 3 פרמטרים ע"מ לנוע: מהירות נסיעה, כיוון נסיעה וזוית סיבוב ואת הפרמטרים הללו צריך עכשיו לספק לו דרך הבלותות מהסמארטפון:

  1. מהירות הנסיעה נקבעה על סקאלה של 0-100. כיוון שבייט אחד יכול לתת לנו טווח של 0-255, קבענו ש 1-100 מייצג מהירות נסיעה קדימה ו- 101-200 מייצג מהירות נסיעה אחורה. כך למעשה איחדנו את מהירות הנסיעה והכיוון לבייט אחד.
  2. זוית סיבוב קצת יותר סיבך אותנו, כיוון שהסקאלה שקבענו היתה 0-360 מעלות, שזה מעט יותר ממה שבייט אחד יכול לייצג. לכן פיצלנו את הפרמטר הזה ל- 2 בתים: זוית סיבוב 0-180 מעלות וכיוון סיבוב שמיוצג ע"י 0 או 1.

בשורה התחתונה, כל פקודה לרובוט מורכבת משלושה בתים כפי שהסברתי לעיל. הרובוט "מאזין" לתקשורת וכל עוד הוא מקבל פקודות, הוא מבצען באופן מיידי. אם יש הפסקה בקבלת פקודות, הוא ממתין כ- 200ms ואם אין עוד תקשורת הוא עוצר וממתין לחידוש התקשורת. בפועל, הפקודות נשלחות מהסמארטפון בפרקי זמן מאוד קצרים (חלקי מילישניה) ות'כלס זה די מיותר ומנצל יותר מידי רוחב פס, אבל זה מימוש הרבה יותר פשוט מאשר ליצור תקשורת מתוזמנת ומסונכרנת (לדוגמא, שליחה/קבלה של פקודות כל X מילישניות).

  הנה הקוד... הוא די דומה לחלק הקודם מלבד התוספת עבור התקשורת.  

//Configure these to fit your design...
#define out_STBY 10
#define out_A_PWM 3
#define out_A_IN2 8
#define out_A_IN1 9
#define out_B_IN1 12
#define out_B_IN2 13
#define out_B_PWM 5
#define motor_A 0
#define motor_B 1
#define motor_AB 2
 
int direction = 0;
 
void setup()
{
  pinMode(out_STBY,OUTPUT);
  pinMode(out_A_PWM,OUTPUT);
  pinMode(out_A_IN1,OUTPUT);
  pinMode(out_A_IN2,OUTPUT);
  pinMode(out_B_PWM,OUTPUT);
  pinMode(out_B_IN1,OUTPUT);
  pinMode(out_B_IN2,OUTPUT);
 
  motor_standby(false);
  Serial.begin(115200);
}
 
void loop()
{
 
 
  if ( Serial.available() ){
   byte vel = Serial.read();
   byte ang = Serial.read();
   byte ang_direction= Serial.read();
 
   int velocity = int(vel) ;
   if (vel > 0 and vel <= 100){
     velocity = -velocity;
   }else if (vel > 100){
     vel -= 100;
     vel = min(100,vel);
 
   }
   int angle = int(ang);
   if (ang_direction == 1){
     angle = -angle; 
   }
 
   drive(velocity , angle); 
   Serial.println(velocity);
   Serial.println(angle);
 
  }else{
 
   delay(200);
   if ( ! Serial.available() ){
    motor_coast(motor_A);
     motor_coast(motor_B); 
   }
  }
 
 
}
 
void drive(int speed, int direction){    
   int motor_A_speed = direction_to_speed(motor_A,speed,direction);
   int motor_B_speed = direction_to_speed(motor_B,speed,direction);
 
 
   motor_move(motor_A,motor_A_speed);
   motor_move(motor_B,motor_B_speed);
}
 
int direction_to_speed(boolean motor, int speed, int direction){
  direction = normalize_direction(direction);
  int newspeed = speed;
  if (motor==motor_A){
    if ( direction > 180 ){
      newspeed = int(speed * cos(DEG_TO_RAD*direction));
    }
  }else if (motor==motor_B){
    if ( direction > 0 && direction <= 180){
      newspeed = int(speed * cos(DEG_TO_RAD*direction));
    }
  } 
  return newspeed;
}
int normalize_direction(int direction){
  int normal = direction % 360;
  if (normal < 0){
    normal = 360 + normal;
  } 
  return normal;
}
 
void motor_move(boolean motor, int speed) { //speed from -100 to 100
  byte PWMvalue=0;
  int fix_speed = min(abs(speed),100);
  if (fix_speed>0){
    PWMvalue = map(fix_speed,1,100,50,255); //anything below 50 is very weak
  }
  if (speed > 0)
    motor_speed(motor,0,PWMvalue);
  else if (speed < 0)
    motor_speed(motor,1,PWMvalue);
  else {
    motor_coast(motor);
  }
}
void motor_speed(boolean motor, boolean direction, byte speed) { //speed from 0 to 255
  if (motor == motor_A) {
    if (direction == 0) {
      digitalWrite(out_A_IN1,HIGH);
      digitalWrite(out_A_IN2,LOW);
    } else {
      digitalWrite(out_A_IN1,LOW);
      digitalWrite(out_A_IN2,HIGH);
    }
    analogWrite(out_A_PWM,speed);
  } else {
    if (direction == 0) {
      digitalWrite(out_B_IN1,HIGH);
      digitalWrite(out_B_IN2,LOW);
    } else {
      digitalWrite(out_B_IN1,LOW);
      digitalWrite(out_B_IN2,HIGH);
    }
    analogWrite(out_B_PWM,speed);
  }
}
void motor_standby(boolean state) { //low power mode
  if (state == true)
    digitalWrite(out_STBY,LOW);
  else
    digitalWrite(out_STBY,HIGH);
}
void motor_brake(boolean motor) {
  if (motor == motor_A) {
    digitalWrite(out_A_IN1,HIGH);
    digitalWrite(out_A_IN2,HIGH);
  } else {
    digitalWrite(out_B_IN1,HIGH);
    digitalWrite(out_B_IN2,HIGH);
  }
}
void motor_coast(boolean motor) {
  if (motor == motor_A) {
    digitalWrite(out_A_IN1,LOW);
    digitalWrite(out_A_IN2,LOW);
    digitalWrite(out_A_PWM,HIGH);
  } else {
    digitalWrite(out_B_IN1,LOW);
    digitalWrite(out_B_IN2,LOW);
    digitalWrite(out_B_PWM,HIGH);
  }
} 

וכמו שאפשר לראות, זה גם עובד. 

אז מה הלאה? חשבתי על כמה אפשרויות:

  1. שיפור הממשק לתמיכה בסיבוב תו"כ תנועה (זה כבר די עובד, בקרוב הסרטון)
  2. בצורה בנוכחית הרובוט מאוד לא יעיל ומתקשה בתנועה. הגלגלים הנוכחיים צרים מידי ולא מספיק משקל מופעל עליהם. כמו כן, בגלל שהרובוט "ארוך" והגלגל הקדמי רחוק מהאחוריים, צריך להפעיל הרבה כח ע"מ להשיג סיבוב על ציר בודד. אני כבר מתכנן לבנות מחדש את הרובוט על הפלטפורמה הזו, עם גלגלים רחבים יותר ומרחק קטן יותר בין הגלגל הקדמי לאחוריים.
  3. הוספת מצלמה (זה הולך להיות מעניין)
  4. אפשרות של נסיעה עצמית בעזרת סנסורים של מרחק.

אשמח לעוד רעיונות ותגובות!

עדכון: וידאו חדש עם הממשק החדש, פי אלף יותר מגניב!

Category: