悟空机器人驱动4(代码)

20230320

起因

分析了OpenCat  4足机器人的步态,用视觉化的方式展示。

看OpenCat 步态控制源码,写死的步态,只能变换速度,并不能变换轨迹。

因此,觉得有改进的空间。






也许是看了傅里叶变换的视频,有了用三角函数作为周期发生器的思路。用这个作为中心函数驱动和同步各个关节。

https://www.bilibili.com/video/BV1m7411n7Ke


肢体的运动有节律,有周期,有重复,比较适合用统一的中心函数驱动,类似于用一个电机带动一组或多组齿轮。

这个算法在每个loop里读取系统时间,在此基础上计算一个舵机的角度。 因为系统时间相同,即使所有舵机依次分别计算,仍旧能够保持(相位)同步

三角函数作为周期发生器,比三角波函数好,加速度更平滑。

成果

用图解释下面的程序



//  成功的实现了大周期里的小周期。 一条腿在大周期里始终动,另一条腿在大周期里的小周期里才动。     Wave=sin( (PhaseTime -ActionArray[ActionID][4]) / (SmallCycleTime/6.28)); // 在周期内,Wave取值区间:负一到正一 ,作为步态的核心发生器   

//   todo bug 舵机偶尔抽风,然后恢复,抓包分析

//   todo bug 另外在不关机器人电源情况下,更新上传arduino 程序时,舵机大幅摆动

//   todo bug 舵机有吱吱声,貌似不停的来回摆动

//   todo 用print()简单测试每个loop周期耗时。sin()函数等较费时间,

 //  memo 在每个大周期里(2000mS  大周期从 time 取余得来。),一个舵机可以有多个小周期(小周期从大周期截取得来)。所以,void loop() 不应该用 ServoID做循环指示器,应该用ActionID做循环指示器。


// ActionID  ServoID  ServoNtrlPos   TimePosition1          TimePosition2         ActionPhase   ActionTime                  AngleRange

// 动作编号  舵机编号   舵机中立位置   时间轴位置(开始)      时间轴位置(结束)     动作相位      动作从开始到结束的总时间     舵机角度幅度

int ActionArray[7][8] = {

    {0,20,0,0,0,0,0,0},

    {1,5,0x70,0,2000,0,0x30,0x70},  //bug 舵机角度幅度 大于0x76 就开始不正常 ,右髋舵机的表现是从向后踢腿变成向前踢腿。

    {2,6,0x70,0,2000,0,0x20,0x20},

    {3,7,0x70,0,2000,0,0x20,0x20},

    {4,8,0x70,1000,2000,1000,0x20,0x70},

    {5,9,0x70,0,2000,0,0x20,0x20},

   {6,10,0x70,0,2000,0,0x20,0x20} };


//常量===============

int ServoID ;             //每个舵机的编号  ,从1-14

int ActionID=0;             //每个动作的编号  ,从1-32

int ServoNtrlPos[15];       //每个舵机的中立位(将机器人站直,然后读取角度,写入本程序中)

int AngleRange[15];      //每个舵机的运动幅度,取值大,来回运动范围大速度大  也可以用float AngleRange[15]; 也可以用int AngleRange[15];

int AngleRangeFix=0x20;  //舵机的运动范围(固定值),

int ActionTime[15];      //每个舵机的转动时间,取值大,速度慢

int ActionTimeFix=0x20;  //舵机的转动时间(固定值),

int ActionPhase[15];      //每个舵机的运动相位,取值大,时间相位推后。取值范围 与 Time 相关。 如果=1/2 Time 将会反相

//步进 度

//变量=================

//定义 机器人结构,舵机,传感器

//定义 步态,腿 走路速度, 抬腿高度,抬腿距离,臂,头,腰

byte outPutToServo[10];

byte inByte[10];

byte sum;

unsigned long sysTime;                      // 4字节,系统时间,ms。  直到49天回到0ms。 

unsigned int sysTimeWithPhase;           //4字节,系统时间,ms。 

unsigned int BigCycleTime=2000;          // 用于取余,类似于钟表表盘刻度,2秒一个周期就是 2000ms,

float Wave;                                           // 动作的正弦波发生器


 //setup()===========

void setup() {

  ///////////////////// 通讯接口

    Serial.begin(1000000);

    delay(20);

  ///////////////////// 机器人各个关节姿态初始化,

 /////////////////////舵机变量初始化

  outPutToServo[0] = 0xFA;               //   帧开始码   FA  AF,FC CF,

  outPutToServo[1] = 0xAF;              //   帧开始码  

  outPutToServo[2] = 0;                    //  舵机ID , ID =0 是广播到所有舵机

  outPutToServo[3] = 0x1;                 //   命令01  舵机转动   //  命令02--读取舵机角度        //     命令04-- LED

  outPutToServo[4] = 0x90;              //  命令1参数--angle 目标角度,     取值范围[0-240],单位:度   --取值 FF ,停止 

  outPutToServo[5] = 0x30;           //  对于01命令 舵机转动时间                                  

  outPutToServo[6] = 0x00;            //   Hight byte   //命令1参数,不应期锁定时间                    

  outPutToServo[7] = 0x00;           //   Low  byte,      //命令1参数,不应期锁定时间

  outPutToServo[8] = 0x1;              //  校验字节,sum(byte 2--byte 7) , 此处数值稍后在loop中被更新为正确的值

  outPutToServo[9] = 0xED;            //  帧结束码 237

}


//loop()==============

void loop() {

/////////// 从1-32个动作,每个loop()执行一个动作。  //if (ActionID==32) {ActionID=0;}

  if (ActionID==6){ActionID=0;}  

  ActionID =ActionID+1  ;

  ////////////////////// 读传感器

  ////////////////////// 读遥控器

  /////////////////////// 读时间

 int SmallCycleTime =ActionArray[ActionID][4]  -  ActionArray[ActionID][3];

 sysTime =millis();                                          //存储 32 位(4 字节)

 int  BigPhaseTime = (sysTime+ActionArray[ActionID][5]) % BigCycleTime;

 if (  (BigPhaseTime <= ActionArray[ActionID][3]) || (BigPhaseTime >= ActionArray[ActionID][4] )   ) {

       return;// debug   <<<<<<<<<<<可以用while( ){}         //  不能用continue 会有报错:continue statement not within a loop,更不能用 break,试图退出loop()

 }

  Wave=sin((BigPhaseTime - ActionArray[ActionID][3]) / (SmallCycleTime/6.28)); //在周期内,Wave取值区间:负一到正一,作为步态 的核心发生器   

                       //  %取模 

                       //  (CycleTime/6.28)是 2000ms对应了2pi(6.28),所以要除以2000/6.28  

                       //  sin(rad) 正弦函数, (以弧度为单位)。结果将介于 -1 和 1 之间

                       // bug 为何 ActionArray[ActionID][4] 也能运行??????????

  /////////////////////////计算步态

  //////////////////////// 写舵机

  outPutToServo[2] = ActionArray[ActionID][1] ;

  outPutToServo[4] = 0x70+ int(  Wave * (ActionArray[ActionID][7])  ) ;      // <<<<<<<   //  bug 计算报错 debug   舵机运动角度的幅度,不能没有[下标]

  sum = 0;                            //  计算校验字节,sum(byte 2--byte 7)

    for (int i = 2; i < 8; i++) {

      sum +=outPutToServo[i];

      }

  outPutToServo[8]=sum;

  for (int i = 0; i <=9; i++){                                          

   //mySerial.write(outPutToServo[i]);                               

    Serial.write(outPutToServo[i]);                           //   <<<<<<<  写舵机数据  ,写10byte

    }

  delay(2);                                                      

}



经验

自顶到底的方法编程,

中心函数作为步态的发生器,

统一的变量命名(直观,简洁),

2维的变量数组(表格),压缩程序篇幅,方便调试


教训

没有完全采用自顶到底的方法编程,导致有些bug错误卡住,浪费了时间。

没有每一步输出一个结果,一大段编程debug不易。

没有完善的监视器,包括硬件不支持(todo修改2线转1线,并将pc串口与舵机串口分开)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 20230310 一个损坏的优某某悟空机器人,去除了头、电池、双层主板,只剩一个带12个舵机的躯体。 ●焊上3根导...
    hydro阅读 444评论 0 1
  • 20230311 进一步追查原因,是2线串口转1线串口转接板 里 IC--74HC126 的响应速度慢导致的,cm...
    hydro阅读 241评论 0 1
  • 控制阶段 舵机和超声波传感器结合实现测距和避障的功能 实现的基本思想:通过超声波传感器测量出小车到障碍物的距离,当...
    竹_e335阅读 552评论 0 0
  • 关于phpAES phpAES是128、192和256位AES加密密码的PHP 5(现在包括PHP 4版本)类实现...
    丶沉寂有定期阅读 595评论 0 0
  • 电路图 代码 ## 8x8x8 led并行接口arduino代码 ```` #include #include #...
    esnis阅读 397评论 0 0