关于小车,之前也写了好几期了,先给大家放一段视频,后面打算将机械臂集成到坦克上,目前还在调试中,夹具已经能够控制夹紧和松开了:
上次那辆小车的底盘实在是脆弱,一不小心就撞碎了,后面直接上了个坦克底盘,在基于坦克底盘的小车上,控制坦克行走和之前小车的方法一致,采用的是PWM直驱,通过改变占空比的形式让坦克前进、后退、左右转。此次加上各种模块,分别如下:
- 激光瞄准头
- 蜂鸣器报警(因为还没买电磁炮,所以暂时用来代替发射)
- 步进电机(用来调节发射瞄准角度)
- MG996R舵机(打算做成360°旋转避障,据说会影响精度,还没试)
- 超声波模块SR04
- 游戏摇杆模块控制
一、激光瞄准头
这个没什么好说的,就跟点灯一样,高电平打开激光射线,低电平关闭激光射线,但我总觉得这个太暗了,改天一定要买一个亮度更大,效果看起来更明显的。
二、蜂鸣器
这里选用的是有源蜂鸣器,也是跟点灯一样,高低电平控制,当小车收到SHOT
指令时,发射炮弹,用蜂鸣器高电平的响声暂时代替,后续替换成电磁炮。
三、步进电机的控制
电机控制是一门非常高深的学问,如果想去走工控行业需要玩到电机方面的,那么步进电机一定少不了,不管怎么说,我们还是可以把它驱动起来的,以下是我买的一个步进电机驱动模块:
步进电机选用的型号是:28BYJ48-H12
这里在软件编程上有一个比较重要参数需要了解一下,就是步距角。
那么什么是步距角呢?度娘给你答案,可以详细看看。
https://baike.baidu.com/item/%E6%AD%A5%E8%B7%9D%E8%A7%92/5946465?fr=aladdin
来看看下面这个换算公式,或许你就明白了,如上图所示,步距角=5.625°/64,意思就是每64个脉冲步进电机就会转5.625度,因此我们很容易得出以下计算公式:电机转一圈有360°,那么转一圈的脉冲数 = 360 / 5.625 * 64 = 4096 个脉冲。进而很容易得到以下角度与脉冲的简单算法:
/* Rotation_Angle:旋转角度 返回:Motor_Pulse 根据公式计算得出的脉冲个数 */ int Motor_Angle_Cal(int Rotation_Angle) { if(Rotation_Angle < 0 || Rotation_Angle > 360) return -1 ; Motor_Pulse = (int)((double)(Rotation_Angle / 5.625) * 64) ; return Motor_Pulse ; }
关于详细配置和程序编写,之前在CSDN博客上也已经做过类似的笔记,这里就不重复写了:
https://yangyuanxin.blog.csdn.net/article/details/100901635
四、MG996R舵机控制
关于舵机控制,我想世伟之前写的文章介绍就已经非常详细了,链接如下:
STM32Cube-21(补充) | 使用通用定时器产生PWM驱动舵机
这里的舵机我让它以固定频率进行360旋转,这个效果感觉像激光雷达哈哈哈,有时间一定买个来玩玩。
五、超声波模块SR04
主要用来实现测距避障用,引用之前学习32时听了温老师的课,这是他的课堂笔记,我觉得下面这个图理解起来超级通俗易懂了,文末回复关键字获取。
六、游戏摇杆模块控制
根据模块提供的手册,下面来了解下工作原理以及如何来应用
看到这里我们就明白了,x,y是模拟量,而z是一个二值数据,在这里,可以利用STM32的ADC控制器来读取X,Y的输出,Z轴就很简单了,把它当作普通按键就可以了,关于ADC介绍,温老师的笔记值得收藏,通俗易懂。
关于游戏摇杆模块控制小车,其实只要把摇杆方向的AD值测出来,然后写个简单的函数进行区分就可以了,以下是我实测的控制数据,可能并不是特别精准,但个人觉得够用了,后期也可以进行优化:
//通过DMA通道转换得到的值 uint32_t JOY_VALUE[2]; //保存转换计算后的电压值 __IO float ADC_VOL_VALUE[2]; #define UP 0 #define DOWN 1 #define LEFT 2 #define RIGHT 3 #define LEFT_UP 4 #define LEFT_DOWN 5 #define RIGHT_UP 6 #define RIGHT_DONW 7 #define ENTER 8 #define UNKNOW_KEY 99 //获取摇杆键值 uint8_t GET_KEY_VALUE(int x, int y, int z) { if((0 == x) && (y > 50 && y < 255)) return UP; else if((255 == x) && (y > 0 && y < 255)) return DOWN ; else if((x > 0 && x < 255) && (255 == y)) return LEFT ; else if((x > 0 && x < 255) && (0 == y)) return RIGHT ; else if((0 == x) && (255 == y)) return LEFT_UP ; else if((255 == x) && (255 == y)) return LEFT_DOWN ; else if((0 == x) && (0 == y)) return RIGHT_UP ; else if((255 == x) && (0 == y)) return RIGHT_DONW ; else if(x > 0 && y > 0 && 0 == z) return ENTER ; return UNKNOW_KEY ; }
要控制小车,那么就要把WIFI和摇杆模块的控制绑定起来,首先肯定是要让WIFI进入透传模式,然后通过WIFI发送控制指令给小车,核心代码如下:
int main(void) { /* USER CODE BEGIN 1 */ int JOY_X, JOY_Y, JOY_Z ; uint8_t key_value ; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); MX_ADC1_Init(); /* USER CODE BEGIN 2 */ ESP8266_Init(); printf("正在配置 ESP8266 ......\n" ); if(ESP8266_AT_Test()) { printf("AT test OK\n"); } printf("\n< 1 >\n"); if(ESP8266_Net_Mode_Choose(STA)) { printf("ESP8266_Net_Mode_Choose OK\n"); } printf("\n< 2 >\n"); while(!ESP8266_JoinAP(User_ESP8266_ApSsid, User_ESP8266_ApPwd)); printf("\n< 3 >\n"); ESP8266_Enable_MultipleId(DISABLE); while(!ESP8266_Link_Server(enumTCP, User_ESP8266_TcpServer_IP, User_ESP8266_TcpServer_Port, Single_ID_0)); printf("\n< 4 >\n"); while(!ESP8266_UnvarnishSend()); printf("配置 ESP8266 完毕\n"); HAL_ADC_Start_DMA(&hadc1, JOY_VALUE, 2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //3.3V为AD转换的参考电压值,STM32的AD转换为12bit,所以2^12=4096 //故当输入参考电压为3.3V时,AD转换的结果为4096 //取12bit数值 //ADC_VOL_VALUE[0] = (float)(JOY_VALUE[0] & 0xFFF) * 3.3 / 4096 ; //ADC_VOL_VALUE[1] = (float)(JOY_VALUE[1] & 0xFFF) * 3.3 / 4096 ; //printf("x:%.2fv y:%.2fv\n", ADC_VOL_VALUE[0], ADC_VOL_VALUE[1]); //只取8位有效数据 JOY_X = JOY_VALUE[0] & 0xFF; JOY_Y = JOY_VALUE[1] & 0xFF; JOY_Z = HAL_GPIO_ReadPin(JOY_Z_GPIO_Port, JOY_Z_Pin); key_value = GET_KEY_VALUE(JOY_X, JOY_Y, JOY_Z); //printf("x:%d y:%d z:%d\n", JOY_X, JOY_Y, JOY_Z); switch(key_value) { //小车前进 case UP : printf("上\n"); ESP8266_SendString(ENABLE,"GO",strlen("GO"),0); break ; //小车后退 case DOWN : printf("下\n"); ESP8266_SendString(ENABLE,"BACK",strlen("BACK"),0); break ; //小车左转 case LEFT : printf("左\n"); ESP8266_SendString(ENABLE,"LEFT",strlen("LEFT"),0); break ; //小车右转 case RIGHT : printf("右\n"); ESP8266_SendString(ENABLE,"RIGHT",strlen("RIGHT"),0); break ; //控制步进电机向右旋转45° case LEFT_UP: printf("左上\n"); ESP8266_SendString(ENABLE,"MOTOR_RADJ",strlen("MOTOR_RADJ"),0); break ; case LEFT_DOWN: printf("左下\n"); break ; //控制步进电机向左旋转45° case RIGHT_UP: printf("右上\n"); ESP8266_SendString(ENABLE,"MOTOR_LADJ",strlen("MOTOR_LADJ"),0); break ; case RIGHT_DONW : printf("右下\n"); break ; //控制发射炮弹 case ENTER : printf("确认\n"); ESP8266_SendString(ENABLE,"SHOT",strlen("SHOT"),0); break ; //控制小车停止 default: ESP8266_SendString(ENABLE,"STOP",strlen("STOP"),0); break ; } HAL_Delay(200); } /* USER CODE END 3 */ }
完整程序等我把小车DIY完,会把硬件模块、软件程序等全部开源,如需学习温老师的笔记,请在公众号后台回复STM32学习笔记
获取,本着分享原则,本资源从网上搜索获取,如有违规,请联系我删除,谢谢!
往期精彩
第10期 | ringbuff,通用FIFO环形缓冲区实现库
ESP8266实战贴:使用HTTP POST请求上传数据到公有云OneNet