1. 功能说明

     本文示例将实现R307样机17自由度人形机器人行走的功能。该项目利用探索者平台制作,其驱动系统采用伺服电机。

人形机器人 架构设计 人形机器人概念_机器人


2. 仿人形机器人结构设计

     人型机器人是一种旨在模仿人类外观和行为的机器人(robot),尤其特指具有和人类相似肌体的种类。常见一个包含完整四肢和头部运动的机器人需要17个自由度,每条腿有5个自由度;两条手臂共6个自由度,每条手臂3个自由度;头部1个自由度,全部一共17个自由度。


人形机器人 架构设计 人形机器人概念_机器人_02


     串联人形机构:串联人形仿生机器人是由多个 舵机关节模组 组合而成的,类似于多个串联机械臂组装而成。

人形机器人 架构设计 人形机器人概念_人工智能_03



3. 仿人形机器人运动算法

      人形机器人行走主要依靠腿部的运动,同时可以通过甩臂等动作调整平衡姿态,所以人形机器人的步态规划主要看腿部各关节的协调,下面给大家列一个10自由度人形双足机器人的前进步态:

      这里为了方便分析,将双足简化成如下图所示,其中每条腿包含一个两自由度的髋关节,共两个自由度A和B,A左右摆动、B为前后摆动;一个自由度的膝关节C,为前后摆动;两个自由度的踝关节D和E,D为前后摆动、E为左右摆动。

人形机器人 架构设计 人形机器人概念_#define_04



      正面图:第一步通过左右倾斜让左腿脱离地面,注意保持上半身的水平。调整A/A1、E/E1:


人形机器人 架构设计 人形机器人概念_#define_05


      侧面图:第二步左腿抬起,右腿回到初始位置。调整B、C、D:

人形机器人 架构设计 人形机器人概念_人形机器人 架构设计_06

      侧面图:第三步右腿向前弯曲,使身体前倾,让左脚落地,为下一步右腿迈步做准备:


人形机器人 架构设计 人形机器人概念_人形机器人 架构设计_07


      正面图:第四步通过左右倾斜让左腿脱离地面,注意保持上半身的水平。调整A/A1、E/E1(注意这一步之前左脚落地后绷直):

人形机器人 架构设计 人形机器人概念_人形机器人 架构设计_08

      侧面图:第五步右腿抬起,左腿回到初始位置。调整B1、C1、D1:

人形机器人 架构设计 人形机器人概念_#define_09

      侧面图:第六步左腿向前弯曲,使身体前倾,让右脚脚落地,为下一步左腿迈步做准备:

人形机器人 架构设计 人形机器人概念_人形机器人 架构设计_10

      提示:在上面的步态描述中所画的图都以每一步最全面的角度为准,所以大家不要在意视图,注意步态。

4. 电子硬件



本实验中采用了以下硬件:

主控板

Basra主控板(兼容Arduino Uno)

扩展板

Bigfish2.1扩展板

SH-ST步进电机扩展板

电池

7.4V锂电池

电路连接:

1~17号舵机接线如下:

D1、D2、D3、D4、D7、D8、D9、D18、D19、D20、D21、D23、D24、D25、D26、D27、D5


人形机器人 架构设计 人形机器人概念_人形机器人 架构设计_11


5. 功能实现

上位机:Controller 1.0

下位机编程环境:Arduino 1.8.19

5.1示例程序

下面提供一个17自由度人形机器人行走前进的参考例程(_17servo_human.ino),将参考例程下载到主控板中:

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-04-14 https://www.robotway.com/

  ------------------------------*/

#include <Arduino.h>        //要用到的函数库

#include <avr/pgmspace.h>

#include "Config.h"

#include <Tlc5940.h>

#include <tlc_servos.h>

/* 此程序包含四个动作组,如有需要可自行添加,需修改处有注释,按注释修改

*

* act_num:定义动作组数量

* frequency:定义舵机转动频率

* servo_num:定义舵机数量

* servo_speed:定义舵机速度,单位毫秒

* action_delay:定义动作组每行动作执行的动作间隔,单位微秒

* actChangeDelay:定义不同动作组执行时的切换时间间隔,单位毫秒

*/

#define act_num 6

#define frequency 10

#define servo_num 17

#define servo_speed 4

#define action_delay 10

#define actChangeDelay 1000


/*

* 动作组定义,可自行添加动作组,名称可修改,此处为 4 个动作组

*/

#define action_move 0

#define action_left 1

#define action_right 2

#define action_back 3

#define action_dance 4

#define action_init 5


/*

* servo_pin:定义舵机引脚,按顺序从小到大定义,引脚号应从 1 开始

* actPwmNum:定义动作组数组的长度

*/

int servo_pin[servo_num] = {1, 2, 3, 4, 5, 7, 8, 9, 18, 19, 20, 21, 23, 24, 25, 26, 27};

int actPwmNum[act_num] = {};


//中间数组

float value_pre[servo_num] = {};

float value_cur[servo_num] = {};

int count_input = 0;

boolean _b = false;


/**************+++++++++++动作组数组,请将转换后的动作组数据粘贴到相应的动作组中+++++++***************************/

//这个动作组没有添加动作,可以自己添加机器人动作

const PROGMEM int actionInit[] = {

};


//actionMove[]该动作组包括了机器人的一些简单的动作,包括转头、下蹲、踮脚、左右摇晃、骑马舞、扎马步等动作。

const PROGMEM int actionMove[] = {

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1322,1433,1078,1500,1456,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

922,1856,1522,1344,1522,1833,767,1456,1322,1433,1078,1500,1456,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,522,1411,1456,1544,1439,1078,1500,1656,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

2056,900,1522,1344,1522,1011,1878,1456,1544,1439,1078,1500,1656,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1411,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

1411,1989,856,1344,1522,633,1922,1456,1367,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,2078,767,1389,1456,1478,1439,1078,1500,1656,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1322,1433,1078,1500,1456,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

922,1856,1522,1344,1522,1833,767,1456,1322,1433,1078,1500,1456,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,522,1411,1456,1544,1439,1078,1500,1656,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1433,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

2056,900,1522,1344,1522,1011,1878,1456,1544,1439,1078,1500,1656,1367,1767,1367,1411,

1411,2300,1522,1344,1522,544,1411,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

1411,1989,856,1344,1522,633,1922,1456,1367,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

656,2100,1522,1344,2078,767,1389,1456,1478,1439,1078,1500,1656,1367,1767,1367,1411,

656,2100,1522,1344,1522,633,1922,1456,1456,1439,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,523,1322,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,523,1322,1456,1456,1433,1078,1500,1500,1367,1767,1367,1411,

1411,2300,1522,1344,1522,523,1322,1500,1589,1452,1078,1500,1688,1367,1767,1367,1411,

1411,2300,1967,1344,1944,523,1322,1500,1589,1452,1078,1500,1567,1367,1678,1233,1411,

1411,2300,1700,1344,1700,523,1322,1456,1456,1452,1078,1500,1544,1367,1678,1233,1411,

1411,2300,1522,1344,1522,523,1322,1433,1144,1452,1078,1500,1389,1367,1678,1233,1353,

1411,2300,967,1344,967,523,1322,1390,1456,1452,1144,1633,1389,1389,1767,1367,1353,

1411,2300,1322,1344,1344,523,1322,1456,1483,1433,1144,1633,1500,1389,1767,1367,1411,

1411,2300,1522,1344,1522,523,1322,1485,1601,1433,1144,1633,1678,1389,1767,1367,1435,

1411,2300,2122,1344,1967,523,1322,1493,1601,1433,1078,1500,1589,1418,1589,1078,1435,

1411,2300,1767,1344,1700,523,1322,1456,1456,1433,1078,1500,1478,1415,1589,1078,1436,

1411,2300,1522,1344,1522,523,1322,1455,1313,1433,1078,1500,1389,1388,1589,1065,1402,

1411,2300,922,1344,856,523,1322,1455,1389,1433,1161,1656,1389,1389,1767,1376,1402,

1411,2300,1344,1344,1278,523,1322,1456,1456,1433,1161,1656,1500,1389,1767,1376,1411,

};


//这个动作组没有添加动作,可以自己添加机器人动作

const PROGMEM int actionLeft[] = {

};


//这个动作组没有添加动作,可以自己添加机器人动作

const PROGMEM int actionRight[] = {

};


//这个动作组没有添加动作,可以自己添加机器人动作

const PROGMEM int actionBack[] = {

};


//actionDance[]该动作组是机器人的行走步态动作组

const PROGMEM int actionDance[] = {

1411,2300,1344,1344,1278,523,1322,1456,1456,1433,1161,1656,1500,1389,1767,1376,1411,

1411,2300,1522,1344,1522,523,1322,1485,1601,1433,1144,1633,1678,1389,1767,1367,1435,

1411,2300,2122,1344,1967,523,1322,1493,1601,1433,1078,1500,1589,1418,1589,1078,1435,

1411,2300,1767,1344,1700,523,1322,1456,1456,1433,1078,1500,1478,1415,1589,1078,1436,

1411,2300,1522,1344,1522,523,1322,1455,1313,1433,1078,1500,1389,1388,1589,1065,1402,

1411,2300,922,1344,856,523,1322,1455,1389,1433,1161,1656,1389,1389,1767,1376,1402,

1411,2300,1344,1344,1278,523,1322,1456,1456,1433,1161,1656,1500,1389,1767,1376,1411,

};


/**************************+++++---------分割线--------++++++*******************************************************/


//动作组数组长度获取函数,增加动作组时需要按如下格式添加:actPwmNum[增加的动作组序号] = sizeof(增加的动作组名称) / sizeof(增加的动作组名称[0]);

void act_length()

{

  actPwmNum[0] = (sizeof(actionMove) / sizeof(actionMove[0]))/servo_num;

  actPwmNum[1] = (sizeof(actionLeft) / sizeof(actionLeft[0]))/servo_num;

  actPwmNum[2] = (sizeof(actionRight) / sizeof(actionRight[0]))/servo_num;

  actPwmNum[3] = (sizeof(actionBack) / sizeof(actionBack[0]))/servo_num;

  actPwmNum[4] = (sizeof(actionDance) / sizeof(actionDance[0]))/servo_num;

  actPwmNum[5] = (sizeof(actionInit) / sizeof(actionInit[0]))/servo_num;


  for(int i=0;i<act_num;i++)

  {

    Serial.println(actPwmNum[i]);   //打印出每个动作组中各有多少个动作(也就是打印出每个动作组中有几行动作)

  }

  delay(2000);


}


//map映射函数

long map_servo(long x, long in_min, long in_max, long out_min, long out_max)

{

  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

}


//PWM 转换为舵机角度的函数,增加动作组时需要修改

void vlaue2angle(int p, int act)

{

  switch(act)

  {

    case 0:   value_cur[p] = map_servo(pgm_read_word_near(actionMove + p + servo_num * count_input), 500, 2500, 0, 180);   break;

    case 1:   value_cur[p] = map_servo(pgm_read_word_near(actionLeft + p + servo_num * count_input), 500, 2500, 0, 180);   break;

    case 2:   value_cur[p] = map_servo(pgm_read_word_near(actionRight + p + servo_num * count_input), 500, 2500, 0, 180);   break;

    case 3:   value_cur[p] = map_servo(pgm_read_word_near(actionBack + p + servo_num * count_input), 500, 2500, 0, 180);   break;

    case 4:   value_cur[p] = map_servo(pgm_read_word_near(actionDance + p + servo_num * count_input), 500, 2500, 0, 180); break;

    case 5:   value_cur[p] = map_servo(pgm_read_word_near(actionInit + p + servo_num * count_input), 500, 2500, 0, 180); break;

    default: break;

  }

}


//舵机初始化函数(初始化舵机动作),动作组第一行为舵机初始化值

void servo_init(int act, int num)

{   

  if(!_b)

  {

    for(int i=0;i<servo_num;i++)

    {

      vlaue2angle(i, act);

      tlc_setServo(servo_pin[i], value_cur[i]);

      value_pre[i] = value_cur[i];

    }

    Tlc.update();

  }  

  num == 1 ? _b = true : _b = false;

}


//舵机移动函数,参数: act:动作组宏定义名称 ;num:动作组执行的次数,num > 0 ;

void servo_move(int act, int num)

{  

  float value_delta[servo_num] = {};

  float in_value[servo_num] = {};

 

  servo_init(act, num);

 

  for(int i=0;i< num * actPwmNum[act];i++)

  {

    count_input++;

   

    if(count_input == actPwmNum[act])

    {

      count_input = 0;

      continue;

    }

   

    for(int i=0;i<servo_num;i++)

    {

      vlaue2angle(i, act);

      in_value[i] = value_pre[i];

      value_delta[i] = (value_cur[i] - value_pre[i]) / frequency;

    }

   

    for(int i=0;i<frequency;i++)

    {

      for(int k=0;k<servo_num;k++)

      {

        in_value[k] += value_delta[k];  

        value_pre[k] = in_value[k];

      }     

      for(int j=0;j<servo_num;j++)

      {       

        tlc_setServo(servo_pin[j], in_value[j]);

        delay(servo_speed);

      }

      Tlc.update();

    }

  }

}

/********************************************************-------分割线--------****************************************************/

//初始化函数

void setup() {

  Serial.begin(9600);     //开启串口通信,波特率9600

  Tlc.init(0);

  tlc_initServos();

  act_length();  

  delay(action_delay);

}


//主函数

void loop()

{

    servo_move(action_move, 1);   //执行action_move动作组(左右摇,踮脚,扎马步等动作)

    delay(actChangeDelay);

    while(1){

     servo_move(action_dance, 1);   //while(1){...动作...}一直执行括号中的动作(即机器人行走)

      };

}



5.2 调试

       双击打开Controller 1.0.exe。将波特率与串口设置好,同时留下1、2、3、4、5、7、8、9、18、19、20、21、23、24、25、26、27几个舵机串口,调试舵机角度。具体操作步骤可参考 【U002】如何驱动模拟舵机-Controller 1.0b软件的使用

人形机器人 架构设计 人形机器人概念_机器人_12



      将几个舵机的角度调试好并记录下来,将调试好的舵机角度写到对应例程(_17servo_human.ino)中的位置;下图所示是角度,大家可尝试自己更改角度值使其动作看起来更流畅。

人形机器人 架构设计 人形机器人概念_舵机_13



提示:17自由度人形机器人行走调试并不只是通过程序去调整步态和舵机角度,还涉及到重心的调整,重心可以通过编程调整,也可以通过调整结构实现。