极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 2316|回复: 0

12自由度六足机器人实现步态规划功能

[复制链接]
发表于 2023-3-20 11:27:42 | 显示全部楼层 |阅读模式
本帖最后由 机器谱 于 2023-3-20 11:27 编辑

1. 运动功能描述
     R111样机是一款拥有12个自由度的串联仿生六足机器人。本文示例实现12自由度六足机器人的行走功能,包括前进、后退、左转、右转。


2. 结构说明
     R111样机由六个2自由度串联结构组成,中间由舵机双折弯和螺柱结合固定,从而达到一个仿生机器人的结构。

      该2自由度串联结构由舵机1和舵机2驱动,其中舵机1实现腿部前后摆动,舵机2实现腿部的上下抬伸。其中抬伸通过一个平行四连杆ABCD作为传动结构以增加腿部的行程和增强腿部运动的稳定性。


3. 步态原理
     12自由度六足机器人的运动步态有两种:一种是采用三角步态进行运动,一种是采用波动步态运动。
① 三角步态
     12自由度六足仿生机器人的三角步态是将机器人六足分成两组腿(身体一侧的前足、后足与另一侧的中足)分别进行摆动和支撑,即处于三角形上的三条腿的动作一样,均处于摆动相或均处于支撑相。如图:


② 波动步态
    12自由度六足仿生机器人的波动步态是机器人每条腿两侧依次运动,即左(右)侧一条腿先迈步,再右(左)侧腿迈步,再左(右)侧下一条腿运动,如此循环完成波动步态的运动。如图:


提示:可以参考蜈蚣的步态。
本文讲一下三角步态的实现案例,波动步态大家可以自己研究。


4. 基于STM32主控板实现
4.1电子硬件

在这个示例中,我们采用了以下硬件,请大家参考:

主控板STM32主控板
扩展板STM32扩展板
舵机标准舵机
电池7.4V锂电池

按下图进行电路连接: 我们先对机构的舵机进行编号(见下图)


接下来与扩展板进行连接(见下图)


相应的引脚编号见下表,这些引脚号在编程中将会用到。

物料引脚号物料引脚号
舵机0PE9舵机6PD15
舵机1PE11舵机7
PD14
舵机2PE13舵机8
PD13
舵机3PE14舵机9
PB15
舵机4PC6舵机10
PB14
舵机5PC7舵机11
PB9
4.2 示例程序
编程环境:keil5
使用Controller获取姿态参数,并拷贝到下面的程序中。
三角步态前进的参考例程(USER\test.uvprojx),main.c的代码:
  1. /*------------------------------------------------------------------------------------

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

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

  4.            https://opensource.org/licenses/MIT

  5.            by 机器谱 2023-02-16 https://www.robotway.com/

  6.   ------------------------------*/

  7. #include "sys.h"

  8. #include "led.h"

  9. #include "stdio.h"

  10. #include "delay.h"

  11. #include "usart.h"

  12. #include "stdio.h"

  13. #include "core_cm4.h"

  14. #include "string.h"

  15. #include "stdlib.h"

  16. #include "pwm.h"

  17. #include "timer.h"

  18. #include "math.h"


  19. /*

  20.   7 1

  21.     |      |

  22. 6----| |----0

  23. | |

  24. 9 3

  25.     |      |

  26. 8----| |----2

  27. | |

  28. 11 5

  29.     |      |

  30. 10----| |----4

  31. | |

  32. */



  33. float Ang[12] = {100, 85, 90, 90, 90, 80, 100, 90, 95, 90, 90, 95};

  34. int Ang_Init[12] = {100, 85, 90, 90, 90, 80, 100, 90, 95, 90, 90, 95};

  35. float data_f[12] = {0};


  36. void split(char *src,const char *separator,char **dest,int *num);//字符串拆分函数

  37. float sort(float data[12]);//排序函数(小->大)

  38. float find_min(float *de);//寻找最小值函数

  39. void Set_Ang(float, float, float, float, float, float, float, float, float, float, float, float);//设置舵机旋转角度函数

  40. void Steering_Gear_Init(void);//机械臂初始化位置函数

  41. void Steering_Gear_Angle(u8 num, float ang);//单个舵机控制函数

  42. void Steering_Gear_Ang_Pwm1(float data[12]);//机械臂移动函数


  43. int main(void)

  44. {

  45. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

  46. delay_init(168);//初始化延时,168为CPU运行频率

  47. usart_init(115200); //串口初始化

  48. LED_Init();//初始化LED灯

  49. TIM1_PWM_Init(20000-1, 168-1);

  50. TIM4_PWM_Init(20000-1,84-1);

  51. TIM3_PWM_Init(20000-1,84-1);

  52. TIM11_PWM_Init(20000-1,168-1);

  53. TIM12_PWM_Init(20000-1,84-1);

  54. Steering_Gear_Init();//六足位置初始化

  55. while(1)

  56. {

  57. Set_Ang(80,85,110,90,110,80,80,90,115,90,70,95);

  58. Steering_Gear_Ang_Pwm1(data_f);

  59. delay_ms(1000);

  60. while(1){

  61. Set_Ang(90,100,105,75,110,95,90,105,105,75,80,110);

  62. Steering_Gear_Ang_Pwm1(data_f);

  63. Set_Ang(70,100,70,75,130,95,120,105,120,75,120,110);

  64. Steering_Gear_Ang_Pwm1(data_f);

  65. Set_Ang(70,70,70,105,130,65,120,75,120,105,120,80);

  66. Steering_Gear_Ang_Pwm1(data_f);

  67. Set_Ang(90,70,105,105,110,65,90,75,105,105,80,80);

  68. Steering_Gear_Ang_Pwm1(data_f);

  69. Set_Ang(120,70,120,105,60,65,80,75,70,105,70,80);

  70. Steering_Gear_Ang_Pwm1(data_f);

  71. Set_Ang(120,100,120,75,60,95,80,105,70,75,70,110);

  72. Steering_Gear_Ang_Pwm1(data_f);

  73. }

  74. }

  75. }


  76. void split(char *src,const char *separator,char **dest,int *num)

  77. {

  78. char *pNext;

  79. int count = 0;

  80. if (src == NULL || strlen(src) == 0)//字符串为空或遇到结束标志

  81. return;

  82. if (separator == NULL || strlen(separator) == 0)//分隔符为空

  83. return;   

  84. pNext = strtok(src,separator);//字符串分割

  85. while(pNext != NULL) {

  86. *dest++ = pNext;

  87. ++count;

  88. pNext = strtok(NULL,separator);  

  89. }  

  90. *num = count;

  91. }


  92. //对数组内素有成员按从小到大的顺序从新排列,并返回最小值

  93. float sort(float data1[12])

  94. {

  95. float array[12] = {0};

  96. int i = 0;

  97. int j = 0;

  98. int temp;

  99. for(int i=0; i < 12; i++){

  100. array[i] = data1[i];

  101. }

  102. for ( i = 0; i < 12; i++){

  103. for ( j = 0; j < 11-i; j++){

  104. if(array[j]>array[j+1]){

  105. temp=array[j];

  106. array[j]=array[j+1];

  107. array[j+1]=temp;

  108. }

  109. }

  110. }

  111. return find_min(array);

  112. }

  113. //寻找数组中的最小值

  114. float find_min(float *de)

  115. {

  116. if((int)*de != 0)

  117. return *de;

  118. find_min(++de);

  119. }

  120. //给数组赋值

  121. void Set_Ang(float ang1, float ang2, float ang3, float ang4, float ang5, float ang6, float ang7, float ang8, float ang9, float ang10, float ang11, float ang12)

  122. {

  123. data_f[0] = ang1;

  124. data_f[1] = ang2;

  125. data_f[2] = ang3;

  126. data_f[3] = ang4;

  127. data_f[4] = ang5;

  128. data_f[5] = ang6;

  129. data_f[6] = ang7;

  130. data_f[7] = ang8;

  131. data_f[8] = ang9;

  132. data_f[9] = ang10;

  133. data_f[10] = ang11;

  134. data_f[11] = ang12;

  135. }


  136. //将两个位置之间需要舵机移动的度数转换为多步,然后分步完成

  137. void Steering_Gear_Ang_Pwm1(float data[12])

  138. {

  139. float den = 0;

  140. float dif[12] = {0};

  141. floatang[12] = {0};

  142. //计算两个位置之间的差值

  143. for(int i=0; i < 12; i++){

  144. dif[i] = fabs(data[i] - Ang[i]);

  145. }


  146. //得到最小差值

  147. den = sort(dif);


  148. //计算12个舵机每次移动的最小距离

  149. if((int)den == 0){

  150. for(int i=0; i < 12; i++)

  151. ang[i] = 0;

  152. }else{

  153. for(int i=0; i < 12; i++){

  154. ang[i] = (data[i] - Ang[i])/den;

  155. }


  156. for(int i=0; i<den; i++){

  157. for(int j=0; j < 12; j++)

  158. Ang[j] += ang[j];

  159. //对多个舵机进行位置限制,防止舵机堵转

  160. for(int g=0; g < 12; g++){

  161. if(Ang[g] < 0)

  162. Ang[g] = 0;

  163. else if(Ang[g] > 180)

  164. Ang[g] = 180;

  165. }

  166. //舵机控制

  167. for(int k=0; k<12; k++){

  168. Steering_Gear_Angle(k, Ang[k]);

  169. }

  170. }

  171. }

  172. }


  173. void Steering_Gear_Angle(u8 num, float ang)

  174. {

  175. u32 Ang = 0;

  176. Ang = 500 + ang*2000/180;

  177. switch(num){

  178. case 0 :

  179. TIM_SetCompare1(TIM1, Ang);//修改比较值,修改占空比

  180. break;

  181. case 1 :

  182. TIM_SetCompare2(TIM1, Ang);//修改比较值,修改占空比

  183. break;

  184. case 2 :

  185. TIM_SetCompare3(TIM1, Ang);//修改比较值,修改占空比

  186.       break;

  187. case 3 :

  188. TIM_SetCompare4(TIM1, Ang);//修改比较值,修改占空比

  189. break;

  190. case 4 :

  191. TIM_SetCompare1(TIM3, Ang);//修改比较值,修改占空比

  192. break;

  193. case 5 :

  194. TIM_SetCompare2(TIM3, Ang);//修改比较值,修改占空比

  195. break;

  196. case 6 :

  197. TIM_SetCompare4(TIM4, Ang);//修改比较值,修改占空比

  198. break;

  199. case 7 :

  200. TIM_SetCompare3(TIM4, Ang);//修改比较值,修改占空比

  201. break;

  202. case 8 :

  203. TIM_SetCompare2(TIM4, Ang);//修改比较值,修改占空比

  204. break;

  205. case 9 :

  206. TIM_SetCompare2(TIM12, Ang);//修改比较值,修改占空比

  207.       break;

  208. case 10 :

  209. TIM_SetCompare1(TIM12, Ang);//修改比较值,修改占空比

  210. break;

  211. case 11 :

  212. TIM_SetCompare1(TIM11, Ang);//修改比较值,修改占空比

  213. break;

  214. default:

  215. break;

  216. }

  217. delay_ms(1);

  218. }

  219. //六足起始位置

  220. void Steering_Gear_Init(void)

  221. {

  222. for(int i=0; i < 12; i++){

  223. Steering_Gear_Angle(i,Ang_Init[i]);

  224. }

  225. delay_ms(1000);

  226. }
复制代码

5. 基于Arduino主控板实现
5.1 电子硬件

在这个示例中,我们采用了以下硬件,请大家参考:

主控板
Basra主控板(兼容Arduino Uno)
扩展板
STM32扩展板
舵机标准舵机
电池7.4V锂电池

电路连接说明:
因为12自由度六足机器人结构上装有12个舵机,所以将采用 SH-SR舵机扩展板 。
舵机接线顺序:1、3; 4、 5; 6、 8;11、13;15、 21;24、26。


5.2 示例程序
使用Controller获取姿态参数,并拷贝到下面的12自由度六足机器人三角步态的参考例程(robot_walk.ino)之中,并下载到主控板:
  1. /*------------------------------------------------------------------------------------

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

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

  4.            https://opensource.org/licenses/MIT

  5.            by 机器谱 2023-02-16 https://www.robotway.com/

  6.   ------------------------------*/

  7. #include <Arduino.h>

  8. #include <avr/pgmspace.h>

  9. #include "Config.h"

  10. #include <Tlc5940.h>

  11. #include <tlc_servos.h>


  12. int count_input = 0;

  13. boolean _b = false;


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

  15. const PROGMEM int actionInit[] = {

  16.   1090, 1790, 1220, 980, 650, 1800, 1020, 1330, 520, 1155, 1260, 2020,

  17.   1090, 1790, 1220, 980, 650, 1800, 1020, 1330, 520, 1155, 1260, 2020,

  18. };

  19. const PROGMEM int actionMove[] = {

  20.   1660, 1230, 1220, 980, 1280, 1190, 1020, 1330, 1090, 1155, 1260, 1540,

  21.   1660, 1790, 1320, 1080, 1280, 1800, 920, 1230, 1090, 1255, 1360, 2020,

  22.   1660, 1230, 1320, 1080, 1280, 1190, 920, 1230, 1090, 1255, 1360, 1540,

  23.   1090, 1300, 1120, 880, 650, 900, 1120, 1430, 520, 1055, 1160, 1300,

  24.   1660, 1300, 1120, 880, 1280, 900, 1120, 1430, 1090, 1055, 1160, 1300,

  25. };

  26. const PROGMEM int actionLeft[] = {

  27. };

  28. const PROGMEM int actionRight[] = {

  29. };

  30. const PROGMEM int actionBack[] = {

  31. };

  32. const PROGMEM int actionDance[] = {

  33. };

  34. /**************************+++++---------分割线--------++++++*******************************************************/

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

  36. void act_length()

  37. {

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

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

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

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

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

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

  44. }


  45. //map映射函数

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

  47. {

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

  49. }


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

  51. void vlaue2angle(int p, int act)

  52. {

  53.   switch(act)

  54.   {

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

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

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

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

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

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

  61.     default: break;

  62.   }

  63. }


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

  65. void servo_init(int act, int num)

  66. {

  67.   if(!_b)

  68.   {

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

  70.     {

  71.       vlaue2angle(i, act);

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

  73.       value_pre[i] = value_cur[i];

  74.     }

  75.     Tlc.update();

  76.     //delay(1000);

  77.   }

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

  79. }


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

  81. void servo_move(int act, int num)

  82. {  

  83.   float value_delta[servo_num] = {};

  84.   float in_value[servo_num] = {};

  85.   servo_init(act, num);



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

  87.   {

  88.     count_input++;

  89.     if(count_input == actPwmNum[act])

  90.     {

  91.       count_input = 0;

  92.       continue;

  93.     }

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

  95.     {

  96.       vlaue2angle(i, act);

  97.       in_value[i] = value_pre[i];

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

  99.     }

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

  101.     {

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

  103.       {

  104.         in_value[k] += value_delta[k];  

  105.         value_pre[k] = in_value[k];

  106.       }

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

  108.       {      

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

  110.         delayMicroseconds(servo_speed);

  111.       }

  112.       Tlc.update();

  113.     }

  114.     delayMicroseconds(action_delay);

  115.   }

  116. }

  117. /********************************************************-------分割线--------****************************************************/

  118. //初始化函数

  119. void setup() {

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

  121.   Tlc.init(0);

  122.   tlc_initServos();

  123.   act_length();

  124.   delay(action_delay);

  125. }


  126. //主函数,walk

  127. void loop() {

  128.     servo_move(action_move, 6);

  129.     delay(100);

  130. }
复制代码
注意:
      上面虽然给大家提供了一个参考例程,但并不意味着直接将程序烧录成功后机器人即可正常运行,还需要大家对一些参数或者结构进行调整。调试提示:
(1)安装时将每个关节的舵机角度调成与程序一致;
(2)程序里面初始角度调整为安装时的角度,然后根据之前运动的角度差调整其它动作的角度;
(3)注意每条腿上舵机对应的端口号,运动需要与三角步态对应;
(4)可以通过调整延迟参数来控制机器人的运动快慢。


6. 资料下载

资料内容:
①步态规划-STM32例程源代码
②步态规划-Arduino例程源代码
资料下载地址:
https://www.robotway.com/h-col-197.html

想了解更多机器人开源项目资料请关注 机器谱网站 https://www.robotway.com

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-25 10:06 , Processed in 0.044944 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表