从零开始做一辆简易麦克纳姆轮小车

一、前期准备

麦克纳姆轮小车(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打印机打印非标准部件,或者按照设计切割木板或亚克力板。
安装电机:将电机固定到框架上,并确保麦克纳姆轮能自由旋转不受阻碍。
连接电子部件:按照电路图连接电机控制器、电池、微控制器和传感器。
上传代码:编写控制代码并在微控制器上运行测试。
调试:检查所有运动是否符合预期,调整代码以优化性能。
image
在这里我使用的芯片型号是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轮就可以斜向左前和右后方运动。
例:以小车车头为正方向,约定轮子前进时的方向为电机正转,轮子后退时的方向为电机反转。
image
拿A轮来说,辊子移动方向上由于滚动从而无法提供前进的力,而在辊子轴线方向上辊子无法滚动并且与地面摩擦产生辊子轴向上的摩擦力,即斜向右前或左后方向,从而A轮的速度方向是斜向右前或左后;同理可分析B轮。
image
根据我们高中学到的物理知识,我们知道速度是可以正交分解的,决定车体运动取决于四个麦轮的合速度方向。
那么A轮就可以分解成轴向向右和垂直轴向向前的速度分量,或者说轴向向左和垂直轴向向后的速度分量。
image
这样,B轮的速度分量和A轮互为镜像关系了。
image

三、麦克纳拉姆轮安装组合及运动分析

1.麦克纳姆轮正确安装组合

从上面我们可以知道麦克纳姆轮主要分为A,B轮,知道对应轮子的速度分量后我们就可以对四轮的麦克纳姆轮底盘进行排列组合,例如:AABB,AAAA,BBBB等,但是通过速度分量分析我们可以得知并不是每一种组合都可以实现全方向移动,在这里我列出麦克纳姆轮一种错误的安装组合,其余可以自行推理,例如错误例子AAAA型:左图为俯视图,右图为麦轮与地面接触的部分,投影到地面的情况!
image
由图我们可以得知:当四个轮子同时向前转动的时候,每个轮子都会有一个向左的速度分量,这样就会导致整个底盘前进的时候必然会同时向左运动;同理,后退的时候必然会向右运动,这样就没办法使用了,这个东西不受控制到处乱跑,这不是我们想要的全向移动。
image
所以我们可以通过组装正确方式,来确保四个轮子同时向前或者向后转动时,实现整个底盘前进或者后退,不发生其他偏移。
如下图所示,一种正确的安装组合:
image
当四个轮子都向前转的时候,AB轮可以相互抵消轴向速度,只剩下向前的速度,这样底盘就是向前直行、不会跑偏,后退也是一样的;
image
如果当A轮正转、B轮反转的时候,向前向后的速度会抵消,仅剩下向右的速度,那么底盘就会向右平移;相反,如果A轮反转,B轮正转,就会向左平移;其他方向都可以自行推理,我在上一篇已经列出常见麦轮组合运动方向。
image
image

2.麦克纳姆轮常见运动方式

根据上面的原理,我们可以推理得出麦克纳姆轮常见的运动方式。

1.对角线方向

对角线方向02
对角线方向

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(PA10),RX接TX(PA9),VCC接5V,GND接GND

2.Cubemx配置

SYS:Debug设置成Serial Wire
image
RCC:配置外部高速晶振
image
配置时钟树:
image
配置八个GPIO口为输出模式:
image
配置UART:设置波特率为9600,因为蓝牙模块默认波特率为9600,开启NVIC中断接收信息。
image
image
配置完后最后点击 generate code 生成代码。

3.代码示例

(1)实现前进和后退

前进、后退

//前进
void goForward(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_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_SET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
}

//后退
void goBack(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_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_RESET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}

(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(下图绿色),这两种我都测试通过。
image
使用串口中断测试收发的数据,串口重映射设置:
image
重映射代码:

int fputc(int ch, FILE *f)
{      
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);  
	return ch;
}

中断接收信息代码:

// 串口接收缓存
uint8_t buf=0;

// 定义最大接收字节数
#define UART1_REC_LEN 200

// 接收缓冲,串口接收到的数据存放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];

//  接收状态
//  bit15,     接收完成标志
//  bit14,     接收到0x0d
//  bit13~0,   接收到的有效字节数目
uint16_t UART1_RX_STA=0;

#define SIZE 12

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"))
					{
						goForward();
						printf("goForward success\r\n");
					}
					else if(!strcmp(UART1_RX_Buffer, "M2"))
					{
						goBack();
						printf("goBack success\r\n");
					}
					else if(!strcmp(UART1_RX_Buffer, "M3"))
					{
						goLeft();
						printf("goLeft success\r\n");
					}
					else if(!strcmp(UART1_RX_Buffer, "M4"))
					{
						goRight();	
						printf("goRight success\r\n");
					}
					else
					{
						if(UART1_RX_Buffer[0] != '\0')
						{
							stop();
							printf("Ö¸Áî·¢ËÍ´íÎó£º%s\r\n", UART1_RX_Buffer);
						}
					}
					
					memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
					UART1_RX_STA = 0;
				}
				else
					// 否则认为接收错误,重新开始
					UART1_RX_STA = 0;
			}
			// 如果没有收到了 0x0d (回车)
			else	
			{
				// 则先判断收到的这个字符是否是 0x0d (回车)
				if(buf == 0x0d)
				{
					// 是的话则将 bit14 位置为1
					UART1_RX_STA |= 0x4000;
				}
				else
				{
					// 否则将接收到的数据保存在缓存数组里
					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
					UART1_RX_STA++;
					
					// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
					if(UART1_RX_STA > UART1_REC_LEN - 1)
						UART1_RX_STA = 0;
				}
			}
		}
		// 重新开启中断
		HAL_UART_Receive_IT(&huart1, &buf, 1);
	}
}

在main.c函数中记得开启接收中断

// 开启接收中断
HAL_UART_Receive_IT(&huart1, &buf, 1);

手机打开蓝牙助手,记得把发送新行勾上,不然中断接收不到数据。
image
最后就可以实现手机通过蓝牙模块控制小车运动:
image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749576.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniApp获取实时定位

通过你获取的key放到项目manifest.json里面&#xff0c;对应填写你所需要的key值&#xff0c;还有高德用户名 用户名&#xff1a; key值的位置&#xff1a; 代码&#xff1a; html: <view class"intList pdNone"><view class"label">详细地…

fpga bitstream userid

fpga version register # xdc 文件 set_property BITSTREAM.CONFIG.USERID "0xDEADC0DE" [current_design] set_property BITSTREAM.CONFIG.USR_ACCESS 0x66669999 [current_design]ug908 在bit下载之后的property可以看到 &#xff0c;GUI里面Tools → Edit Devic…

链动2+1奖金制度|2+1玩法走人留人机制|电商分销模式

用这个链动21模式&#xff0c;半年清空42万瓶白酒库存&#xff0c;还帮你迅速搭建个人团队。 这是一次彻底的玩法拆解&#xff0c;一步步教你如何用这个模式实现销量和收益双提高&#xff01; 链动21模式是当前裂变会员发展速度最快的一种方式&#xff0c;它具备合理合规的身份…

[创业之路-131] :制造业企业的必备管理神器-ERP-ERP常见单据

目录 一、采购管理的ERP常见单据 1.1 请购单&#xff1a; 主要内容 作用 操作流程 1.2 采购订单&#xff08;Purchase Order, PO&#xff09;&#xff1a; 1.3 采购合同&#xff08;Purchase Contract&#xff09;&#xff1a; 1.4 采购发票&#xff08;Purchase Invoi…

通过验证邮箱进行注册信息确认

应用在进行注册时&#xff0c;避免恶意攻击和垃圾注册&#xff0c;可以通过验证注册者身份后才能够提交。一般可以使用验证手机短信或者验证邮箱&#xff0c;验证短信会有专门的第三方服务&#xff0c;可以进行付费购买。验证邮箱的正确与否&#xff0c;可以通过以下2种方式进行…

一张顶20张H100,速度10倍于B200:史上最快AI芯片,华人制造

在谈到 AI、大模型、算力等关键词时&#xff0c;如果要提及硬件产品&#xff0c;很多人应该会不假思索的说出英伟达。的确&#xff0c;在全球都缺算力的环境下&#xff0c;英伟达的地位是独特又难以撼动的。然而就在近日&#xff0c;有一家公司带着自己的 AI 芯片来叫板了。昨天…

海南聚广众达电子商务咨询有限公司抖音开店靠谱吗?

在当今数字化时代&#xff0c;电商行业迅猛发展&#xff0c;抖音作为短视频平台的佼佼者&#xff0c;其电商功能也日益凸显出其巨大的商业价值。海南聚广众达电子商务咨询有限公司&#xff0c;凭借其专业的电商服务能力和对抖音平台的深入理解&#xff0c;成为众多品牌进军抖音…

C++实现一个简单的Qt信号槽机制

昨天写这个文章《深入探讨C的高级反射机制&#xff08;2&#xff09;&#xff1a;写个能用的反射库》的时候就在想&#xff0c;是不是也能在这套反射逻辑的基础上&#xff0c;实现一个类似Qt的信号槽机制&#xff1f; Qt信号槽机制简介 所谓的Qt的信号槽&#xff08;Signals …

多维度mysql性能优化手段实践

数据库优化维度有四个:硬件升级、系统配置、表结构设计、SQL语句及索引。 优化选择: 优化成本:硬件升级>系统配置>表结构设计>SQL语句及索引。 优化效果:硬件升级<系统配置<表结构设计<SQL语句及索引。 系统配置优化 保证从内存中读取数据 MySQL会在内…

Open3d 点云投影到 xoy yoz 平面最简单的方式(附python 代码)

最简单的方式&#xff0c;就是直接把原有的点云的数据的 z or x 赋值为0, 然后生成一个新的点云。 filename_model1 r"1.pcd"down 10point_cloud o3d.io.read_point_cloud(filename_model1) point_cloud point_cloud.uniform_down_sample(int(down)) print(降采样…

Java对象集合按照指定元素顺序排序

需求背景 最近在对一个集合列表的数据进行排序&#xff0c;需求是要集合数据按照一个排序状态值进行排序&#xff0c;而这个状态值&#xff0c;不是按照从小到大这样的顺序排序的&#xff0c;而是要按照特定的顺序&#xff0c;比如按照1, 0, 2的顺序排的&#xff0c;所以需要自…

Attention步骤

一个典型的Attention思想包括三部分&#xff1a;Qquery、Kkey、Vvalue。 Q是query&#xff0c;是输入的信息&#xff1b;key和value成组出现&#xff0c;通常是原始文本等已有的信息&#xff1b;通过计算Q与K之间的相关性a&#xff0c;得出不同的K对输出的重要程度&#xff1b;…

智慧公厕系统在办公楼卫生管理中的作用,高效、便捷、智能

在现代化的办公楼中&#xff0c;卫生管理是营造舒适、高效工作环境的重要环节。而智慧公厕系统的引入&#xff0c;正以其高效、便捷、智能的特点&#xff0c;为办公楼的卫生管理带来了革命性的变革。 一、智慧公厕系统首先展现出了令人瞩目的高效性。 传统的公厕管理往往依赖人…

【zabbix】zabbix四大监控方式

zabbix四大监控方式 zabbix四大监控方式1、 Agent2、 SNMP3、IPMI4、JMX 设置 zabbix-snmp 监控 zabbix监控tomcat的jvm内存1.介绍Zabbix Java Gateway 主要功能使用场景 2.Zabbix Java Gateway 配置步骤**3.在被控端的tomcat上开启jvm监控**4.在zabbix-server上添加监控4.1.添…

Codeforces Round 954 (Div. 3) (A~F)(不会数学)

A - X Axis 暴力枚举一下所有可能 void solve() {int a , b , c;cin >> a >> b >> c;int ans 100;for(int i 0 ; i < 10 ; i ){ans min(ans , abs(i - a) abs(i - b) abs(i - c));} cout << ans << endl; } B - Matrix Stabiliz…

Python魔法参数:深入解析*args和**kwargs的强大用途

目录 引言 基础概念解析 *args:处理位置参数 **kwargs:处理关键字参数 *args和**kwargs的实际应用场景 1. 函数装饰器中使用*args和**kwargs 2. 类构造函数中使用*args和**kwargs 3. API调用中使用**kwargs 与其他参数类型的结合使用 结合默认参数 位置参数与关键…

第5讲:建立自己的C函数库,js调用自己写的C/C++函数,并包含依赖C/C++第三方静态库。

在javascript中&#xff0c;Array有很多内置的功能&#xff0c;比如Array.map&#xff0c;Array.filter&#xff0c;Array.find等等&#xff0c;能用内置的功能就用内置的功能&#xff0c;最好不要自己实现一套&#xff0c;因为底层调用的可能压根就不是js语言本身&#xff0c;…

从零开始了解GPT-4o模型:它是如何工作的?

人工智能&#xff08;AI&#xff09;技术正以惊人的速度发展&#xff0c;其中最引人注目的是OpenAI发布的GPT-4o模型。作为GPT系列的新成员&#xff0c;GPT-4o在多模态输入处理和响应速度上取得了重大进展。本文将深入探讨GPT-4o的工作原理&#xff0c;帮助您全面了解这一尖端A…

【教程】DPW 325T FPGA板卡程序下载与固化全攻略

到底什么是固化&#xff1f;&#xff1f;&#xff1f; 在开发板领域&#xff0c;"固化"通常指的是将软件或操作系统的镜像文件烧录&#xff08;Flash&#xff09;到开发板的存储介质上&#xff0c;使其成为开发板启动时加载的系统。这个过程可以确保开发板在启动时能…

Java日志 - JUL

一、JUL学习总结 &#xff08;1&#xff09;总结 JDK自带的日志系统中已经为我们创建了一个顶层的RootLogger&#xff0c;可以针对这个顶层的RootLogger设置多个Handler&#xff08;如ConsoleHandler, FileHandler等&#xff09;&#xff0c;如果想在控制台输出debug级别以上的…