一、前期准备
能实现全向移动的机器人类型里,有一种是麦克纳姆轮小车,也就是Mecanum wheel robot,它的关键之处在于运用了经过特殊设计的麦克纳姆轮。要是打算从最开始动手制作一辆麦克纳姆轮小车,那你或许得准备下面这些组件以及工具:
1. 材料和部件
选择恰当尺寸以及承载能力的麦克纳姆轮,这些轮子常常是由橡胶或者塑料制作而成的,并且带有能够自由旋转的滚轮,能够在专业的机器人配件商店抑或是在线市场进行购买。
需依据设计所需,挑选步进电机或者伺服电机作为电机。电机具备的扭矩连同转速,应当跟麦克纳姆轮的规格一致。比如说哒,针对小型机器人而言哟,能够采用NEMA17步进电机。
若是针对电机控制器情形,当使用步进电机时,那就得要有对应的驱动器,像A4988模块这样的;要是使用伺服电机的话,便需要ESC电子调速器。务必要保证控制器能够处理所需的电流及其电压。
电池,要依据电机、以及控制板对电源的要求,方可选择恰当契合情况的锂电池或者镍氢电池哟。容量须得充足,进而能够支持预期设定的操作时间呢。
开发板当中,像Arduino UNO、Nano,或者Raspberry Pi这类,是微控制器常见被选用范围。这些开发板作用是去把传感器数据拿来收受,并且对电机予以控制。
搭建车体的结构框架,能够选用木板,或者是亚克力板,又或者是3D打印部件,做出这样一种用于构建车体的选择。在进行设计期间,需要将强度以及重量纳入考量的范围之内。
传感器,其中超声波传感器被用于避障,而编码器能够用于轮速反馈,以此来提高运动精度 。
其他电子元件:包括电线、插座、电阻、LED指示灯等。
2. 工具
螺丝刀:一套交叉和一字螺丝刀,适用于不同大小的螺丝。
烙铁和焊锡:用于电子元件的焊接工作,确保连接牢固可靠。
剥线钳和剪刀:用于准备电线和切割材料。
尺子和标记笔:测量和标记材料以便准确切割。
锯子或激光切割机:根据所选材料类型进行切割。
3D打印机:如果有3D设计文件,可以直接打印复杂的零件。
螺丝和螺母套装:用于组装结构的各种尺寸螺丝和螺母。
3. 软件
编程环境方面,Arduino IDE被用来编写代码并上传至Arduino板,PyCharm或者Thonny用于Raspberry Pi的Python编程,Keil5 以及 Cubemx用于单片机编程。
用于设计以及修改车辆结构部件的 CAD 软件,像 AutoCAD 或者 SolidWorks 这样 。
诸如 Arduino 的 Stepper 这样的固件库,或者 Servo 库,把电机控制代码的编写予以简化 。
4. 组装步骤
创建设计构架,于CAD软件里边开展车体构架的设计工作,将全部部件的布局纳入考量范围之内,。
打印部件,利用3D打印机去打印不是标准规格的部件,制造部件,依照设计对木板切割,或者对亚克力板切割 。
进行电机安装操作:把电机安放在框架之上,并且要保证麦克纳姆轮能够自由自在地进行旋转,不会受到哪怕是丝毫的阻碍 。
按照电路图所呈现样式,将电机控制器与电池相连,把微控制器和传感器进行连接,完成电子部件连接相关操作。
上传代码:编写控制代码并在微控制器上运行测试。
调试:检查所有运动是否符合预期,调整代码以优化性能。
这款芯片的型号,也就是我在此处运用的,是STM32F103C8T6,编程的环境呢,是Keil5以及Cubemx,电机驱动那模块是TB6612,蓝牙模块则是HC - 05或者HC - 08,地板轮子还有电机,在网上买便可。
关于电机驱动基础知识,具体的内容可去查看我之前所撰写的文章,链接为:https://blog.csdn.net/m0_74712453/article/details/140008931?spm=1001.2014.3001.5501 ,句号。
二、麦克纳姆轮工作原理
首先,麦克纳姆轮在于轮子上小滚子有着独特布局以及角度,这是其核心原理所在,这些小滚子能把轮毂传来的力,分解成水平方向以及垂直于滚子轴线方向的两个分力。其次呢,对四个麦克纳姆轮的相对速度与方向进行巧妙控制,就能达成车辆在不改变车体方向的状况下,朝着任意方向移动的目的。比如说,当所有轮子都朝着前方旋转时,若相邻轮子的旋转方向是相反的,那么车辆就会侧向滑动;要是对角线上的轮子旋转方向一致,那就能够实现原地旋转。
之前提及麦轮分为 A,B 两种,要是 A 轮向前行进之际同时向右方行进,也就是朝着斜向右前方行进,于是反过来讲,A 轮向后移动的同时会朝着左方移动,也就是朝着斜向左后方移动;相对应地,B 轮能够朝着斜向左前以及右后方移动。
把小车车头当作正方向,规定轮子向前行进之方向是电机正向转动,轮子向后倒退之方向是电机反向转动 。
就拿A轮来讲,在辊子移动的方向那里,因为滚动所以没办法提供向前行进的力,然而在辊子轴线的方向上,辊子不能够滚动,并且跟地面摩擦进而产生辊子轴向上的摩擦力,也就是斜向右边靠前或者左边靠后的方向,这样一来A轮的速度方向就是斜向右边靠前或者左边靠后;同样的道理能够对B轮作分析。
依据我们于高中阶段所学到的 Physics 知识,身为我们所知悉的一件事情,速度具备能够实行正交分解的这样一种特性,而对于车体运动起到决定性作用的因素,乃是四个麦轮的合速度方向 。
那么,A轮能够被分解,分解为轴向朝着右边以及垂直轴向朝着前方的速度分量,又或者说,分解为轴向朝着左边以及垂直轴向朝着后方的速度分量。
这样,B轮的速度分量和A轮互为镜像关系了。
三、麦克纳拉姆轮安装组合及运动分析
1.麦克纳姆轮正确安装组合
从上面能够明白麦克纳姆轮主要分成A、B轮,知晓对应轮子的速度分量之后就能对四轮的麦克纳姆轮底盘进行排列组合,像:AABB、AAAA、BBBB等,然而借助速度分量分析能够晓得并非每一种组合都能够达成全方向移动,在此我列举麦克纳姆轮一种错误的安装组合,其余的可以自行去推理,比如错误例子AAAA型:左图是俯视图,右图是麦轮与地面接触的部分,投影到地面的情形!
从图能够知道,当那四个轮子一块儿向前进行转动之际,每一个轮子都会存在一个朝着左的速度分量,如此一来就会致使整个底盘在前进之时必定会同时朝着左运动;同样的道理,在后退的时候必然会朝着右运动,这般就无法得以使用了,这个物品不受控制地四处乱窜,这并非是我们所期望的全向移动。
那么我们是能够借由组装正确的方式,以此来保证当四个轮子同时往前或者往后转动之际,达成整个底盘向前或者向后运行,而且不会出现其他偏移情况 。
如下图所示,一种正确的安装组合:
在四个轮子均朝前转动之情状下,AB 轮能够彼此抵消轴向速度,仅余向前之速度,如此底盘便朝着前方径直行进且不会出现跑偏现象,往后退时亦是如此 。
要是在A轮朝着正方向转动、B轮朝着反方向转动之际,向前以及向后的速度将会相互抵消,仅仅留下来向右的速度,如此一来底盘便会朝着右边平行移动;与之相反地,要是A轮朝着反方向转动,B轮朝着正方向转动,那就会朝着左边平行移动;其他的方向都能够自己去进行推理,我于上一篇当中已经列举出常见的麦轮组合运动方向。
2.麦克纳姆轮常见运动方式
根据上面的原理,我们可以推理得出麦克纳姆轮常见的运动方式。
1.对角线方向
2.前进和后退
3.左移和右移
4.绕正前方左右摆尾
5.绕正后方左右摆尾
6.绕左右上角转动
7.绕左右下角转动
四、软件程序代码编写

我们在这儿,先达成蓝牙操控小车能够向前行进、向后倒退、向左移动、向右移动的效果,具体所关联到的编码器以及pid的知识,后续等我有空闲时间的时候,会另外撰写一篇来进行一番总结 。
1.接线
电机驱动模块与单片机对应引脚接线:
AIN1,AIN2 对应 PB2,PB10 左上轮
BIN1,BIN2 对应 PB1,PB0 左下轮
DIN1,DIN2 对应 PB5,PB6 右上轮
CIN1,CIN2 对应 PB8, PB7 右下轮
HC-08蓝牙模块与单片机接线:
蓝颜模块被Tx所连接的是RX呀,其TX所对应的是PA10引脚,而RX连接的是接TX呀,其对应的乃是PA9引脚,VCC要连接到5V那里,与此同时GND所进行连接的是GND 。
2.Cubemx配置
SYS:Debug设置成Serial Wire
RCC:配置外部高速晶振
配置时钟树:
配置八个GPIO口为输出模式:
对于UART进行配置,将波特率设定为9600,这是鉴于蓝牙模块其默认的波特率就是9600,随后开启NVIC中断以此来接收相应信息。
配置完后最后点击 generate code 生成代码。
3.代码示例
(1)实现前进和后退
//前进void goForward(void){要左轮向前行进,对于左上轮而言,涉及PB2以及PB10,而对于左下轮来说,涉及PB1以及PB0 。将GPIOB端口的GPIO_PIN_2引脚,设置为GPIO_PIN_SET状态,通过HAL_GPIO_WritePin函数来体现。对GPIOB端口其GPIO_PIN_10引脚,执行HAL_GPIO_WritePin函数,将该引脚电平设置为GPIO_PIN_RESET 。将GPIOB端口的GPIO_PIN_0引脚,设置为GPIO_PIN_SET状态写成HAL_GPIO_WritePin函数调用语句 。向 GPIOB 输入,将 GPIO_PIN_1 设为一种状态,使得 HAL_GPIO_WritePin 执行,朝着 GPIO_PIN_RESET 进行操作 。右轮朝着前方行进,右上轮对应的是PB5以及PB6,右下轮对应的是PB8还有PB7 。HAL_GPIO_WritePin函数,将GPIOB端口的GPIO_PIN_5引脚,设置为GPIO_PIN_SET状态 。霍尔通用输入输出端口写入引脚,在通用输入输出端口B中,针对引脚6,执行引脚重置操作,使其处于相应电位状态。让通用输入输出端口B的引脚7处于置位状态,通过执行HAL_GPIO_WritePin函数来达成这一操作 。将GPIOB端口的GPIO_PIN_8引脚,写入GPIO_PIN_RESET状态,由HAL_GPIO_WritePin函数来执行 。}//后退void goBack(void){// 左轮朝着后方退去,左上轮涉及PB2以及PB10,左下轮关联PB1还有PB0 。HAL_GPIO_WritePin向GPIO端口B写入引脚值,该引脚值为GPIO_PIN_RESET,所操作的引脚编号是GPIO_PIN_2 。HAL_GPIO_WritePin函数调用,其参数为GPIOB以及GPIO_PIN_10,输出状态设为GPIO_PIN_SET ,。调用一个名为HAL_GPIO_writePin的函数,该函数的第一个参数是GPIOB, 第二个参数是GPIO_PIN_0, 并将第三个参数设置为GPIO_PIN_RESET,以此来实现特定的操作,操作有着其重要的意义,操作是基于特定的。使通用输入输出端口B的引脚1处于置位状态,通过HAL库函数HAL_GPIO_WritePin来实现 。右轮向后退,右上轮涉及PB5与PB6,右下轮关联PB8以及PB7 。把名为 HAL_GPIO_WritePin 的函数,作用于 GPIOB,针对 GPIO_PIN_5 这个引脚进行操作结果为 GPIO_PIN_RESET 的写入 。使通用输入输出端口B的引脚6处于置位状态,通过主控制器的相关函数来达成这个操作 。HAL_GPIO_WritePin函数调用,其参数为GPIOB,再一个参数是GPIO_PIN_7,最后一个参数是GPIO_PIN_RESET 。将GPIOB端口的GPIO_PIN_8引脚,设置为GPIO_PIN_SET状态,通过执行HAL_GPIO_WritePin函数 。}
(2)实现左移和右移
//左移void goLeft(void){向左上方的轮子往后退,这个向左上方的轮子涉及PB2以及PB10,左下方的轮子向前行进,此左下方的轮子关联PB1和PB0 。HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);右上轮朝着前方行进,右上轮关联着PB5与PB6之处,右下轮朝着后方退行,右下轮关联着PB8与PB7之处。HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);}//右移void goRight(void){往左上方向的轮子向前行进,该左上位的轮子涉及PB2以及PB10,处于左下方的轮子向后退,此左下侧的轮子关联PB1和PB0 。HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);处于右上方位置的轮子向后退,此右上方的轮子借助PB5以及PB6进行控制,处于右下方位置的轮子向前行进,该右下方的轮子依靠PB8以及PB7来操控。HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);}
(3)蓝牙接收信息
能够运用HC - 05(如下图示蓝色的那个),或者运用HC - 08(如下图示绿色的那个),这两种我针对其都进行了测试且获得通过。
使用串口中断测试收发的数据,串口重映射设置:
重映射代码:
int fputc(int ch, FILE *f){unsigned char temp[1]={ch};HAL_UART_Transmit,针对huart1来进行 transmit操作,所涉及的对象是temp ,。1,0xffff);return ch;}
中断接收信息代码:
// 串口接收缓存uint8_t buf=0;// 定义最大接收字节数串口接收到的数据,会存放在接收缓冲里,此数据被放置于一个数组之中,该数组最大有UART1_REC_LEN个字节,是用于存接收到的数据的接收缓冲 。uint8_t包含在UART1 _ REC_LEN中的UART1 _ RX _ Buffer,有这样的存在形式 。 其中各项安排归属特定规则类别。// 接收状态// bit15, 接收完成标志// bit14, 接收到0x0d// bit13~0, 接收到的有效字节数目uint16_t UART1_RX_STA=0;char buffer[SIZE];// 接收完成回调函数,收到数据后在这里处理void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){// 判断中断是有哪个串口触发的if(huart->Instance == USART1){判断,接收,是否,完成,查看,UART1_RX_STA的,bit15位,是不是,为1 ,。if((UART1_RX_STA & 0x8000) == 0){// 如果已经收到了 0x0d (回车)if(UART1_RX_STA & 0x4000){// 则接着判断是否收到 0x0a (换行)if(buf == 0x0a){要是收到了 0x0a,并且收到了 0x0d,那么就把 bit15 这个位置设为 1 。UART1_RX_STA进行了或等于操作,操作对象是,某个未明确给出的内容 。0x8000;// 控制小车运动方向指令if(!strcmp(UART1_RX_Buffer, "M1")){朝前走去;进行向前的动作;朝着前方的态势发展以便前行 。printf("goForward success\r\n");}else if(!strcmp(UART1_RX_Buffer, "M2")){执行返回操作,行动朝着回去的方向,进行返回的动作 。printf("goBack success\r\n");}else if(!strcmp(UART1_RX_Buffer, "M3")){朝左边行进;执行向左的动作;进行向左的行为 。printf("goLeft success\r\n");}else if(!strcmp(UART1_RX_Buffer, "M4")){往右边去;进行向右的动作了;作出向右行进的行为了 。printf("goRight success\r\n");}else{if(UART1_RX_Buffer[0] != '\0'){停下;使停止(实施相应影响或改变);终止(进行中的事情或行动) 。printf("Ö¸Áî·¢ËÍ´íÎó£º%s\r\n", UART1_RX_Buffer);}}memset(UART1_RX_Buffer, 0, UART1_REC_LEN);0;}else// 否则认为接收错误,重新开始UART1_RX_STA = 0;}// 如果没有收到了 0x0d (回车)else{// 则先判断收到的这个字符是否是 0x0d (回车)if(buf == 0x0d){// 是的话则将 bit14 位置为1UART1_RX_STA |= 0x4000;}else{// 否则将接收到的数据保存在缓存数组里不太明确你提供的内容完整意图,仅从这部分看不是一个完整表意的句子改写。如果这些文字是从某个代码片段中截取的关键部分,比如是一段关于某个特定硬件相关的代码变量引用,那可以这样改写:UART1_RX_Buffer,。0X3FFF] = buf;UART1_RX_STA施行自增操作,使其数值增加1 。若所接收的数据,比UART1_REC_LEN(此为200字节)要大,那么就再次开启接收 。if(通用异步收发传输器1接收状态大于通用异步收发传输器1接收长度减去 ) , ( 这里的表述似乎不完整。1)通用异步收发传输器 1 的接收状态等于 ,(这里似乎原句不完整,可自行补充完整后再说)。0;}}}// 重新开启中断HAL_UART_Receive_IT,针对huart1,将buf与之关联,。1);}}
在main.c函数中记得开启接收中断
// 开启接收中断硬件抽象层通用异步收发传输器接收中断,针对串口1,将数据接收至缓冲区给定数量字节,这里给定的值是,。1);
打开手机里的蓝牙助手,要记着把发送新行勾选上,不然要是中断了就接收不到数据了。
最后就可以实现手机通过蓝牙模块控制小车运动: