查看原文
其他

动手智能小车记(5)-坦克底盘硬件模块大杂烩

杨源鑫 嵌入式云IOT技术圈 2021-01-31

关于小车,之前也写了好几期了,先给大家放一段视频,后面打算将机械臂集成到坦克上,目前还在调试中,夹具已经能够控制夹紧和松开了:

上次那辆小车的底盘实在是脆弱,一不小心就撞碎了,后面直接上了个坦克底盘,在基于坦克底盘的小车上,控制坦克行走和之前小车的方法一致,采用的是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学习笔记获取,本着分享原则,本资源从网上搜索获取,如有违规,请联系我删除,谢谢!

往期精彩

STM32在线升级OTA,看这一篇就够啦~

第10期 | ringbuff,通用FIFO环形缓冲区实现库

ESP8266实战贴:使用HTTP POST请求上传数据到公有云OneNet

会C/C++就可以开发Linux/Android应用程序?替代传统串口屏的Yoxios了解一下!

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存