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

2023-05-16

摘要

小伙伴们,停更很久的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软件包里面配置,我是自己移植过来的,也非常的简单,写好接口就可以了

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:mpu6050初始化
    * @param NULL
    * @return NULL
*/
void mpu_measurement_init(void)
{
    i2c_bus = (struct mpu6xxx_device *) mpu6xxx_init(MPU6050_I2C_BUS_NAME, MPU6050_ADDR); //初始化MPU6050,测量单位为角速度,加速度    while(count++)

    rt_int8_t res = 1;
    while (res)
    {
        res = mpu_dmp_init();
        rt_kprintf("\r\nRES = %d\r\n",res);

        rt_thread_mdelay(500);
        rt_kprintf("\r\nMPU6050 DMP init Error\r\n");
    }
    rt_kprintf("\r\nMPU6050 DMP init OK\r\n");
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:MPU写入多字节数据
    * @param NULL
    * @return NULL
*/
rt_uint8_t MPU_Write_Len(rt_uint8_t addr,rt_uint8_t reg,rt_uint8_t len,rt_uint8_t *databuf)
{
    rt_int8_t res = 0;
#ifdef RT_USING_I2C
    struct rt_i2c_msg msgs;
    rt_uint8_t buf[50] = {0};
#endif
    buf[0] = reg;

    for(int i = 0;i<len;i++)
    {
        buf[i+1]=databuf[i];
    }

    if (i2c_bus->bus->type == RT_Device_Class_I2CBUS)
    {
        msgs.addr  = i2c_bus->i2c_addr;    /* slave address */
        msgs.flags = RT_I2C_WR;        /* write flag */
        msgs.buf   = buf;              /* Send data pointer */
        msgs.len   = len+1;

        if (rt_i2c_transfer((struct rt_i2c_bus_device *)i2c_bus->bus, &msgs, 1) == 1)
        {
            res = RT_EOK;
        }
        else
        {
            res = -RT_ERROR;
        }
    }
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:mpu读取多字节数据
    * @param NULL
    * @return NULL
*/
rt_uint8_t MPU_Read_Len(rt_uint8_t addr,rt_uint8_t reg,rt_uint8_t len,rt_uint8_t *buf)
{
    rt_int8_t res = 0;
#ifdef RT_USING_I2C
    struct rt_i2c_msg msgs[2];
#endif
#ifdef RT_USING_SPI
    rt_uint8_t tmp;
#endif
    if (i2c_bus->bus->type == RT_Device_Class_I2CBUS)
    {
        msgs[0].addr  = i2c_bus->i2c_addr;    /* Slave address */
        msgs[0].flags = RT_I2C_WR;        /* Write flag */
        msgs[0].buf   = &reg;             /* Slave register address */
        msgs[0].len   = 1;                /* Number of bytes sent */

        msgs[1].addr  = i2c_bus->i2c_addr;    /* Slave address */
        msgs[1].flags = RT_I2C_RD;        /* Read flag */
        msgs[1].buf   = buf;              /* Read data pointer */
        msgs[1].len   = len;              /* Number of bytes read */

        if (rt_i2c_transfer((struct rt_i2c_bus_device *)i2c_bus->bus, msgs, 2) == 2)
        {
            res = RT_EOK;
        }
        else
        {
            res = -RT_ERROR;
        }
    }

    return res;
}

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

  • 电机驱动

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车电机控制初始化
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_pinInit(void)
{
    rt_pin_mode(motor_A1, PIN_MODE_OUTPUT );
    rt_pin_mode(motor_A2, PIN_MODE_OUTPUT );
    rt_pin_mode(motor_B1, PIN_MODE_OUTPUT );
    rt_pin_mode(motor_B2, PIN_MODE_OUTPUT );

    rt_pin_write(motor_A1,PIN_LOW);
    rt_pin_write(motor_A2,PIN_LOW);
    rt_pin_write(motor_B1,PIN_LOW);
    rt_pin_write(motor_B2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车左轮前进
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_LeftMotorforward(void)
{
    rt_pin_write(motor_B1,PIN_LOW);
    rt_pin_write(motor_B2,PIN_HIGH);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车左轮后退
    * @param NULL
    * @return NULL
*/
void  rt_balanceCar_LeftMotorback(void)
{
  rt_pin_write(motor_B1,PIN_HIGH);
  rt_pin_write(motor_B2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车右轮前进
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_RightMotorforward(void)
{
    rt_pin_write(motor_A1,PIN_LOW);
    rt_pin_write(motor_A2,PIN_HIGH);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车右轮后退
    * @param NULL
    * @return NULL
*/
void  rt_balanceCar_RightMotorback(void)
{   rt_pin_write(motor_A1,PIN_HIGH);
    rt_pin_write(motor_A2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车整机前进
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_forward(void)
{
    rt_pin_write(motor_A1,PIN_HIGH);
    rt_pin_write(motor_A2,PIN_LOW);
    rt_pin_write(motor_B1,PIN_HIGH);
    rt_pin_write(motor_B2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车整机后退
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_back(void)
{
    rt_pin_write(motor_A1,PIN_LOW);
    rt_pin_write(motor_A2,PIN_HIGH);
    rt_pin_write(motor_B1,PIN_LOW);
    rt_pin_write(motor_B2,PIN_HIGH);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车整机左转
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_turnLeft(void)
{
    rt_pin_write(motor_A1,PIN_LOW);
    rt_pin_write(motor_A2,PIN_LOW);
    rt_pin_write(motor_B1,PIN_HIGH);
    rt_pin_write(motor_B2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车整机右转
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_turnRight(void)
{
    rt_pin_write(motor_A1,PIN_HIGH);
    rt_pin_write(motor_A2,PIN_LOW);
    rt_pin_write(motor_B1,PIN_LOW);
    rt_pin_write(motor_B2,PIN_LOW);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车电机停转
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_stop(void)
{
    rt_pin_write(motor_A1,PIN_LOW);
    rt_pin_write(motor_A2,PIN_LOW);
    rt_pin_write(motor_B1,PIN_LOW);
    rt_pin_write(motor_B2,PIN_LOW);
}

  • PWM控制

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:pwm使能
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_pwmEnable(void)
{
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL3);
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL4);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:pwm失能
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_pwmDisable(void)
{
    rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL3);
    rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL4);
}

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:pwm输出限幅
    * @param pwm1
    * @param pwm2
    * @return NULL
*/
void rt_balanceCar_pwmlimit(rt_int32_t *pwm1,rt_int32_t *pwm2)
{
    if(*pwm1 >= PWM_UPPER_LIMIT)
    {
        *pwm1 = PWM_UPPER_LIMIT;
    }
    else if(*pwm1 <= PWM_LOWER_LIMIT)
    {
        *pwm1 = PWM_LOWER_LIMIT;
    }

    if(*pwm2 >= PWM_UPPER_LIMIT)
    {
        *pwm2 = PWM_UPPER_LIMIT;
    }
    else if(*pwm2 <= PWM_LOWER_LIMIT)
    {
        *pwm2 = PWM_LOWER_LIMIT;
    }
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:pwm设置
    * @param channel1
    * @param channel2
    * @param L_speed
    * @param R_speed
    * @return NULL
*/
void rt_balanceCar_pwmSet(rt_uint8_t channel1,rt_uint8_t channel2,rt_int32_t L_speed,rt_int32_t R_speed)
{
    //输出限幅
    rt_balanceCar_pwmlimit(&L_speed,&R_speed);

    //pwm设置
    rt_pwm_set(pwm_dev, channel1, PWM_PERIOD, _ABS(L_speed));
    rt_pwm_set(pwm_dev, channel2, PWM_PERIOD, _ABS(R_speed));
/*
    rt_pwm_enable(pwm_dev, channel1);
    rt_pwm_enable(pwm_dev, channel2);
*/
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:pwm初始化
    * @param NULL
    * @return NULL
*/
rt_int8_t rt_balanceCar_pwmInit(void)
{
    pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
    if (pwm_dev == RT_NULL)
    {
        rt_kprintf("\r\npwm sample run failed! can't find %s device!\r\n", PWM_DEV_NAME);
        return RT_ERROR;
    }
    rt_kprintf("\r\npwm sample run success! find %s device!\r\n", PWM_DEV_NAME);

    //关闭PWM
    //rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL3);
    //rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL4);
    //开启PWM
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL3);
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL4);
    return RT_EOK;
}
  • 编码器

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

/* USER CODE END 0 */

TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
/* TIM1 init function */

/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */

}

/**
  * @brief TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4294967295;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:清除编码器数值
    * @param NULL
    * @return NULL
*/
void encoder_clearCounter(void)
{
    __HAL_TIM_SET_COUNTER(&htim1,0);
    __HAL_TIM_SET_COUNTER(&htim2,0);
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:获取编码器数值
    * @param out s_encoder_measure
    * @return NULL
*/
void encoder_getCounter(rt_int32_t *l_speed,rt_int32_t *r_speed)
{
    *l_speed = ((rt_int32_t)__HAL_TIM_GET_COUNTER(&htim1)-COUNTER_RESET);
    *r_speed = (rt_int32_t)__HAL_TIM_GET_COUNTER(&htim2)-COUNTER_RESET;

    encoder_clearCounter();
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:编码器初始化
    * @param NULL
    * @return NULL
*/
int hw_Encoder_init(void)
{
    MX_TIM1_Init();
    MX_TIM2_Init();

    HAL_TIM_Encoder_Start(&htim1,TIM_CHANNEL_ALL);
    HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL);

    rt_kprintf("\r\ntim1,tim2 init ok\r\n");
}
  • PID控制算法

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

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:PID参数初始化
    * @param NULL
    * @param NULL
    * @return NULL
     */
void pid_init(void)
{
    s_pid.kp_speed = -0.35;//速度环kp值
    s_pid.kp_stand = -1600*0.6;//直立环kp值


    s_pid.ki = s_pid.kp_speed/200;
    s_pid.kd = 65*0.6;

    s_pid.kp_turn = 20;

    s_pid.limit = 800;
    s_pid.err_current = 0;
    s_pid.err_last = 0;
    s_pid.err_sum = 0;

    s_pid.lowfilter_rate = 0.7;

    s_pid.mid_value = -1;
}

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车直立环控制
    * @param 当前角度
    * @param 目标角度
    * @param 真实角速度
    * @return pwm值
*/

rt_int32_t balance_stand(float current_angle,float target_angle,float gyro_y)
{
    rt_int32_t s_pwm_out;

    s_pwm_out = s_pid.kp_stand *(target_angle - current_angle) + s_pid.kd * gyro_y;

    return s_pwm_out ;
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车速度环控制
    * @param 左轮编码器
    * @param 右轮编码器
    * @param 目标值
    * @return pwm值
*/
rt_int32_t balance_speed(rt_int32_t encoder_left,rt_int32_t encoder_right,rt_int32_t target)
{
    rt_int32_t s_pwm_out;
    //计算当前误差
    s_pid.err_current = encoder_left + encoder_right - target;
    //低通滤波器,low_filter_out = (1-a)*Ek+a*low_filter_out_last
    s_pid.lowfilter_out = (1-s_pid.lowfilter_rate)*s_pid.err_current + s_pid.lowfilter_out_last*s_pid.lowfilter_rate;
    s_pid.lowfilter_out_last = s_pid.lowfilter_out;
    //速度环偏差积分,积分出位移
    s_pid.err_sum += s_pid.lowfilter_out;
    //积分限幅
    s_pid.err_sum = s_pid.err_sum>50000?50000:((s_pid.err_sum<-50000)?-50000:s_pid.err_sum);
    //速度环计算输出
    s_pwm_out = s_pid.kp_speed * s_pid.lowfilter_out + s_pid.ki * s_pid.err_sum;

    return s_pwm_out;
}

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车转向环控制
    * @param gyro_z
    * @return pwm值
*/
rt_int32_t balance_turn(rt_int32_t gyro_z)
{
    rt_int32_t pwm_out;
    pwm_out = s_pid.kp_turn*gyro_z;
    return pwm_out;
}

最终是主控制代码

/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车控制初始化
    * @param NULL
    * @return NULL
*/

//机械中值

void rt_balanceCar_ctrlinit(void)
{
    //pwm初始化
   rt_balanceCar_pwmInit();
   //电机控制IO初始化
   rt_balanceCar_pinInit();
   //PID参数初始化
   pid_init();
   //MPU6050初始化
   mpu_measurement_init();
   //mpu6050中断初始化
   mpu6050_isr_init();
   //编码器初始化
   hw_Encoder_init();
   //按键初始化
   rt_key_init();
}
//INIT_COMPONENT_EXPORT(rt_balanceCar_ctrlinit);
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车运动控制
    * @param NULL
    * @return NULL
*/
void rt_balanceCar_ctrl(rt_int32_t motor_l,rt_int32_t motor_r)
{
    if(motor_l > 0)
    {
        rt_balanceCar_LeftMotorforward();
    }
    else {

        rt_balanceCar_LeftMotorback();
    }

    if(motor_r > 0)
    {
        rt_balanceCar_RightMotorforward();
    }
    else {

        rt_balanceCar_RightMotorback();
    }

    rt_balanceCar_pwmSet(PWM_DEV_CHANNEL3,PWM_DEV_CHANNEL4,motor_l,motor_r);
}

/* 线程入口 */
static void thread1_entry(void* parameter)
{
    S_MEASURE_OUT s_measure_out;
    S_ENCODER_MEASURE s_encoder_measure;
    char str[32];
    static rt_int32_t pwm_out = 0;


    static rt_int32_t pwm_value_stand = 0;
    static rt_int32_t pwm_value_speed = 0;
    static rt_int32_t pwm_value_turn = 0;

    while(1)
    {
        //获取编码器数据
        encoder_getCounter(&s_encoder_measure.l_speed,&s_encoder_measure.r_speed);
        if(RT_EOK == rt_sem_take(RT_TIMER_SEM, 0xFFFF))
        {
          Button_Process();     //需要周期调用按键处理函数


          //获取陀螺仪数据
          //mpu_measurement_out(measure_out);
          mpu6xxx_get_gyro(i2c_bus,&s_measure_out.gyro);

          if (0==mpu_dmp_get_data(&s_measure_out.pitch, &s_measure_out.roll, &s_measure_out.yaw) )
          {
              //计算直立环PWM输出
              pwm_value_stand = balance_stand(s_measure_out.pitch,s_pid.mid_value,s_measure_out.gyro.y);
              //计算速度环PWM输出
              pwm_value_speed = balance_speed(s_encoder_measure.l_speed,s_encoder_measure.r_speed,0);
              //计算转向环输出
              pwm_value_turn = balance_turn(s_measure_out.gyro.z);
              //PWM输出
              pwm_out = pwm_value_stand - s_pid.kp_stand*pwm_value_speed;
              //PWM控制
              if(_ABS(s_measure_out.pitch)>30)//倾角>30度,关闭电机
              {
                  rt_balanceCar_stop();
                  pid_init();
              }
              else {
                  rt_balanceCar_ctrl(pwm_out+pwm_value_turn,pwm_out-pwm_value_turn);
            }
          }
          static int i = 0;
          i++;
          if(i%50 == 0)
          {
              i = 0;

              rt_kprintf("\r\n\r\n\r\n");

               sprintf(str,"pitch=%.2f\r\n",s_measure_out.pitch);
               rt_kprintf(str);

               sprintf(str,"roll=%.2f\r\n",s_measure_out.roll);
               rt_kprintf(str);

               sprintf(str,"yaw=%.2f\r\n",s_measure_out.yaw);
               rt_kprintf(str);
               rt_kprintf("\r\n\r\n\r\n");


               sprintf(str,"gyro.x=%d\r\n",s_measure_out.gyro.x);
               rt_kprintf(str);
               sprintf(str,"gyro.y=%d\r\n",s_measure_out.gyro.y);
               rt_kprintf(str);
               sprintf(str,"gyro.z=%d\r\n",s_measure_out.gyro.z);
               rt_kprintf(str);
              rt_kprintf("\r\nencoder_l = %d\r\n",s_encoder_measure.l_speed);
              rt_kprintf("\r\nencoder_r = %d\r\n",s_encoder_measure.r_speed);
              rt_kprintf("\r\ns_pid.kp_stand = %d\r\n",(rt_int32_t)s_pid.kp_stand);

          }

        }
    }
}
/**
    * @author:小飞哥玩嵌入式-小飞哥
    * @TODO:小车控制线程创建
    * @param NULL
    * @return NULL
*/
int balanceCar_sample(void)
{
    static rt_thread_t tid1 = RT_NULL;

    rt_balanceCar_ctrlinit();
    /* 创建线程  */
    tid1=rt_thread_create(
                   "thread1",
                   thread1_entry,
                   RT_NULL,
                   THREAD_STACK_SIZE,
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);
    return 0;
}
/* 导出到 msh 命令列表中 */
INIT_COMPONENT_EXPORT(balanceCar_sample);

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

经验交流

 

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 36.【linux驱动】spi framebuffer驱动(切换并显示虚拟终端)

    1 framebuffer驱动 2 spi framebuffer驱动 3 spi framebuffer驱动 切换并显示虚拟终端 切换终端输出 接这上一节 spi framebuffer驱动实现了 xff0c 但是只有刷屏 6个虚拟终端并
  • ubuntu 移除PPA

    ubuntu 移除PPA 问题描述解决方案1 移除PPA源2 移除key3 查看那些包被替换4 从官方源下载包并替换5 测试是否替换成功 总结反思 问题描述 试图在ubuntu 20 04上安装Phalcon框架 xff0c 但是发现版本不
  • django内置的密码加密与解密

    Django 内置的User类提供了用户密码的存储 验证 修改等功能 xff0c 默认使用pbkdf2 sha256方式来存储和管理用的密码 django通过PASSWORD HASHERS来设置选择要使用的算法 xff0c 列表的第一个元
  • TF坐标变换的学习

    官方教程 xff1a http wiki ros org tf ROS中的很多软件包都需要机器人发布tf变换树 xff0c 那么什么是tf变换树呢 xff1f 抽象的来讲 xff0c 一棵tf变换树定义了不同坐标系之间的平移与旋转变换关系
  • Docker 服务端口一览

    最近研究微服务 xff0c 使用Docker来进行部署应用 说实话docker是个好东西 xff0c 只要编写好Dockerfile文件和docker compose yml文件 xff0c 便能快速启动并运行相关服务 调试过程中查看服务可
  • 鱼眼镜头的成像原理到畸变矫正(完整版)

    最近刚接触鱼眼相机 xff0c 发现网上资料还是比较零散的 xff0c 于是把搜罗到的资料汇总梳理了一下 这里推荐大家直接看链接6的论文 xff0c 从成像模型到畸变矫正整个过程讲的比较清楚 xff0c 网上很多版本其实都是根据这两篇论文来
  • STM32通过广和通ADP-L610-Arduino进行TCP/IP通信

    STM32通过广和通L610进行TCP IP通信 一 写在前面 本次参加嵌入式大赛 xff0c 使用了广和通的ADP L610 Arduino板子进行通信 项目要求大概是本地上传数据到服务器 xff0c 服务器接收后发送给客户端 xff0c
  • Visual Studio Code Git版本控制 更改语言成英文

    一 初始化git库 新版vscode只能打开一个文件夹 xff0c 当你打开这个文件夹后 xff0c 再点击左边的git控制按钮 xff0c 就会初始化该文件夹为git工作目录 xff0c 如果已经有git控制 xff0c 则不会改变 在v
  • STM8S003F3 开发环境搭建

    硬件相关 芯片介绍 型号 xff1a STM8S003F3P6 xff0c 用的不是ARM内核 STM32用的是ARM xff0c 而是意法半导体自己生产的高性能8位内核 xff1a STM8AF 主要针对汽车电子应用 xff0c 如 xf
  • 教你写Makefile(很全,含有工作经验的)

    原文 转载文 Makefile 值得一提的是 xff0c 在Makefile中的命令 xff0c 必须要以 Tab 键开始 什么是makefile xff1f 或许很多Winodws的程序员都不知道这个东西 xff0c 因为那些Window
  • 与JWT的不解之缘

    jar xff1a maven lt dependency gt lt groupId gt io jsonwebtoken lt groupId gt lt artifactId gt jjwt lt artifactId gt lt v
  • 连接服务器报错:Key exchange was not finished, connection is closed.

    解决方案 xff1a 在 etc ssh sshd config最后添加如下行内容解决问题 KexAlgorithms diffie hellman group1 sha1 diffie hellman group14 sha1 diffi
  • ros多线程管理

    单线程Spinning ros spin 是最简单的单线程自旋 它会一直调用直到结束 用法 ros spin ros spinOnce 另一个单线程spinning是ros spinOnce 它定期调用等待在那个点上的所有回调 用法 ros
  • (每日一读2019.10.24)一种基于通用优化方法的多传感器全局里程计估计框架(VINS-Fusion)

    参考博文 萌新学VINS Fusion 一 特征跟踪 萌新学VINS Fusion 二 特征跟踪 摘要 精确的状态估计是自主机器人的一个基本问题 为了实现局部精确和全局无漂移状态估计 通常将具有互补特性的多个传感器融合在一起 局部传感器 摄
  • (每日一读2019.10.25)一种基于通用优化方法的多传感器局部里程计估计框架(VINS-Fusion)

    摘要 为了提高机器人的鲁棒性和自主性 越来越多的传感器被安装在机器人上 我们已经看到了不同平台上安装的各种传感器套件 例如地面车辆上的立体摄像机 手机上带有IMU 惯性测量单元 的单目摄像机以及空中机器人上带有IMU的立体摄像机 虽然过去已
  • Gazebo模型下载以及配置--解决Gazebo黑屏原因

    前往ExBot ROS专区下载Gazebo模型 https bitbucket org osrf gazebo models downloads 下载后把文件放在 gazebo下的models文件夹中 span class token fu
  • 相机内外参数以及畸变参数

    关于大佬们的一些见解 下面是引用知乎的一段文字 xff1a 我们从单目视觉说起 平时我们都说要做视觉识别 测量云云 xff0c 然后我们就会去拍照 xff0c 再对数字图像做各种处理 xff0c 颜色处理 灰度化 滤波 边缘检测 霍夫变换
  • cmake学习4--自定义编译选项

    CMake 允许为项目增加编译选项 xff0c 从而可以根据用户的环境和需求选择最合适的编译方案 例如 xff0c 可以将 MathFunctions 库设为一个可选的库 xff0c 如果该选项为 ON xff0c 就使用该库定义的数学函数
  • ROS与C++学习1

    ROS与C 入门教程 构建工作空间 构建Catkin包 搭建开发环境 catkin make 编写简单发布节点和订阅节点 编写简单服务端和客户端 使用类方法作为回调函数 使用Timers类 编写高级的发布器和订阅器 Callbacks和Sp
  • IAR的UI界面优化

    显示行数 Tools Options 点击 Editor Tab size xff1a 设置Tab键占用多少个空格Indent size xff1a 应该是设置过行时缩进多少个空格Insert tab xff1a 选了之后在删除Tab时 x

随机推荐