• RT-Thread实战笔记-小白一看就会的平衡车教程(附源码)


    摘要

    小伙伴们,停更很久的RT-Thread实战笔记来啦,本章节教大家如何打造一个属于自己的平衡车,废话不多说,来吧,淦!!!

    主要RT-Thread内容

    • RT-Thread

    • PID

    • PWM

    • MPU6050

    • 编码器

    • 定时器

    • 线程

    模组介绍

    利用手中已经积灰多年的小模块,废物利用,打造一个专属的平衡车

    电机驱动模块

    某宝买的L298N电机驱动模组

    或者TB6612,关于这两个模组的介绍就不多说了,大家可以自行百度下哈

    陀螺仪

    陀螺仪选用的是用的比较多的[MPU6050],目前好像要停产了,价格也越来越贵

    电机

    电机采用的是带有编码器的直流减速电机,价格也略微贵一些

    电池


    主控

    RT-Thread ART-PI控制板

    亚克力板

    亚克力板也是自己设计的尺寸图分享给大家

    软件设计

    接线

    电机驱动接线:

    电机ART-PI
    PWMAPB0
    IN1H14
    IN2C7
    IN3G10
    In4I6
    PWMBPB1
    12V/
    5V5V
    GNDGND

    MPU6050接线

    MPU6050ART-PI
    3.3V3.3V
    GNDGND
    SCLPH11
    SDAPH12

    左电机与电机驱动模组:

    电机ART-PI电机驱动模组
    电机+/OUT1
    编码器电源-GND
    编码器APA8
    编码器BPA9
    编码器电源+3.3V/5V
    电机-/OUT2

    右电机与电机驱动模组:

    电机ART-PI电机驱动模组
    电机+/OUT3
    编码器电源-GND
    编码器APA1
    编码器BPA15
    编码器电源+3.3V/5V
    电机-/OUT4

    软件代码

    代码很多,主要介绍下,具体的大家可以看源码,源码都是开源的哈

    软件包只用了按键和MPU6050的软件包,IIC用的是PH11和PH12

    • MPU6050驱动

    移植的是DMP驱动,也可以用rt-thread软件包里面配置,我是自己移植过来的,也非常的简单,写好接口就可以了

    1. /**
    2.     * @author:小飞哥玩嵌入式-小飞哥
    3.     * @TODO:mpu6050初始化
    4.     * @param NULL
    5.     * @return NULL
    6. */
    7. void mpu_measurement_init(void)
    8. {
    9.     i2c_bus = (struct mpu6xxx_device *) mpu6xxx_init(MPU6050_I2C_BUS_NAME, MPU6050_ADDR); //初始化MPU6050,测量单位为角速度,加速度    while(count++)
    10.     rt_int8_t res = 1;
    11.     while (res)
    12.     {
    13.         res = mpu_dmp_init();
    14.         rt_kprintf("\r\nRES = %d\r\n",res);
    15.         rt_thread_mdelay(500);
    16.         rt_kprintf("\r\nMPU6050 DMP init Error\r\n");
    17.     }
    18.     rt_kprintf("\r\nMPU6050 DMP init OK\r\n");
    19. }
    20. /**
    21.     * @author:小飞哥玩嵌入式-小飞哥
    22.     * @TODO:MPU写入多字节数据
    23.     * @param NULL
    24.     * @return NULL
    25. */
    26. rt_uint8_t MPU_Write_Len(rt_uint8_t addr,rt_uint8_t reg,rt_uint8_t len,rt_uint8_t *databuf)
    27. {
    28.     rt_int8_t res = 0;
    29. #ifdef RT_USING_I2C
    30.     struct rt_i2c_msg msgs;
    31.     rt_uint8_t buf[50] = {0};
    32. #endif
    33.     buf[0] = reg;
    34.     for(int i = 0;i<len;i++)
    35.     {
    36.         buf[i+1]=databuf[i];
    37.     }
    38.     if (i2c_bus->bus->type == RT_Device_Class_I2CBUS)
    39.     {
    40.         msgs.addr  = i2c_bus->i2c_addr;    /* slave address */
    41.         msgs.flags = RT_I2C_WR;        /* write flag */
    42.         msgs.buf   = buf;              /* Send data pointer */
    43.         msgs.len   = len+1;
    44.         if (rt_i2c_transfer((struct rt_i2c_bus_device *)i2c_bus->bus, &msgs, 1) == 1)
    45.         {
    46.             res = RT_EOK;
    47.         }
    48.         else
    49.         {
    50.             res = -RT_ERROR;
    51.         }
    52.     }
    53. }
    54. /**
    55.     * @author:小飞哥玩嵌入式-小飞哥
    56.     * @TODO:mpu读取多字节数据
    57.     * @param NULL
    58.     * @return NULL
    59. */
    60. rt_uint8_t MPU_Read_Len(rt_uint8_t addr,rt_uint8_t reg,rt_uint8_t len,rt_uint8_t *buf)
    61. {
    62.     rt_int8_t res = 0;
    63. #ifdef RT_USING_I2C
    64.     struct rt_i2c_msg msgs[2];
    65. #endif
    66. #ifdef RT_USING_SPI
    67.     rt_uint8_t tmp;
    68. #endif
    69.     if (i2c_bus->bus->type == RT_Device_Class_I2CBUS)
    70.     {
    71.         msgs[0].addr  = i2c_bus->i2c_addr;    /* Slave address */
    72.         msgs[0].flags = RT_I2C_WR;        /* Write flag */
    73.         msgs[0].buf   = &reg;             /* Slave register address */
    74.         msgs[0].len   = 1;                /* Number of bytes sent */
    75.         msgs[1].addr  = i2c_bus->i2c_addr;    /* Slave address */
    76.         msgs[1].flags = RT_I2C_RD;        /* Read flag */
    77.         msgs[1].buf   = buf;              /* Read data pointer */
    78.         msgs[1].len   = len;              /* Number of bytes read */
    79.         if (rt_i2c_transfer((struct rt_i2c_bus_device *)i2c_bus->bus, msgs, 2) == 2)
    80.         {
    81.             res = RT_EOK;
    82.         }
    83.         else
    84.         {
    85.             res = -RT_ERROR;
    86.         }
    87.     }
    88.     return res;
    89. }

    然后对接到inv_mpu.c里面的接口函数

    • 电机驱动

    1. /**
    2.     * @author:小飞哥玩嵌入式-小飞哥
    3.     * @TODO:小车电机控制初始化
    4.     * @param NULL
    5.     * @return NULL
    6. */
    7. void rt_balanceCar_pinInit(void)
    8. {
    9.     rt_pin_mode(motor_A1, PIN_MODE_OUTPUT );
    10.     rt_pin_mode(motor_A2, PIN_MODE_OUTPUT );
    11.     rt_pin_mode(motor_B1, PIN_MODE_OUTPUT );
    12.     rt_pin_mode(motor_B2, PIN_MODE_OUTPUT );
    13.     rt_pin_write(motor_A1,PIN_LOW);
    14.     rt_pin_write(motor_A2,PIN_LOW);
    15.     rt_pin_write(motor_B1,PIN_LOW);
    16.     rt_pin_write(motor_B2,PIN_LOW);
    17. }
    18. /**
    19.     * @author:小飞哥玩嵌入式-小飞哥
    20.     * @TODO:小车左轮前进
    21.     * @param NULL
    22.     * @return NULL
    23. */
    24. void rt_balanceCar_LeftMotorforward(void)
    25. {
    26.     rt_pin_write(motor_B1,PIN_LOW);
    27.     rt_pin_write(motor_B2,PIN_HIGH);
    28. }
    29. /**
    30.     * @author:小飞哥玩嵌入式-小飞哥
    31.     * @TODO:小车左轮后退
    32.     * @param NULL
    33.     * @return NULL
    34. */
    35. void  rt_balanceCar_LeftMotorback(void)
    36. {
    37.   rt_pin_write(motor_B1,PIN_HIGH);
    38.   rt_pin_write(motor_B2,PIN_LOW);
    39. }
    40. /**
    41.     * @author:小飞哥玩嵌入式-小飞哥
    42.     * @TODO:小车右轮前进
    43.     * @param NULL
    44.     * @return NULL
    45. */
    46. void rt_balanceCar_RightMotorforward(void)
    47. {
    48.     rt_pin_write(motor_A1,PIN_LOW);
    49.     rt_pin_write(motor_A2,PIN_HIGH);
    50. }
    51. /**
    52.     * @author:小飞哥玩嵌入式-小飞哥
    53.     * @TODO:小车右轮后退
    54.     * @param NULL
    55.     * @return NULL
    56. */
    57. void  rt_balanceCar_RightMotorback(void)
    58. {   rt_pin_write(motor_A1,PIN_HIGH);
    59.     rt_pin_write(motor_A2,PIN_LOW);
    60. }
    61. /**
    62.     * @author:小飞哥玩嵌入式-小飞哥
    63.     * @TODO:小车整机前进
    64.     * @param NULL
    65.     * @return NULL
    66. */
    67. void rt_balanceCar_forward(void)
    68. {
    69.     rt_pin_write(motor_A1,PIN_HIGH);
    70.     rt_pin_write(motor_A2,PIN_LOW);
    71.     rt_pin_write(motor_B1,PIN_HIGH);
    72.     rt_pin_write(motor_B2,PIN_LOW);
    73. }
    74. /**
    75.     * @author:小飞哥玩嵌入式-小飞哥
    76.     * @TODO:小车整机后退
    77.     * @param NULL
    78.     * @return NULL
    79. */
    80. void rt_balanceCar_back(void)
    81. {
    82.     rt_pin_write(motor_A1,PIN_LOW);
    83.     rt_pin_write(motor_A2,PIN_HIGH);
    84.     rt_pin_write(motor_B1,PIN_LOW);
    85.     rt_pin_write(motor_B2,PIN_HIGH);
    86. }
    87. /**
    88.     * @author:小飞哥玩嵌入式-小飞哥
    89.     * @TODO:小车整机左转
    90.     * @param NULL
    91.     * @return NULL
    92. */
    93. void rt_balanceCar_turnLeft(void)
    94. {
    95.     rt_pin_write(motor_A1,PIN_LOW);
    96.     rt_pin_write(motor_A2,PIN_LOW);
    97.     rt_pin_write(motor_B1,PIN_HIGH);
    98.     rt_pin_write(motor_B2,PIN_LOW);
    99. }
    100. /**
    101.     * @author:小飞哥玩嵌入式-小飞哥
    102.     * @TODO:小车整机右转
    103.     * @param NULL
    104.     * @return NULL
    105. */
    106. void rt_balanceCar_turnRight(void)
    107. {
    108.     rt_pin_write(motor_A1,PIN_HIGH);
    109.     rt_pin_write(motor_A2,PIN_LOW);
    110.     rt_pin_write(motor_B1,PIN_LOW);
    111.     rt_pin_write(motor_B2,PIN_LOW);
    112. }
    113. /**
    114.     * @author:小飞哥玩嵌入式-小飞哥
    115.     * @TODO:小车电机停转
    116.     * @param NULL
    117.     * @return NULL
    118. */
    119. void rt_balanceCar_stop(void)
    120. {
    121.     rt_pin_write(motor_A1,PIN_LOW);
    122.     rt_pin_write(motor_A2,PIN_LOW);
    123.     rt_pin_write(motor_B1,PIN_LOW);
    124.     rt_pin_write(motor_B2,PIN_LOW);
    125. }
    • PWM控制

    1. /**
    2.     * @author:小飞哥玩嵌入式-小飞哥
    3.     * @TODO:pwm使能
    4.     * @param NULL
    5.     * @return NULL
    6. */
    7. void rt_balanceCar_pwmEnable(void)
    8. {
    9.     rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL3);
    10.     rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL4);
    11. }
    12. /**
    13.     * @author:小飞哥玩嵌入式-小飞哥
    14.     * @TODO:pwm失能
    15.     * @param NULL
    16.     * @return NULL
    17. */
    18. void rt_balanceCar_pwmDisable(void)
    19. {
    20.     rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL3);
    21.     rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL4);
    22. }
    23. /**
    24.     * @author:小飞哥玩嵌入式-小飞哥
    25.     * @TODO:pwm输出限幅
    26.     * @param pwm1
    27.     * @param pwm2
    28.     * @return NULL
    29. */
    30. void rt_balanceCar_pwmlimit(rt_int32_t *pwm1,rt_int32_t *pwm2)
    31. {
    32.     if(*pwm1 >= PWM_UPPER_LIMIT)
    33.     {
    34.         *pwm1 = PWM_UPPER_LIMIT;
    35.     }
    36.     else if(*pwm1 <= PWM_LOWER_LIMIT)
    37.     {
    38.         *pwm1 = PWM_LOWER_LIMIT;
    39.     }
    40.     if(*pwm2 >= PWM_UPPER_LIMIT)
    41.     {
    42.         *pwm2 = PWM_UPPER_LIMIT;
    43.     }
    44.     else if(*pwm2 <= PWM_LOWER_LIMIT)
    45.     {
    46.         *pwm2 = PWM_LOWER_LIMIT;
    47.     }
    48. }
    49. /**
    50.     * @author:小飞哥玩嵌入式-小飞哥
    51.     * @TODO:pwm设置
    52.     * @param channel1
    53.     * @param channel2
    54.     * @param L_speed
    55.     * @param R_speed
    56.     * @return NULL
    57. */
    58. void rt_balanceCar_pwmSet(rt_uint8_t channel1,rt_uint8_t channel2,rt_int32_t L_speed,rt_int32_t R_speed)
    59. {
    60.     //输出限幅
    61.     rt_balanceCar_pwmlimit(&L_speed,&R_speed);
    62.     //pwm设置
    63.     rt_pwm_set(pwm_dev, channel1, PWM_PERIOD_ABS(L_speed));
    64.     rt_pwm_set(pwm_dev, channel2, PWM_PERIOD_ABS(R_speed));
    65. /*
    66.     rt_pwm_enable(pwm_dev, channel1);
    67.     rt_pwm_enable(pwm_dev, channel2);
    68. */
    69. }
    70. /**
    71.     * @author:小飞哥玩嵌入式-小飞哥
    72.     * @TODO:pwm初始化
    73.     * @param NULL
    74.     * @return NULL
    75. */
    76. rt_int8_t rt_balanceCar_pwmInit(void)
    77. {
    78.     pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
    79.     if (pwm_dev == RT_NULL)
    80.     {
    81.         rt_kprintf("\r\npwm sample run failed! can't find %s device!\r\n"PWM_DEV_NAME);
    82.         return RT_ERROR;
    83.     }
    84.     rt_kprintf("\r\npwm sample run success! find %s device!\r\n"PWM_DEV_NAME);
    85.     //关闭PWM
    86.     //rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL3);
    87.     //rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL4);
    88.     //开启PWM
    89.     rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL3);
    90.     rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL4);
    91.     return RT_EOK;
    92. }
    • 编码器

    编码器驱动是把HAL库的驱动移植过来的,直接复制粘贴就可以了

    1. /* USER CODE END 0 */
    2. TIM_HandleTypeDef htim1;
    3. TIM_HandleTypeDef htim2;
    4. /* TIM1 init function */
    5. /**
    6.   * @brief TIM1 Initialization Function
    7.   * @param None
    8.   * @retval None
    9.   */
    10. static void MX_TIM1_Init(void)
    11. {
    12.   /* USER CODE BEGIN TIM1_Init 0 */
    13.   /* USER CODE END TIM1_Init 0 */
    14.   TIM_Encoder_InitTypeDef sConfig = {0};
    15.   TIM_MasterConfigTypeDef sMasterConfig = {0};
    16.   /* USER CODE BEGIN TIM1_Init 1 */
    17.   /* USER CODE END TIM1_Init 1 */
    18.   htim1.Instance = TIM1;
    19.   htim1.Init.Prescaler = 0;
    20.   htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
    21.   htim1.Init.Period = 65535;
    22.   htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    23.   htim1.Init.RepetitionCounter = 0;
    24.   htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    25.   sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
    26.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
    27.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    28.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
    29.   sConfig.IC1Filter = 0;
    30.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
    31.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
    32.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
    33.   sConfig.IC2Filter = 0;
    34.   if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
    35.   {
    36.     Error_Handler();
    37.   }
    38.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    39.   sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
    40.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    41.   if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
    42.   {
    43.     Error_Handler();
    44.   }
    45.   /* USER CODE BEGIN TIM1_Init 2 */
    46.   /* USER CODE END TIM1_Init 2 */
    47. }
    48. /**
    49.   * @brief TIM2 Initialization Function
    50.   * @param None
    51.   * @retval None
    52.   */
    53. static void MX_TIM2_Init(void)
    54. {
    55.   /* USER CODE BEGIN TIM2_Init 0 */
    56.   /* USER CODE END TIM2_Init 0 */
    57.   TIM_Encoder_InitTypeDef sConfig = {0};
    58.   TIM_MasterConfigTypeDef sMasterConfig = {0};
    59.   /* USER CODE BEGIN TIM2_Init 1 */
    60.   /* USER CODE END TIM2_Init 1 */
    61.   htim2.Instance = TIM2;
    62.   htim2.Init.Prescaler = 0;
    63.   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    64.   htim2.Init.Period = 4294967295;
    65.   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    66.   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    67.   sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
    68.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
    69.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
    70.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
    71.   sConfig.IC1Filter = 0;
    72.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
    73.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
    74.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
    75.   sConfig.IC2Filter = 0;
    76.   if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
    77.   {
    78.     Error_Handler();
    79.   }
    80.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    81.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    82.   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    83.   {
    84.     Error_Handler();
    85.   }
    86.   /* USER CODE BEGIN TIM2_Init 2 */
    87.   /* USER CODE END TIM2_Init 2 */
    88. }
    89. /**
    90.     * @author:小飞哥玩嵌入式-小飞哥
    91.     * @TODO:清除编码器数值
    92.     * @param NULL
    93.     * @return NULL
    94. */
    95. void encoder_clearCounter(void)
    96. {
    97.     __HAL_TIM_SET_COUNTER(&htim1,0);
    98.     __HAL_TIM_SET_COUNTER(&htim2,0);
    99. }
    100. /**
    101.     * @author:小飞哥玩嵌入式-小飞哥
    102.     * @TODO:获取编码器数值
    103.     * @param out s_encoder_measure
    104.     * @return NULL
    105. */
    106. void encoder_getCounter(rt_int32_t *l_speed,rt_int32_t *r_speed)
    107. {
    108.     *l_speed = ((rt_int32_t)__HAL_TIM_GET_COUNTER(&htim1)-COUNTER_RESET);
    109.     *r_speed = (rt_int32_t)__HAL_TIM_GET_COUNTER(&htim2)-COUNTER_RESET;
    110.     encoder_clearCounter();
    111. }
    112. /**
    113.     * @author:小飞哥玩嵌入式-小飞哥
    114.     * @TODO:编码器初始化
    115.     * @param NULL
    116.     * @return NULL
    117. */
    118. int hw_Encoder_init(void)
    119. {
    120.     MX_TIM1_Init();
    121.     MX_TIM2_Init();
    122.     HAL_TIM_Encoder_Start(&htim1,TIM_CHANNEL_ALL);
    123.     HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);
    124.     rt_kprintf("\r\ntim1,tim2 init ok\r\n");
    125. }
    • PID控制算法

    PID采用的是位置式PID,关于位置式PID,本章也不再具体介绍了,主要包括直立环、转向环、速度环三个控制环

    1. /**
    2.     * @author:小飞哥玩嵌入式-小飞哥
    3.     * @TODO:PID参数初始化
    4.     * @param NULL
    5.     * @param NULL
    6.     * @return NULL
    7.      */
    8. void pid_init(void)
    9. {
    10.     s_pid.kp_speed = -0.35;//速度环kp值
    11.     s_pid.kp_stand = -1600*0.6;//直立环kp值
    12.     s_pid.ki = s_pid.kp_speed/200;
    13.     s_pid.kd = 65*0.6;
    14.     s_pid.kp_turn = 20;
    15.     s_pid.limit = 800;
    16.     s_pid.err_current = 0;
    17.     s_pid.err_last = 0;
    18.     s_pid.err_sum = 0;
    19.     s_pid.lowfilter_rate = 0.7;
    20.     s_pid.mid_value = -1;
    21. }
    22. /**
    23.     * @author:小飞哥玩嵌入式-小飞哥
    24.     * @TODO:小车直立环控制
    25.     * @param 当前角度
    26.     * @param 目标角度
    27.     * @param 真实角速度
    28.     * @return pwm值
    29. */
    30. rt_int32_t balance_stand(float current_angle,float target_angle,float gyro_y)
    31. {
    32.     rt_int32_t s_pwm_out;
    33.     s_pwm_out = s_pid.kp_stand *(target_angle - current_angle) + s_pid.kd * gyro_y;
    34.     return s_pwm_out ;
    35. }
    36. /**
    37.     * @author:小飞哥玩嵌入式-小飞哥
    38.     * @TODO:小车速度环控制
    39.     * @param 左轮编码器
    40.     * @param 右轮编码器
    41.     * @param 目标值
    42.     * @return pwm值
    43. */
    44. rt_int32_t balance_speed(rt_int32_t encoder_left,rt_int32_t encoder_right,rt_int32_t target)
    45. {
    46.     rt_int32_t s_pwm_out;
    47.     //计算当前误差
    48.     s_pid.err_current = encoder_left + encoder_right - target;
    49.     //低通滤波器,low_filter_out = (1-a)*Ek+a*low_filter_out_last
    50.     s_pid.lowfilter_out = (1-s_pid.lowfilter_rate)*s_pid.err_current + s_pid.lowfilter_out_last*s_pid.lowfilter_rate;
    51.     s_pid.lowfilter_out_last = s_pid.lowfilter_out;
    52.     //速度环偏差积分,积分出位移
    53.     s_pid.err_sum += s_pid.lowfilter_out;
    54.     //积分限幅
    55.     s_pid.err_sum = s_pid.err_sum>50000?50000:((s_pid.err_sum<-50000)?-50000:s_pid.err_sum);
    56.     //速度环计算输出
    57.     s_pwm_out = s_pid.kp_speed * s_pid.lowfilter_out + s_pid.ki * s_pid.err_sum;
    58.     return s_pwm_out;
    59. }
    60. /**
    61.     * @author:小飞哥玩嵌入式-小飞哥
    62.     * @TODO:小车转向环控制
    63.     * @param gyro_z
    64.     * @return pwm值
    65. */
    66. rt_int32_t balance_turn(rt_int32_t gyro_z)
    67. {
    68.     rt_int32_t pwm_out;
    69.     pwm_out = s_pid.kp_turn*gyro_z;
    70.     return pwm_out;
    71. }

    最终是主控制代码

    1. /**
    2.     * @author:小飞哥玩嵌入式-小飞哥
    3.     * @TODO:小车控制初始化
    4.     * @param NULL
    5.     * @return NULL
    6. */
    7. //机械中值
    8. void rt_balanceCar_ctrlinit(void)
    9. {
    10.     //pwm初始化
    11.    rt_balanceCar_pwmInit();
    12.    //电机控制IO初始化
    13.    rt_balanceCar_pinInit();
    14.    //PID参数初始化
    15.    pid_init();
    16.    //MPU6050初始化
    17.    mpu_measurement_init();
    18.    //mpu6050中断初始化
    19.    mpu6050_isr_init();
    20.    //编码器初始化
    21.    hw_Encoder_init();
    22.    //按键初始化
    23.    rt_key_init();
    24. }
    25. //INIT_COMPONENT_EXPORT(rt_balanceCar_ctrlinit);
    26. /**
    27.     * @author:小飞哥玩嵌入式-小飞哥
    28.     * @TODO:小车运动控制
    29.     * @param NULL
    30.     * @return NULL
    31. */
    32. void rt_balanceCar_ctrl(rt_int32_t motor_l,rt_int32_t motor_r)
    33. {
    34.     if(motor_l > 0)
    35.     {
    36.         rt_balanceCar_LeftMotorforward();
    37.     }
    38.     else {
    39.         rt_balanceCar_LeftMotorback();
    40.     }
    41.     if(motor_r > 0)
    42.     {
    43.         rt_balanceCar_RightMotorforward();
    44.     }
    45.     else {
    46.         rt_balanceCar_RightMotorback();
    47.     }
    48.     rt_balanceCar_pwmSet(PWM_DEV_CHANNEL3,PWM_DEV_CHANNEL4,motor_l,motor_r);
    49. }
    50. /* 线程入口 */
    51. static void thread1_entry(void* parameter)
    52. {
    53.     S_MEASURE_OUT s_measure_out;
    54.     S_ENCODER_MEASURE s_encoder_measure;
    55.     char str[32];
    56.     static rt_int32_t pwm_out = 0;
    57.     static rt_int32_t pwm_value_stand = 0;
    58.     static rt_int32_t pwm_value_speed = 0;
    59.     static rt_int32_t pwm_value_turn = 0;
    60.     while(1)
    61.     {
    62.         //获取编码器数据
    63.         encoder_getCounter(&s_encoder_measure.l_speed,&s_encoder_measure.r_speed);
    64.         if(RT_EOK == rt_sem_take(RT_TIMER_SEM0xFFFF))
    65.         {
    66.           Button_Process();     //需要周期调用按键处理函数
    67.           //获取陀螺仪数据
    68.           //mpu_measurement_out(measure_out);
    69.           mpu6xxx_get_gyro(i2c_bus,&s_measure_out.gyro);
    70.           if (0==mpu_dmp_get_data(&s_measure_out.pitch, &s_measure_out.roll, &s_measure_out.yaw) )
    71.           {
    72.               //计算直立环PWM输出
    73.               pwm_value_stand = balance_stand(s_measure_out.pitch,s_pid.mid_value,s_measure_out.gyro.y);
    74.               //计算速度环PWM输出
    75.               pwm_value_speed = balance_speed(s_encoder_measure.l_speed,s_encoder_measure.r_speed,0);
    76.               //计算转向环输出
    77.               pwm_value_turn = balance_turn(s_measure_out.gyro.z);
    78.               //PWM输出
    79.               pwm_out = pwm_value_stand - s_pid.kp_stand*pwm_value_speed;
    80.               //PWM控制
    81.               if(_ABS(s_measure_out.pitch)>30)//倾角>30度,关闭电机
    82.               {
    83.                   rt_balanceCar_stop();
    84.                   pid_init();
    85.               }
    86.               else {
    87.                   rt_balanceCar_ctrl(pwm_out+pwm_value_turn,pwm_out-pwm_value_turn);
    88.             }
    89.           }
    90.           static int i = 0;
    91.           i++;
    92.           if(i%50 == 0)
    93.           {
    94.               i = 0;
    95.               rt_kprintf("\r\n\r\n\r\n");
    96.                sprintf(str,"pitch=%.2f\r\n",s_measure_out.pitch);
    97.                rt_kprintf(str);
    98.                sprintf(str,"roll=%.2f\r\n",s_measure_out.roll);
    99.                rt_kprintf(str);
    100.                sprintf(str,"yaw=%.2f\r\n",s_measure_out.yaw);
    101.                rt_kprintf(str);
    102.                rt_kprintf("\r\n\r\n\r\n");
    103.                sprintf(str,"gyro.x=%d\r\n",s_measure_out.gyro.x);
    104.                rt_kprintf(str);
    105.                sprintf(str,"gyro.y=%d\r\n",s_measure_out.gyro.y);
    106.                rt_kprintf(str);
    107.                sprintf(str,"gyro.z=%d\r\n",s_measure_out.gyro.z);
    108.                rt_kprintf(str);
    109.               rt_kprintf("\r\nencoder_l = %d\r\n",s_encoder_measure.l_speed);
    110.               rt_kprintf("\r\nencoder_r = %d\r\n",s_encoder_measure.r_speed);
    111.               rt_kprintf("\r\ns_pid.kp_stand = %d\r\n",(rt_int32_t)s_pid.kp_stand);
    112.           }
    113.         }
    114.     }
    115. }
    116. /**
    117.     * @author:小飞哥玩嵌入式-小飞哥
    118.     * @TODO:小车控制线程创建
    119.     * @param NULL
    120.     * @return NULL
    121. */
    122. int balanceCar_sample(void)
    123. {
    124.     static rt_thread_t tid1 = RT_NULL;
    125.     rt_balanceCar_ctrlinit();
    126.     /* 创建线程  */
    127.     tid1=rt_thread_create(
    128.                    "thread1",
    129.                    thread1_entry,
    130.                    RT_NULL,
    131.                    THREAD_STACK_SIZE,
    132.                    THREAD_PRIORITYTHREAD_TIMESLICE);
    133.     /* 如果获得线程控制块,启动这个线程 */
    134.     if (tid1 != RT_NULL)
    135.         rt_thread_startup(tid1);
    136.     return 0;
    137. }
    138. /* 导出到 msh 命令列表中 */
    139. INIT_COMPONENT_EXPORT(balanceCar_sample);

    至此,基本代码控制就完成了,内容很多,小飞哥可能会出视频讲,大家可以先自己看源码消化哈

    经验交流

     

    欢迎关注小飞哥玩嵌入式,期待遇到优秀的你

  • 相关阅读:
    使用浏览功能
    CGLIB 动态代理使用
    设计LRU/LFU缓存结构
    读书会招募 | 勇气七日谈,一起在“讨厌”中寻找幸福的真谛
    Flink PostgreSQL CDC配置和常见问题
    python 两个文件对比,以文件1为标准将文件2中有相等的内容整行取出
    vue3中使用element plus
    MySQL数据库的备份和恢复
    回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出
    java毕业设计驾考预约系统(附源码、数据库)
  • 原文地址:https://blog.csdn.net/qq_16519885/article/details/125306996