نحيا السداسي. الجزء الثاني

فيديو سداسي الأرجل متحرك


مقارنة المنشور السابق، لها سلف تبين أن أكثر إثارة، وذلك بفضل عدد كبير من الصور. أود ملء الفراغ في هذه المسألة وتقديم بعض مقاطع الفيديو التي تلتقط اختبار قيادة صغيرًا للروبوت حول الشقة.


يتم التحكم في الروبوت من قبل المشغل من الهاتف عبر Wi-Fi. ولكن عند مشاهدة مقطعي الفيديو الأولين ، قد تحصل على انطباع خاطئ بأن سداسي الأرجل لديه أساسيات الذكاء. للأسف، هذا ليس كذلك ...


علاوة على ذلك ، تم طرد الروبوت من السجادة الناعمة إلى مشمع زلق إلى حد ما ، مما أظهر نتيجة جيدة.


تنفيذ حركات سداسي الأرجل


كما ذكر في المقالة السابقة ، فإن فئة MotionJob والفئات المشتقة الموروثة من فئة الحركة هي المسؤولة عن تنفيذ حركات الروبوت .

فئة MotionJob لديها طريقة تنفيذ عامة واحدة فقط ، والتي تطلق حركة معينة للتنفيذ.

int MotionJob :: تنفيذ (Motion * pMotion)
int MotionJob::execute(Motion* pMotion) {
  if(m_Status == NONE && pMotion != NULL) {
     
     m_pMotion = pMotion;
     m_MotionTime = 0;
     m_TotalTime = m_pMotion->totalTime();
     m_Status = RUNING;
  } else if(pMotion != NULL){
    
    if(m_pMotion->m_pNext != NULL) 
      delete m_pMotion->m_pNext;
      
    m_pMotion->m_pNext = pMotion;
     m_Status = STOPING;
  }
  return S_OK;
}


منطقها بسيط جدا. كمعلمة ، يأخذ المؤشر إلى كائن يصف عملية الحركة. إذا لم تكن هناك حركة أخرى في التنفيذ في الوقت الحالي ، تصبح الحركة المشار إليها نشطة ، وتدخل في حالة التنفيذ ( RUNING ). في حالة وجود بعض الحركة بالفعل في التنفيذ ، فإننا نترجمها إلى حالة توقف ( STOPING ) ، ويتم تسجيل الحركة الجديدة على أنها التالية في حقل m_pNext .

هام: من المتفق عليه أن جميع الكائنات الموروثة من الحركة تعني الإنشاء في منطقة الذاكرة الديناميكية (عبر جديد ) ويتم تحريرها بواسطة فئة MotionJob بعد اكتمال الحركة أو إلغاؤها.

تطبق فئة MotionJob ، الموروثة من AJob ، كل سحر الحركة في طريقة onRun المتداخلة ، والتي يتم استدعاؤها بتردد معين أثناء تشغيل الروبوت.

باطل MotionJob :: onRun ()
void MotionJob::onRun() {

  Vector3D points[FOOTS_COUNT];
  int pose[FOOTS_COUNT][3];
  //    ,  
  if(m_pMotion == NULL)
    return;
  //           ,
  //            .
  //      .
  int res;
  if(m_pMotion->getPose(min(m_MotionTime, m_pMotion->maxTime()), pose) == S_OK) {
     res = m_pGeksapod->_setPose(pose);
  } else if(m_pMotion->getPose(min(m_MotionTime, m_pMotion->maxTime()), points) == S_OK) {
     res = m_pGeksapod->_setPose(points);
  }
  //     
  m_MotionTime += MOTION_JOB_PERIOD;
  //     ,    ,
  //      :
  //   m_TotalTime -     
  if(m_TotalTime > 0) { 
    m_TotalTime -= MOTION_JOB_PERIOD;
    m_TotalTime = max(0,m_TotalTime); 
  }
  //      , 
  //      
  if(m_TotalTime == 0 && m_pMotion->isLooped()) {
     m_Status = STOPING;
  }
  //         
  //          .
  if( (m_MotionTime - m_pMotion->maxTime() >= MOTION_JOB_PERIOD && (!m_pMotion->isLooped() || m_Status == STOPING)) ) { //  
    //      
    Motion* pNextMotion = m_pMotion->m_pNext;
    m_pMotion->m_pNext = NULL;
    delete m_pMotion;
    m_pMotion = NULL;
    //   
    m_Status = NONE;
    execute(pNextMotion);
  } else if(m_MotionTime - m_pMotion->maxTime() >= MOTION_JOB_PERIOD) {
    //      
    //         
    m_MotionTime = 0; 
  }
}


يحتوي الرمز أعلاه على تعليقات كافية حول عمله ، وبالتالي ، كإضافة ، ألاحظ بضع نقاط رئيسية فقط:
  • Motion (Vector3D points[FOOTS_COUNT]), (int pose[FOOTS_COUNT][3]). , . getPose , getPose. _setPose, .
  • يمكن أن تكون الحركات منتظمة أو دورية. سيتم إكمال الحركة الطبيعية عند الوصول إلى نهاية الفترة الزمنية المخصصة لتنفيذ دورة حركة واحدة (يتم إرجاعها بواسطة طريقة Motion :: maxTime () ).
    سيتم إكمال الحركات الدورية فقط بعد التوقف القسري الذي يحدث عندما يتم استدعاء طريقة التنفيذ (يمكن استدعاؤها بمؤشر فارغ) ، أو عندما يتم تخصيص نقطة موجبة في الوقت لتنفيذ الحركة (يتم إرجاعها بواسطة طريقة Motion :: totalTime () ).

يتم تعريف الفئات التالية لوصف خيارات الحركة المختلفة:

فئة TransPoseMotion
class TransPoseMotion: public Motion {
  Vector3D m_Pose0[FOOTS_COUNT];  //  
  Vector3D m_Offset[FOOTS_COUNT];  //  
public:  
  TransPoseMotion(Vector3D begPose[FOOTS_COUNT], Vector3D endPose[FOOTS_COUNT],
        long maxTime, Motion* pNext = NULL);
  int getPose(long time, Vector3D points[FOOTS_COUNT]);
};

الطبقة TransAnglesMotion
class TransAnglesMotion: public Motion {
  char m_Pose0[FOOTS_COUNT][3];
  char m_Offset[FOOTS_COUNT][3];
public:  
  TransAnglesMotion(int begPose[FOOTS_COUNT][3], int endPose[FOOTS_COUNT][3],
        long maxTime, Motion* pNext = NULL);
  int getPose(long actionTime, int pose[FOOTS_COUNT][3]);
};

فئة LinearMotion
class LinearMotion: public Motion {
  //     ( ) 
  Vector3D m_Pose0[FOOTS_COUNT];
  Vector3D m_Offset; //     
  int m_Heigth;          //   
public:  
  LinearMotion(int heigth, Vector3D pose0[FOOTS_COUNT], Vector3D speed, 
        long maxTime, long totalTime = -1, Motion* pNext = NULL);
  int getPose(long time, Vector3D points[FOOTS_COUNT]);
};

فئة RotateMotion
class RotateMotion: public Motion {
  //     ( ) 
  Vector3D m_Pose0[FOOTS_COUNT]; 
  Vector3D m_Angles; //     
  int m_Heigth;           //   
public:  
  RotateMotion(int heigth, Vector3D pose0[FOOTS_COUNT], Vector3D rotor, 
        long maxTime, long totalTime = -1, Motion* pNext = NULL);
  int getPose(long time, Vector3D points[FOOTS_COUNT]);
};

فئة TransPoseMotion


تسمح لك فئة TransPoseMotion بنقل الأطراف من موضع إلى آخر. عند إنشاء كائن ، يتم نقل إحداثيات النقاط والفترة الزمنية التي يجب تنفيذ الحركة بعدها إلى مُنشئ الصف.

TransPoseMotion :: TransPoseMotion (...)
TransPoseMotion::TransPoseMotion(Vector3D begPose[FOOTS_COUNT],
  Vector3D endPose[FOOTS_COUNT], long maxTime, Motion* pNext) : Motion(maxTime, -1, pNext) {
  for(int i = 0; i < FOOTS_COUNT; i++) {
    m_Pose0[i] = begPose[i];
    m_Offset[i] = endPose[i] - begPose[i];
  }
}


بمعرفة الموضع الأولي ومتجه الإزاحة لكل طرف ، يمكننا بسهولة الحصول على الموضع الجديد للأقدام في طريقة getPose في الفترة الزمنية من 0 إلى maxTime من خلال إجراء حسابات خطية بسيطة.

int TransPoseMotion :: getPose (...)
int TransPoseMotion::getPose(long time, Vector3D points[FOOTS_COUNT]) {
  for(int i = 0; i < FOOTS_COUNT; i++)
    linearFunc(1.0, m_Offset[i], m_Pose0[i], 1.0*time/m_MaxTime, points[i]);
}


يتم تعيين الحسابات الخطية للدالة المساعدة linearFunc ، وهي معادلة خطية عادية مع معلمات محددة من خلال وسيطات الدالة.

الخطية الفراغ (...]
void linearFunc(float a, Vector3D offset, Vector3D p0, float k,  Vector3D& res) {
  res = a*offset*k + p0;
}


فئة TransAnglesMotion


منطق هذه الفئة يشبه المنطق السابق. والفرق الوحيد هو أنه في فئة TransAnglesMotion ، يتم تعيين مواضع الأطراف السداسية عن طريق مجموعة الزوايا التي يجب أن تنحني فيها مفاصلها ، وليس عن طريق الإحداثيات المكانية لأطراف الكفوف الروبوتية.

TransAnglesMotion :: TransAnglesMotion (...]
TransAnglesMotion::TransAnglesMotion(int begPose[FOOTS_COUNT][3],
  int endPose[FOOTS_COUNT][3], long maxTime, Motion* pNext) : Motion(maxTime, -1, pNext) {
  for(int i = 0; i < FOOTS_COUNT; i++) {
    for(int j = 0; j < 3; j++) {
      m_Pose0[i][j] = begPose[i][j];
      m_Offset[i][j] = endPose[i][j] - begPose[i][j];
    }
  }
}



int TransAnglesMotion :: getPose (...)
int TransAnglesMotion::getPose(long time, int pose[FOOTS_COUNT][3]) {
  for(int i = 0; i < FOOTS_COUNT; i++) {
    for(int j = 0; j < 3; j++) 
      pose[i][j] = m_Pose0[i][j] + (int)(1.0*m_Offset[i][j]*time/m_MaxTime);
  }
  return S_OK;
}


فئة LinearMotion


يتطلب تنفيذ الحركة المباشرة المزيد من التعليمات البرمجية واهتمامك. يحتوي تصميم سداسي الأرجل على عدد مثالي من الأطراف ، مما يسمح لك دائمًا بالحصول على ثلاث نقاط دعم عند الحركة ، بحيث يقع إسقاط مركز كتلة الروبوت دائمًا داخل المثلث الذي تشكله. وهذا يضمن وضعًا مستقرًا للروبوت عند الحركة. لذلك ، يمكن اعتبار الحركة الروبوتية المستقيمة كمجموعة من الخطوات المتطابقة. في كل خطوة من هذه الخطوات ، يمكن تمييز مرحلتين. في المرحلة الأولى ، يلامس ثلاثة أرجل السطح وتتحرك أطرافه بالتوازي مع السطح في الاتجاه المعاكس لاتجاه الحركة. في هذه الحالة ، يتحرك ثالوث الكفوف الثاني ، دون لمس السطح ، على طول مسار منحني في اتجاه حركة الروبوت.عند نقطة معينة من الزمن ، يصلون إلى موقعهم المتطرف ، ويبدأ الثلاثي الثاني في الاتصال بالسطح. في المرحلة الثانية ، تتكرر عملية الحركة ، هذه المرة فقط يقوم الثلاثي الثاني بتحريك الروبوت ، ويعود الأول إلى نقطة البداية.



تم العثور على هذه الرسوم المتحركة أثناء تحضير مقال على الإنترنت على هذه الصفحة ، ولكن بعد مراجعتها ، كنت مقتنعا بأن مصدرها الأصلي كان هابر ، وأن مؤلف المنشور كان conKORD المحترم .

عندما تدرك الحركة في خط مستقيم، نحن بحاجة إلى تعيين ارتفاع الساقين من السطح ( heigth )، موقف أطراف الروبوت في البداية (وعند نهاية) من حركة ( pose0 )، وناقل السرعة ( السرعة )، والوقت الذي يستغرقه كاملة خطوة واحدة ( maxTime )، ومتى يجب على الروبوت أداء الحركة ( totalTime ). إذا قمت بتحديد قيمة سالبة لـ totalTime، ستستمر الحركة إلى أجل غير مسمى حتى تنفد شحنة الروبوت أو تكتمل الحركة بالقوة.

LinearMotion :: LinearMotion (...)
LinearMotion::LinearMotion(int heigth, Vector3D pose0[FOOTS_COUNT], Vector3D speed,
  long maxTime, long totalTime, Motion* pNext = NULL) 
    : Motion(maxTime, true, totalTime, pNext) {
  copyPose(m_Pose0, pose0);
  m_Offset = 1.0*speed*maxTime/1000;
  m_Heigth = heigth;
}

على الرغم من حقيقة أن طريقة getPose تحتوي على كمية رائعة من التعليمات البرمجية ، إلا أنها بسيطة للغاية.

int LinearMotion :: getPose (...)
int LinearMotion::getPose(long time, Vector3D points[FOOTS_COUNT]) {
  double k = 1.0*time/m_MaxTime;

  if(k < 0.25) {  //   
    linearFunc(2.0, -m_Offset, m_Pose0[LEFT_FRONT_FOOT_IDX], k,
      points[LEFT_FRONT_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[LEFT_BACK_FOOT_IDX], k,
      points[LEFT_BACK_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_MIDLE_FOOT_IDX], k,
      points[RIGTH_MIDLE_FOOT_IDX]);

    linearFunc(2.0, m_Offset, m_Pose0[LEFT_MIDLE_FOOT_IDX], k,
      points[LEFT_MIDLE_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[RIGTH_FRONT_FOOT_IDX], k,
      points[RIGTH_FRONT_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[RIGTH_BACK_FOOT_IDX], k,
      points[RIGTH_BACK_FOOT_IDX]);
  
    int h = (int)m_Heigth*cos(M_PI*k*2);
    points[LEFT_MIDLE_FOOT_IDX].z += h;
    points[RIGTH_FRONT_FOOT_IDX].z += h;
    points[RIGTH_BACK_FOOT_IDX].z += h;

  } else if(k < 0.5) { //   
    
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_FRONT_FOOT_IDX], k - 0.5,
      points[LEFT_FRONT_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_BACK_FOOT_IDX], k - 0.5,
      points[LEFT_BACK_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[RIGTH_MIDLE_FOOT_IDX], k - 0.5,
      points[RIGTH_MIDLE_FOOT_IDX]);

    linearFunc(2.0, -m_Offset, m_Pose0[LEFT_MIDLE_FOOT_IDX], k - 0.5,
      points[LEFT_MIDLE_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_FRONT_FOOT_IDX], k - 0.5,
      points[RIGTH_FRONT_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_BACK_FOOT_IDX], k - 0.5,
      points[RIGTH_BACK_FOOT_IDX]);

    int h = (int)m_Heigth*sin(M_PI*(k - 0.25)*2);
    points[LEFT_FRONT_FOOT_IDX].z += h;
    points[LEFT_BACK_FOOT_IDX].z += h;
    points[RIGTH_MIDLE_FOOT_IDX].z += h;

  } else if(k < 0.75) { //   
    
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_FRONT_FOOT_IDX], k - 0.5,
      points[LEFT_FRONT_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_BACK_FOOT_IDX], k - 0.5,
      points[LEFT_BACK_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[RIGTH_MIDLE_FOOT_IDX], k - 0.5,
      points[RIGTH_MIDLE_FOOT_IDX]);

    linearFunc(2.0, -m_Offset, m_Pose0[LEFT_MIDLE_FOOT_IDX], k - 0.5,
      points[LEFT_MIDLE_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_FRONT_FOOT_IDX], k - 0.5,
      points[RIGTH_FRONT_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_BACK_FOOT_IDX], k - 0.5,
      points[RIGTH_BACK_FOOT_IDX]);
    
    int h = (int)m_Heigth*cos(M_PI*(k - 0.5)*2);
    points[LEFT_FRONT_FOOT_IDX].z += h;
    points[LEFT_BACK_FOOT_IDX].z += h;
    points[RIGTH_MIDLE_FOOT_IDX].z += h;
    
  } else {  //   
    
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_FRONT_FOOT_IDX], 1 - k,
      points[LEFT_FRONT_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[LEFT_BACK_FOOT_IDX], 1 - k,
      points[LEFT_BACK_FOOT_IDX]);
    linearFunc(2.0, m_Offset, m_Pose0[RIGTH_MIDLE_FOOT_IDX], 1 - k,
      points[RIGTH_MIDLE_FOOT_IDX]);

    linearFunc(2.0, -m_Offset, m_Pose0[LEFT_MIDLE_FOOT_IDX], 1 - k,
      points[LEFT_MIDLE_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_FRONT_FOOT_IDX], 1 - k,
      points[RIGTH_FRONT_FOOT_IDX]);
    linearFunc(2.0, -m_Offset, m_Pose0[RIGTH_BACK_FOOT_IDX], 1 - k,
      points[RIGTH_BACK_FOOT_IDX]);
    
    int h = (int)m_Heigth*sin(M_PI*(k - 0.75)*2);
    points[LEFT_MIDLE_FOOT_IDX].z += h;
    points[RIGTH_FRONT_FOOT_IDX].z += h;
    points[RIGTH_BACK_FOOT_IDX].z += h;
  }

  return S_OK;
}


نقسم الخطوة بأكملها إلى أربعة أرباع:

  1. في الربع الأول ، يقوم الثلاثي الأول من الأطراف (الأمامي الأيسر ، واليسار الخلفي والوسط الأيمن) بحركة دفع ، أي أنها تتحرك على طول السطح في الاتجاه المعاكس للحركة. يتحرك الثالوث الثاني من الكفوف (الأمامي الأيمن والظهر الأيمن والوسط الأيسر) في اتجاه الحركة ، بينما يختلف ارتفاع ارتفاعها من القيمة القصوى للارتفاع المحدد للساقين (في بداية الربع) إلى الصفر (في نهاية الربع) كدالة للجيب.
  2. في الربع الثاني ، تتغير اتجاهات الثلاثيات. الآن يقوم الثلاثي الثاني من الكفوف بتحريك الروبوت ، ويتحرك الأول في نفس اتجاه الروبوت. في هذه الحالة ، يتغير ارتفاع الكفوف للثالوث الأول في الجيب الجيبي من صفر إلى القيمة القصوى.
  3. في الربع الثالث ، يتم الحفاظ على اتجاه حركة الكفوف ، وتهبط نهايات الثالوث الأول ، لتصل إلى السطح في نهاية الربع الثالث.
  4. في الربع الأخير ، يصبح الثلاثي الأول هو الدعم ، والثاني يغير الاتجاه ويتحرك صعودًا على طول الجيوب الأنفية ، ليصل إلى أقصى ارتفاع في نهاية الربع.

فئة RotateMotion


بعد فهم منطق فئة LinearMotion ، يمكننا بسهولة معرفة تطبيق فئة RotateMotion .

RotateMotion :: RotateMotion (...)
RotateMotion::RotateMotion(int heigth, Vector3D pose0[FOOTS_COUNT], Vector3D speed,
  long maxTime, long totalTime = -1, Motion* pNext = NULL) 
    : Motion(maxTime, true, totalTime, pNext) {
  copyPose(m_Pose0, pose0);
  m_Angles = 0.001*maxTime*speed*M_PI/180;
  m_Heigth = heigth;
}


يكاد يكون منشئو كلا الطبقتين متطابقين في تكوين الحجج التي تم تمريرها. فقط في حالة عكس السرعة ، يتم تحديد ناقل السرعة الزاوية لمحور وسرعة دوران الروبوت.

في طريقة getPose لفئة RotateMotion ، ينقسم منطق العمل أيضًا إلى أربع مراحل. ومع ذلك ، لحساب موضع الكفوف ، بدلاً من الدالة الخطية linearFunc ، يتم استخدام rotFunc للدالة الشعاعية .

int RotateMotion :: getPose (...)
int RotateMotion::getPose(long time, Vector3D points[FOOTS_COUNT]) {
  double k = 1.0*time/m_MaxTime;
  if(k < 0.25) {  //   

    rotFunc(2.0, m_Angles, m_Pose0[LEFT_FRONT_FOOT_IDX], k,
      points[LEFT_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_BACK_FOOT_IDX], k,
      points[LEFT_BACK_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_MIDLE_FOOT_IDX], k,
      points[RIGTH_MIDLE_FOOT_IDX]);
      
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_MIDLE_FOOT_IDX], -k,
      points[LEFT_MIDLE_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_FRONT_FOOT_IDX], -k,
      points[RIGTH_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_BACK_FOOT_IDX], -k,
      points[RIGTH_BACK_FOOT_IDX]);

    int h = (int)m_Heigth*cos(M_PI*k*2);
    points[LEFT_MIDLE_FOOT_IDX].z += h;
    points[RIGTH_FRONT_FOOT_IDX].z += h;
    points[RIGTH_BACK_FOOT_IDX].z += h;

  } else if(k < 0.5) {  //   
    
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_FRONT_FOOT_IDX], 0.5 - k,
      points[LEFT_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_BACK_FOOT_IDX], 0.5 - k,
      points[LEFT_BACK_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_MIDLE_FOOT_IDX], 0.5 - k,
      points[RIGTH_MIDLE_FOOT_IDX]);
      
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_MIDLE_FOOT_IDX], k - 0.5,
      points[LEFT_MIDLE_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_FRONT_FOOT_IDX], k - 0.5,
      points[RIGTH_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_BACK_FOOT_IDX], k - 0.5,
      points[RIGTH_BACK_FOOT_IDX]);
  
    int h = (int)m_Heigth*sin(M_PI*(k - 0.25)*2);
    points[LEFT_FRONT_FOOT_IDX].z += h;
    points[LEFT_BACK_FOOT_IDX].z += h;
    points[RIGTH_MIDLE_FOOT_IDX].z += h;

  } else if(k < 0.75) {  //   
  
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_FRONT_FOOT_IDX], 0.5 - k,
      points[LEFT_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_BACK_FOOT_IDX], 0.5 - k,
      points[LEFT_BACK_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_MIDLE_FOOT_IDX], 0.5 - k,
      points[RIGTH_MIDLE_FOOT_IDX]);
      
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_MIDLE_FOOT_IDX], k - 0.5,
      points[LEFT_MIDLE_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_FRONT_FOOT_IDX], k - 0.5,
      points[RIGTH_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_BACK_FOOT_IDX], k - 0.5,
      points[RIGTH_BACK_FOOT_IDX]);
  
    int h = (int)m_Heigth*cos(M_PI*(k - 0.5)*2);
    points[LEFT_FRONT_FOOT_IDX].z += h;
    points[LEFT_BACK_FOOT_IDX].z += h;
    points[RIGTH_MIDLE_FOOT_IDX].z += h;
  } else { //   
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_FRONT_FOOT_IDX], k - 1,
      points[LEFT_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_BACK_FOOT_IDX], k - 1,
      points[LEFT_BACK_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_MIDLE_FOOT_IDX], k - 1,
      points[RIGTH_MIDLE_FOOT_IDX]);
      
    rotFunc(2.0, m_Angles, m_Pose0[LEFT_MIDLE_FOOT_IDX], 1 - k,
      points[LEFT_MIDLE_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_FRONT_FOOT_IDX], 1 - k,
      points[RIGTH_FRONT_FOOT_IDX]);
    rotFunc(2.0, m_Angles, m_Pose0[RIGTH_BACK_FOOT_IDX], 1 - k,
      points[RIGTH_BACK_FOOT_IDX]);

    int h = (int)m_Heigth*sin(M_PI*(k - 0.75)*2);
    points[LEFT_MIDLE_FOOT_IDX].z += h;
    points[RIGTH_FRONT_FOOT_IDX].z += h;
    points[RIGTH_BACK_FOOT_IDX].z += h;
  }
  return S_OK;
}


تسمح لك الوظيفة الشعاعية بحساب موضع نقطة في الفضاء بعد دورانها حول مركز الروبوت ، والذي يتزامن مع الأصل.

rotFunc باطلة (...)
void rotFunc(float a, Vector3D angles, Vector3D p0, float k, Vector3D& res) {
  Matrix3D m = rotMatrix2(a*angles*k);
  res = m*p0;
}


داخل الوظيفة ، يتم تضمين جهاز رياضي ، مما يسمح لنا بالعمل بكميات المتجه والمصفوفة.

هذا كل شيء في الوقت الراهن. لقد قمنا بتحليل جزء أساسي من كود البرنامج المسؤول عن تنفيذ حركة الروبوت. إذا كان لديك أي أسئلة أو تعليقات ، يسعدني أن أجيب عليها في التعليقات. سأحاول تخصيص المقالة التالية لبقية الشفرة ، والتي سيكون رمز المصدر الخاص بها متاحًا للتنزيل.
شكرا للانتباه!

يتبع…

All Articles