麦克纳姆轮全向移动原理

什么是麦克纳姆轮

在竞赛机器人和特殊工种机器人中,全向移动经常是一个必需的功能。「全向移动」意味着可以在平面内做出任意方向平移同时自转的动作。为了实现全向移动,一般机器人会使用「全向轮」(Omni Wheel)或「麦克纳姆轮」(Mecanum Wheel)这两种特殊轮子。

全向轮:

 

麦克纳姆轮全向移动原理_模块

麦克纳姆轮

 

麦克纳姆轮全向移动原理_模块_02

全向轮与麦克纳姆轮的共同点在于他们都由两大部分组成:轮毂和辊子(roller)。轮毂是整个轮子的主体支架,辊子则是安装在轮毂上的鼓状物。全向轮的轮毂轴与辊子转轴相互垂直,而麦克纳姆轮的轮毂轴与辊子转轴呈 45° 角。理论上,这个夹角可以是任意值,根据不同的夹角可以制作出不同的轮子,但最常用的还是这两种。

全向轮与麦克纳姆轮(以下简称「麦轮」)在结构、力学特性、运动学特性上都有差异,其本质原因是轮毂轴与辊子转轴的角度不同。经过分析,二者的运动学和力学特性区别可以通过以下表格来体现。

 

麦克纳姆轮全向移动原理_麦克纳姆轮_03

计算过程如下,供参考,学霸可点开大图验算:

 

麦克纳姆轮全向移动原理_麦克纳姆轮_04

近年来,麦轮的应用逐渐增多,特别是在 Robocon、FRC 等机器人赛事上。这是因为麦克纳姆轮可以像传统轮子一样,安装在相互平行的轴上。而若想使用全向轮完成类似的功能,几个轮毂轴之间的角度就必须是 60°,90° 或 120° 等角度,这样的角度生产和制造起来比较麻烦。所以许多工业全向移动平台都是使用麦克纳姆轮而不是全向轮,比如这个国产的叉车: 全向移动平台 麦克纳姆轮叉车 美科斯叉车

另外一个原因,可能是麦轮的造型比全向轮要酷炫得多,看起来有一种不明觉厉的感觉……

 

麦克纳姆轮全向移动原理_模块_05

的确,第一次看到麦轮运转起来,不少人都会惊叹。以下视频直观地说明了麦轮底盘在平移和旋转时的轮子旋转方向。

 

麦克纳姆轮全向移动原理_麦克纳姆轮_06

麦克纳姆轮工作原理—在线播放—优酷网,视频高清在线观看

视频

麦轮的安装方法

麦轮一般是四个一组使用,两个左旋轮,两个右旋轮。左旋轮和右旋轮呈手性对称,区别如下图。

 

麦克纳姆轮全向移动原理_麦克纳姆轮_07

安装方式有多种,主要分为:X-正方形(X-square)、X-长方形(X-rectangle)、O-正方形(O-square)、O-长方形(O-rectangle)。其中 X 和 O 表示的是与四个轮子地面接触的辊子所形成的图形;正方形与长方形指的是四个轮子与地面接触点所围成的形状。

 

麦克纳姆轮全向移动原理_模块_08

  • X-正方形:轮子转动产生的力矩会经过同一个点,所以 yaw 轴无法主动旋转,也无法主动保持 yaw 轴的角度。一般几乎不会使用这种安装方式。
  • X-长方形:轮子转动可以产生 yaw 轴转动力矩,但转动力矩的力臂一般会比较短。这种安装方式也不多见。
  • O-正方形:四个轮子位于正方形的四个顶点,平移和旋转都没有任何问题。受限于机器人底盘的形状、尺寸等因素,这种安装方式虽然理想,但可遇而不可求。
  • O-长方形:轮子转动可以产生 yaw 轴转动力矩,而且转动力矩的力臂也比较长。是最常见的安装方式。

 

麦轮底盘的正逆运动学模型

以O-长方形的安装方式为例,四个轮子的着地点形成一个矩形。正运动学模型(forward kinematic model)将得到一系列公式,让我们可以通过四个轮子的速度,计算出底盘的运动状态;而逆运动学模型(inverse kinematic model)得到的公式则是可以根据底盘的运动状态解算出四个轮子的速度。需要注意的是,底盘的运动可以用三个独立变量来描述:X轴平动、Y轴平动、yaw 轴自转;而四个麦轮的速度也是由四个独立的电机提供的。所以四个麦轮的合理速度是存在某种约束关系的,逆运动学可以得到唯一解,而正运动学中不符合这个约束关系的方程将无解。

先试图构建逆运动学模型,由于麦轮底盘的数学模型比较复杂,我们在此分四步进行:

①将底盘的运动分解为三个独立变量来描述;

②根据第一步的结果,计算出每个轮子轴心位置的速度;

③根据第二步的结果,计算出每个轮子与地面接触的辊子的速度;

④根据第三部的结果,计算出轮子的真实转速。

 

一、底盘运动的分解

我们知道,刚体在平面内的运动可以分解为三个独立分量:X轴平动、Y轴平动、yaw 轴自转。如下图所示,底盘的运动也可以分解为三个量:

麦克纳姆轮全向移动原理_模块_09 表示 X 轴运动的速度,即左右方向,定义向右为正;

麦克纳姆轮全向移动原理_麦克纳姆轮_10 表示 Y 轴运动的速度,即前后方向,定义向前为正;

麦克纳姆轮全向移动原理_模块_11 表示 yaw 轴自转的角速度,定义逆时针为正。

以上三个量一般都视为四个轮子的几何中心(矩形的对角线交点)的速度。

麦克纳姆轮全向移动原理_模块_12

二、计算出轮子轴心位置的速度

定义:

麦克纳姆轮全向移动原理_麦克纳姆轮_13 为从几何中心指向轮子轴心的矢量;

麦克纳姆轮全向移动原理_麦克纳姆轮_14 为轮子轴心的运动速度矢量;

麦克纳姆轮全向移动原理_模块_15 为轮子轴心沿垂直于 麦克纳姆轮全向移动原理_麦克纳姆轮_13 的方向(即切线方向)的速度分量;

 

那么可以计算出:

麦克纳姆轮全向移动原理_模块_17
 

分别计算 X、Y 轴的分量为:

麦克纳姆轮全向移动原理_麦克纳姆轮_18

 

麦克纳姆轮全向移动原理_模块_19

同理可以算出其他三个轮子轴心的速度。

 

麦克纳姆轮全向移动原理_模块_20

三、计算辊子的速度

根据轮子轴心的速度,可以分解出沿辊子方向的速度 麦克纳姆轮全向移动原理_麦克纳姆轮_21 和垂直于辊子方向的速度 麦克纳姆轮全向移动原理_麦克纳姆轮_22 。其中 麦克纳姆轮全向移动原理_麦克纳姆轮_23 是可以无视的(思考题:为什么垂直方向的速度可以无视?),而

麦克纳姆轮全向移动原理_麦克纳姆轮_24

其中 麦克纳姆轮全向移动原理_模块_25 是沿辊子方向的单位矢量。

麦克纳姆轮全向移动原理_麦克纳姆轮_26

四、计算轮子的速度

从辊子速度到轮子转速的计算比较简单:

麦克纳姆轮全向移动原理_麦克纳姆轮_27

 

麦克纳姆轮全向移动原理_模块_28

根据上图所示的 麦克纳姆轮全向移动原理_麦克纳姆轮_29麦克纳姆轮全向移动原理_麦克纳姆轮_30 的定义,有

 

麦克纳姆轮全向移动原理_模块_31

结合以上四个步骤,可以根据底盘运动状态解算出四个轮子的转速:

麦克纳姆轮全向移动原理_模块_32

以上方程组就是O-长方形麦轮底盘的逆运动学模型,而正运动学模型可以直接根据逆运动学模型中的三个方程解出来,此处不再赘述。

另一种计算方式

「传统」的推导过程虽然严谨,但还是比较繁琐的。这里介绍一种简单的逆运动学计算方式。

我们知道,全向移动底盘是一个纯线性系统,而刚体运动又可以线性分解为三个分量。那么只需要计算出麦轮底盘在「沿X轴平移」、「沿Y轴平移」、「绕几何中心自转」时,四个轮子的速度,就可以通过简单的加法,计算出这三种简单运动所合成的「平动+旋转」运动时所需要的四个轮子的转速。而这三种简单运动时,四个轮子的速度可以通过简单的测试,或是推动底盘观察现象得出。

当底盘沿着 X 轴平移时:

麦克纳姆轮全向移动原理_麦克纳姆轮_33

当底盘沿着 Y 轴平移时:

麦克纳姆轮全向移动原理_模块_34

当底盘绕几何中心自转时:

麦克纳姆轮全向移动原理_模块_35

将以上三个方程组相加,得到的恰好是根据「传统」方法计算出的结果。这种计算方式不仅适用于O-长方形的麦轮底盘,也适用于任何一种全向移动的机器人底盘。

 

Makeblock 麦轮底盘的组装

理论分析完成,可以开始尝试将其付诸实践了。

第一步,组装矩形框架。

麦克纳姆轮全向移动原理_麦克纳姆轮_36

第二步,组装电机模块。

由于麦轮底盘的四个轮子速度有约束关系,必须精确地控制每个轮子的速度,否则将会导致辊子与地面发生滑动摩擦,不仅会让底盘运动异常,还会让麦轮的寿命减少。所以必须使用编码电机。

 

麦克纳姆轮全向移动原理_麦克纳姆轮_37

第三步,将电机模块安装到框架上。

 

 

麦克纳姆轮全向移动原理_麦克纳姆轮_38

第四步,将麦轮安装到框架上。

 

麦克纳姆轮全向移动原理_模块_39

第五步,安装电路板并接线。

编码电机必须配上相应的驱动板才能正常工作。这里使用的 Makeblock 编码电机驱动板,每一块板可以驱动两个电机。接线顺序在下文中会提及,也可以随意接上,在代码中定义好对应的顺序即可。

 

麦克纳姆轮全向移动原理_麦克纳姆轮_40

第六步,装上电池。

 

麦克纳姆轮全向移动原理_模块_41

至此,一个能独立运行的麦轮底盘就完成了。

 

控制程序

根据麦轮的底盘的运动学模型,要完全控制它的运动,需要有三个控制量:X轴速度、Y轴速度、自转角速度。要产生这三个控制量,有很多种方法,本文将使用一个 USB 游戏手柄,左边的摇杆产生平移速度,右边的摇杆产生角速度。

首先将一个 USB Host 模块连接到 Orion 主板的 3 口。

 

麦克纳姆轮全向移动原理_模块_42

然后插上一个无线 USB 游戏手柄。

 

 

麦克纳姆轮全向移动原理_模块_43

然后再添加其他细节,就大功告成啦!

 

 

麦克纳姆轮全向移动原理_麦克纳姆轮_44

其他细节:

 


 
  1. #include <Wire.h>
  2. #include <SoftwareSerial.h>
  3. #include "MeOrion.h"
  4. MeUSBHost joypad(PORT_3);
  5. // 手柄代码(红灯亮模式)
  6. // 默认:128-127-128-127-15-0-0-128
  7. // 左一:128-127-128-127-15-1-0-128
  8. // 右一:128-127-128-127-15-2-0-128
  9. // 左二:128-127-128-127-15-4-0-128
  10. // 右二:128-127-128-127-15-8-0-128
  11. // 三角:128-127-128-127-31-0-0-128 (0001 1111)
  12. // 方形:128-127-128-127-143-0-0-128 (1000 1111)
  13. // 叉号:128-127-128-127-79-0-0-128 (0100 1111)
  14. // 圆圈:128-127-128-127-47-0-0-128 (0010 1111)
  15. // 向上:128-127-128-127-0-0-0-128 (0000 0000)
  16. // 向下:128-127-128-127-4-0-0-128 (0000 0100)
  17. // 向左:128-127-128-127-6-0-0-128 (0000 0110)
  18. // 向右:128-127-128-127-2-0-0-128 (0000 0010)
  19. // 左上:128-127-128-127-7-0-0-128 (0000 0111)
  20. // 左下:128-127-128-127-5-0-0-128 (0000 0101)
  21. // 右上:128-127-128-127-1-0-0-128 (0000 0001)
  22. // 右下:128-127-128-127-3-0-0-128 (0000 0011)
  23. // 选择:128-127-128-127-15-16-0-128
  24. // 开始:128-127-128-127-15-32-0-128
  25. // 摇杆:右X-右Y-左X-左Y-15-0-0-128
  26. MeEncoderMotor motor1(0x02, SLOT2);
  27. MeEncoderMotor motor2(0x02, SLOT1);
  28. MeEncoderMotor motor3(0x0A, SLOT2);
  29. MeEncoderMotor motor4(0x0A, SLOT1);
  30. // 底盘:a = 130mm, b = 120mm
  31. float linearSpeed = 100;
  32. float angularSpeed = 100;
  33. float maxLinearSpeed = 200;
  34. float maxAngularSpeed = 200;
  35. float minLinearSpeed = 30;
  36. float minAngularSpeed = 30;
  37. void setup()
  38. {
  39. // 要上电才能工作,不能只是插上 USB 线来调试。
  40. motor1.begin();
  41. motor2.begin();
  42. motor3.begin();
  43. motor4.begin();
  44. Serial.begin( 57600);
  45. joypad.init(USB1_0);
  46. }
  47. void loop()
  48. {
  49. Serial.println( "loop:");
  50. //setEachMotorSpeed(100, 50, 50, 100);
  51. if(!joypad.device_online)
  52. {
  53. // 若一直输出离线状态,重新拔插 USB Host 的 RJ25 线试一下。
  54. Serial.println( "Device offline.");
  55. joypad.probeDevice();
  56. delay( 1000);
  57. }
  58. else
  59. {
  60. int len = joypad.host_recv();
  61. parseJoystick(joypad.RECV_BUFFER);
  62. delay( 5);
  63. }
  64. //delay(500);
  65. }
  66. void setEachMotorSpeed(float speed1, float speed2, float speed3, float speed4)
  67. {
  68. motor1.runSpeed(speed1);
  69. motor2.runSpeed(-speed2);
  70. motor3.runSpeed(-speed3);
  71. motor4.runSpeed(-speed4);
  72. }
  73. void parseJoystick(unsigned char *buf) //Analytic function, print 8 bytes from USB Host
  74. {
  75. // 输出手柄的数据,调试用
  76. // int i = 0;
  77. // for(i = 0; i < 7; i++)
  78. // {
  79. // Serial.print(buf[i]); //It won't work if you connect to the Makeblock Orion.
  80. // Serial.print('-');
  81. // }
  82. // Serial.println(buf[7]);
  83. // delay(10);
  84. // 速度增减
  85. switch (buf[ 5])
  86. {
  87. case 1:
  88. linearSpeed += 5;
  89. if (linearSpeed > maxLinearSpeed)
  90. {
  91. linearSpeed = maxLinearSpeed;
  92. }
  93. break;
  94. case 2:
  95. angularSpeed += 5;
  96. if (angularSpeed > maxAngularSpeed)
  97. {
  98. angularSpeed = maxAngularSpeed;
  99. }
  100. break;
  101. case 4:
  102. linearSpeed -= 5;
  103. if (linearSpeed < minLinearSpeed)
  104. {
  105. linearSpeed = minLinearSpeed;
  106. }
  107. break;
  108. case 8:
  109. angularSpeed -= 5;
  110. if (angularSpeed < minAngularSpeed)
  111. {
  112. angularSpeed = minAngularSpeed;
  113. }
  114. break;
  115. default:
  116. break;
  117. }
  118. if (( 128 != buf[ 0]) || ( 127 != buf[ 1]) || ( 128 != buf[ 2]) || ( 127 != buf[ 3]))
  119. {
  120. // 处理摇杆
  121. float x = (( float)(buf[ 2]) - 127) / 128;
  122. float y = ( 127 - ( float)(buf[ 3])) / 128;
  123. float a = ( 127 - ( float)(buf[ 0])) / 128;
  124. mecanumRun(x * linearSpeed, y * linearSpeed, a * angularSpeed);
  125. }
  126. else
  127. {
  128. switch (buf[ 4])
  129. {
  130. case 0:
  131. mecanumRun( 0, linearSpeed, 0);
  132. break;
  133. case 4:
  134. mecanumRun( 0, -linearSpeed, 0);
  135. break;
  136. case 6:
  137. mecanumRun(-linearSpeed, 0, 0);
  138. break;
  139. case 2:
  140. mecanumRun(linearSpeed, 0, 0);
  141. break;
  142. case 7:
  143. mecanumRun(-linearSpeed/ 2, linearSpeed/ 2, 0);
  144. break;
  145. case 5:
  146. mecanumRun(-linearSpeed/ 2, -linearSpeed/ 2, 0);
  147. break;
  148. case 1:
  149. mecanumRun(linearSpeed/ 2, linearSpeed/ 2, 0);
  150. break;
  151. case 3:
  152. mecanumRun(linearSpeed/ 2, -linearSpeed/ 2, 0);
  153. break;
  154. default:
  155. mecanumRun( 0, 0, 0);
  156. break;
  157. }
  158. }
  159. }
  160. void mecanumRun(float xSpeed, float ySpeed, float aSpeed)
  161. {
  162. float speed1 = ySpeed - xSpeed + aSpeed;
  163. float speed2 = ySpeed + xSpeed - aSpeed;
  164. float speed3 = ySpeed - xSpeed - aSpeed;
  165. float speed4 = ySpeed + xSpeed + aSpeed;
  166. float max = speed1;
  167. if (max < speed2) max = speed2;
  168. if (max < speed3) max = speed3;
  169. if (max < speed4) max = speed4;
  170. if (max > maxLinearSpeed)
  171. {
  172. speed1 = speed1 / max * maxLinearSpeed;
  173. speed2 = speed2 / max * maxLinearSpeed;
  174. speed3 = speed3 / max * maxLinearSpeed;
  175. speed4 = speed4 / max * maxLinearSpeed;
  176. }
  177. setEachMotorSpeed(speed1, speed2, speed3, speed4);
  178. }

 

 

转载自:知乎:【学渣的自我修养】麦克纳姆轮浅谈

 

 

//=======================================================================================//

//=======================================================================================//

 

文章中所有图片资料均来自:炽点机器人

 

麦克纳姆轮全向移动原理_麦克纳姆轮_45

我们来画个受力示意图吧

先来看看前后移动

麦克纳姆轮全向移动原理_麦克纳姆轮_46

图中红色实线箭头是车轮向前转产生的摩擦力

蓝色箭头是车轮向后转产生的摩擦力

虚线是分力

于是左边的小车就会向前跑,右边的小车会向后跑


接下来我们画原地旋转

麦克纳姆轮全向移动原理_模块_47
于是左边的小车就顺时针旋转

右边的小车逆时针旋转


让我们再来看看平移

麦克纳姆轮全向移动原理_麦克纳姆轮_48
左边的小车向左平移

右边的小车向右平移


接下来看看斜着跑


麦克纳姆轮全向移动原理_麦克纳姆轮_49麦克纳姆轮全向移动原理_麦克纳姆轮_50
只要同向转动对角线上两个轮子,

就能斜着跑啦

 

转载自:CSDN:麦克纳姆轮全向移动原理

  •                     <li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#csdnc-thumbsup"></use>
                        </svg><span class="name">点赞</span>
                        <span class="count">16</span>
                        </a></li>
                        <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-Collection-G"></use>
                        </svg><span class="name">收藏</span></a></li>
                        <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-fenxiang"></use>
                        </svg>分享</a></li>
                        <!--打赏开始-->
                                                <!--打赏结束-->
                                                <li class="tool-item tool-more">
                            <a>
                            <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                            </a>
                            <ul class="more-box">
                                <li class="item"><a class="article-report">文章举报</a></li>
                            </ul>
                        </li>
                                            </ul>
                </div>
                            </div>
            <div class="person-messagebox">
                <div class="left-message"><a href="https://blog.csdn.net/qq_33835307">
                    <img src="https://profile.csdnimg.cn/D/F/0/3_qq_33835307" class="avatar_pic" username="qq_33835307">
                                            <img src="https://g.csdnimg.cn/static/user-reg-year/1x/4.png" class="user-years">
                                    </a></div>
                <div class="middle-message">
                                        <div class="title"><span class="tit"><a href="https://blog.csdn.net/qq_33835307" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">在路上@Amos</a></span>
                                            </div>
                    <div class="text"><span>发布了325 篇原创文章</span> · <span>获赞 200</span> · <span>访问量 24万+</span></div>
                </div>
                                <div class="right-message">
                                            <a href="https://bbs.csdn.net/forums/p-qq_33835307" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-messageboard">他的留言板
                        </a>
                                                            <a class="btn btn-sm  bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">关注</a>
                                    </div>
                            </div>
                    </div>
    </article>
    

什么是麦克纳姆轮