• 风力摆?这是不是太简单了点


    关于风力摆

    风力摆是2015年的电子设计大赛题目

    关于风力摆的说明啊,实现的功能啊我就不多说了

    网上有很多的资料,说难了这是PID调节什么的,但是说简单了这就是一个单摆 和 圆锥摆模型

    如果你用PDI一点一点调,你会调死,要是用我的方法,呵呵,往下看吧

    废话我就不多说了直接进入正题

    所需材料

     材料就简单了

    • 单片机最小系统(最好用stm32 快,开源,资料多)
    • 电机驱动模块2个双通道的(淘宝上很多 几块钱)
    • 液晶显示屏(OLED足够用了)
    • JLink ( 下载程序用的,为了节约成本用串口下载也可以 )
    • 碳杆60cm
    • 万向节
    • 陀螺仪(mpu6050)
    • 空心杯电机(4个)
    • 导线若干
    • 还有就是电池

    先来个图片吧

     

     虽然有点抽,但完全不影响功能

    风力摆思路

    1. 画直线就是单摆思路

      陀螺仪可以测得角加速度,同时经过四元数换算可以得到三轴方向的角度,有了这个基础就简单了。

      

      1.1首先先弄出0°方向的直线

        我选的是roll作为0°轴,这样可以先用此时角度算出此时应该要的角速度,然后写两个环,角度负反馈环 和 角速度负反馈环,就可以平衡了,

      1.2直线长度可设置

        直线长度就和顶点到地面距离L,期望摆起的角度有关

      1.3各个方向

        然后通过希望的方向角度,可以算出,x, y (x,y 可以根据自己的喜好设置) 方向上所期望的角度和角速度(这个角度满足公式 tan(a)= y / x , 不清楚的慢慢推,中学物理),就可以向180度延申。

      1.4停止就只要吧期望线长设为0

    2.画圆的思路

      和画直线是一样的,由圆锥摆的性质,可得,摆长一定,摆起的高度和角速度成比例关系,

      要注意的是这里的角速度和mpu6050的角速度不等价,这里mpu6050测出的角速度是旋转时的线速度。这个地方不懂的可以多想想。(我可是吃了亏的,白白延迟了半天的进度)

      这里也可以向上面那样加两个负反馈环,不过我就用了一个角速度环,简单嘛。

    然后就没有然后,这样就搞定了,附上控制代码吧

      1 #include "mymath.h"
      2 #include "math.h"
      3 #include "IMU6050.h"
      4 #include "control.h"
      5 
      6 //================================================单摆
      7 //得到此时的偏差值
      8 float Get_Offset(Imu_t* imu_t, ALL_Expect* expect, ALL_OFFSET* offset)
      9 {
     10     float expect_x, expect_y;
     11     float speed;
     12     float speed_x, speed_y;
     13     int dir_x = 0, dir_y = 0;
     14     
     15     dir_x = Get_Direct(expect->angle, imu_t->gyro.x, imu_t->roll, imu_t->pitch, 0);
     16     dir_y = Get_Direct(expect->angle, imu_t->gyro.y, imu_t->roll, imu_t->pitch, 1);
     17     Get_Expect_X_Y(expect->angle, expect->length_to_angle, &expect_x, &expect_y);
     18     
     19     speed = Get_Speed(expect->length_to_angle, imu_t->roll, imu_t->pitch, 60);
     20     Get_Expect_X_Y(expect->angle, speed, &speed_x, &speed_y);
     21     
     22     if(speed>0)
     23     {
     24         if((imu_t->gyro.x)*(imu_t->roll)>0)
     25         {
     26             offset->x_offset = MY_ABS(expect_x - MY_ABS(imu_t->roll))*dir_x*1.0;
     27             offset->y_offset = MY_ABS(expect_y - MY_ABS(imu_t->pitch))*dir_y*1.0;
     28         }
     29         else
     30         {
     31             offset->x_offset = 0;
     32             offset->y_offset = 0;
     33         }
     34     }
     35     else if(speed < 0)
     36     {
     37         
     38         offset->x_offset = -MY_ABS(expect_x - MY_ABS(imu_t->roll))*dir_x*1.0;
     39         
     40         offset->y_offset = -MY_ABS(expect_y - MY_ABS(imu_t->pitch))*dir_y*1.0;    
     41     }
     42     else
     43     {
     44             offset->x_offset = 0;
     45             offset->y_offset = 0;
     46     }
     47     
     48     if(speed>=0)
     49     {
     50             offset->x_gyro_offset = (speed_x - MY_ABS(imu_t->gyro.x))*dir_x*1.0;
     51             offset->y_gyro_offset = (speed_y - MY_ABS(imu_t->gyro.y))*dir_y*1.0;
     52     }
     53     else if(speed < 0)
     54     {
     55         offset->x_gyro_offset = -MY_ABS(imu_t->gyro.x)*dir_x*1.0;
     56         offset->y_gyro_offset = -MY_ABS(imu_t->gyro.y)*dir_y*1.0;
     57     }
     58     
     59     return speed_y;
     60 }
     61 
     62 //得到期望角的方向
     63 int Get_Direct(int angle, float gyro, float roll, float pitch, int x_y)
     64 {
     65     int speed = 0;
     66     
     67     speed = gyro*20;
     68     if(speed>0)
     69     {
     70         return 1;
     71     }
     72     else if(speed == 0)
     73     {
     74         if(MY_ABS(roll)<3 && MY_ABS(pitch)<3)  //判读是否没有动
     75         {
     76             if(x_y == 0)
     77             {
     78                 if(angle<=90)
     79                 {
     80                     return 1;
     81                 }
     82                 else
     83                 {
     84                     return -1;
     85                 }
     86             }
     87             else if(x_y == 1)
     88             {
     89                 return 1;
     90             }
     91         }
     92         return 0;
     93     }
     94     else if(speed<0)
     95     {
     96         return -1;
     97     }
     98 }
     99 
    100 //得到期望的x, y 比例
    101 void Get_Expect_X_Y(int angle, float self, float *x, float* y)
    102 {
    103     float p=0;
    104     
    105     if(self<0)
    106     {
    107         *x = 0;
    108         *y = 0;
    109         return;
    110     }
    111     
    112     if(angle<90)
    113     {
    114         p = tan(ANGLE_TO_RADIAN(angle))*1.0;   //得到比例值
    115     }
    116     else if(angle == 90)
    117     {
    118         *x = 0;
    119         *y = self;
    120         return ;
    121     }
    122     else if(angle > 90)
    123     {
    124         p = tan(ANGLE_TO_RADIAN(angle-90))*1.0;   //得到比例值
    125     }
    126     *x = sqrt(self*self / (1+p*p))*1.0;
    127     *y = *x * p;
    128 }
    129 
    130 //得到一定角度时的速度
    131 //L是杆子的长度
    132 float Get_Speed(float length_to_angle, float roll, float pitch, int L)
    133 {
    134     float now_angle;
    135     float h;
    136 
    137     now_angle = sqrt((roll*roll)+(pitch*pitch));
    138     if(now_angle>length_to_angle)
    139     {    
    140         return length_to_angle - now_angle;
    141     }
    142     h = L*(cos(ANGLE_TO_RADIAN(now_angle))-cos(ANGLE_TO_RADIAN(length_to_angle)));
    143     return (sqrt(2*980*h))/L*1.0;
    144 }
    145     
    146 
    147 //===============================================圆锥摆
    148 float Get_Circulat_Offset(Imu_t *imu_t, ALL_Expect* expect, ALL_OFFSET* offset)
    149 {
    150     float r, speed, a;
    151     float speed_x, speed_y;
    152     float angle;
    153     int dir_x = 0, dir_y = 0;
    154     
    155 //    dir_x = Get_Direct(expect->angle, imu_t->gyro.x, imu_t->roll, imu_t->pitch, 0);
    156 //    dir_y = Get_Direct(expect->angle, imu_t->gyro.y, imu_t->roll, imu_t->pitch, 1);
    157     Get_Circulat_Dir(imu_t->roll, imu_t->pitch, &dir_x, &dir_y, 1);
    158     
    159     r = 0.7 * sin(ANGLE_TO_RADIAN(expect->length_to_angle));
    160     a = 9.8 * tan(ANGLE_TO_RADIAN(expect->length_to_angle));  //向心加速度
    161     speed = sqrt(r * a)*1.0 + 0.31;
    162 
    163     if (imu_t->roll != 0 | imu_t->roll != 180)
    164         angle = atan2(MY_ABS(imu_t->pitch), MY_ABS(imu_t->roll)) * 57.3;
    165     else
    166         angle = 90;
    167     Get_Expect_X_Y(angle, speed, &speed_y, &speed_x);
    168         
    169     offset->x_offset = 0;
    170     offset->y_offset = 0;
    171     offset->x_gyro_offset = (speed_x - MY_ABS(imu_t->gyro.x))*dir_x*1.0;
    172     offset->y_gyro_offset = (speed_y - MY_ABS(imu_t->gyro.y))*dir_y*1.0;
    173     return speed;;
    174 }
    175 
    176 //根据 旋转方向得到 电机方向
    177 void Get_Circulat_Dir(float roll, float pitch, int *dir_x, int *dir_y, int dir)  // 
    178 {
    179 
    180     int clock_dir[2][4][2] = {
    181         {
    182             {+1,-1},  //一相线
    183             {+1,+1},    //二相线
    184             {-1,+1},  //三相线
    185             {-1,-1},  //四相线
    186         },
    187         {
    188             {-1,+1},  //一相线
    189             {-1,-1},    //二相线
    190             {+1,-1},  //三相线
    191             {+1,+1},  //四相线
    192         }
    193     }
    194     ;
    195     //判断象限
    196     if(roll>=0 && pitch>=0)
    197     {
    198         *dir_x = clock_dir[dir][0][0];
    199         *dir_y = clock_dir[dir][0][1];
    200     }
    201     else    if(roll<0 && pitch>=0)
    202     {
    203         *dir_x = clock_dir[dir][1][0];
    204         *dir_y = clock_dir[dir][1][1];
    205     }
    206     else    if(roll<0 && pitch<0)
    207     {
    208         *dir_x = clock_dir[dir][2][0];
    209         *dir_y = clock_dir[dir][2][1];
    210     }
    211     else    if(roll>=0 && pitch<0)
    212     {
    213         *dir_x = clock_dir[dir][3][0];
    214         *dir_y = clock_dir[dir][3][1];
    215     }
    216 }

    也就简单的两百行

    要不你再看看我的PID调节代码,看看简单不简单???

     1 int P1_1=1, P1_2=1, P1_3=1, P1_4=1;
     2 int P2_1=50, P2_2=50, P2_3=50, P2_4=50;
     3 void PID_Judge(ALL_OFFSET offset)
     4 {
     5     uint16_t pwm1=0, pwm2=0, pwm3=0, pwm4=0;
     6     
     7     if(offset.x_offset>=0) 
     8     {
     9         pwm1 += P1_1*offset.x_offset;
    10     }
    11     else
    12     {
    13         pwm2 += P1_2*(-offset.x_offset);
    14     }
    15     /////
    16     if(offset.x_gyro_offset>=0)
    17     {
    18         pwm1 += P2_1*offset.x_gyro_offset;
    19     }
    20     else
    21     {
    22         pwm2 += P2_2*(-offset.x_gyro_offset);
    23     }
    24     /////
    25     if(offset.y_offset>=0)
    26     {
    27         pwm3 += P1_3*offset.y_offset;
    28     }
    29     else
    30     {
    31         pwm4 += P1_4*(-offset.y_offset);
    32     }
    33     /////
    34     if(offset.y_gyro_offset>=0)
    35     {
    36         pwm3 += P2_3*offset.y_gyro_offset;
    37     }
    38     else
    39     {
    40         pwm4 += P2_4*(-offset.y_gyro_offset);
    41     }
    42     
    43     pwm1 = LIMIT_MAX(pwm1, 80);
    44     pwm2 = LIMIT_MAX(pwm2, 80);
    45     pwm3 = LIMIT_MAX(pwm3, 80);
    46     pwm4 = LIMIT_MAX(pwm4, 80);
    47     
    48     TIM_SetCompare1(TIM10,pwm1);    //修改比较值,修改占空比
    49     TIM_SetCompare1(TIM11,pwm2);    //修改比较值,修改占空比
    50     TIM_SetCompare1(TIM13,pwm3);    //修改比较值,修改占空比
    51     TIM_SetCompare1(TIM14,pwm4);    //修改比较值,修改占空比
    52 }

    总结

    这个题目说起来很难,其实想成物理模型就很简单了,也就那样吧

    可惜不附上视频不然肯定秀一波,和大神做的差不了多少,照样是乱动 5s 内就能恢复

    这个东西加搭硬件写程序只用了2天,因为我不用怎么调PID,全在数学公式。

    其实只需要一天半的,但是那个画圆锥的地方出现了问题, 不是公式出了问题,而是想法错了,就是我说的注意的地方,白白焦躁了半天,日了狗,哎。

    有问题的可以留言哈

  • 相关阅读:
    iptables 增删查改
    在Ubuntu14.04上安装WordPress4搭建技术博客
    Revit 二次开发之 零件
    Revit 二次开发之 结构层次
    revit二次开发之 过滤器二FilteredElementCollector收集器
    Revit二次开发之 动态模型更新(DMU: Dynamic Model Update)功能
    revit二次开发之 过滤器一
    Revit 二次开发之标高参数
    Revit二次开发之 错误
    Visual Studio删除所有的注释和空行
  • 原文地址:https://www.cnblogs.com/kaixindexiaocao/p/11290266.html
Copyright © 2020-2023  润新知