新入生の新作: 赤外線追跡モジュールは、私なしで車を追跡する最小限の PID アルゴリズムです。

(全くの初心者です。半年前からMCUを勉強しています。冬休みに帰省した際に作った作品です。江科大学で学び、気づいたらその経験を記録するためにブログを書きました。)だいたい)

序文:

超音波障害物回避モジュールを購入しなかったため、赤外線追跡を備えた同様の車しか作ることができません。

読み取りAD値に光が干渉する、詳細な距離を設定するのが難しく、おおよその距離しか設定できない、初心者のためIアルゴリズムのプログラムの仕方が分からないなど問題は多い。 PIDアルゴリズムがあり、一時的にモーターの回転速度が読めず、内輪が作れないので、かろうじてPとDだけで車を追従させるPIDを作ることができますが、十分すぎるほどです。基本的な機能を実現する; この記事では 1 つのモジュールのみを使用するため、直線のみにすることができます 旋回などの機能を実行したい場合は、3 つのモジュールで追跡車と同じように簡単な if ステートメントを使用できます。

目標:

  1. ワンチップマイコンで赤外線モジュールのAD値を読み取ることが可能

  1. 比較的安定した単純な追跡を実現するために、I を使用しない単純な PID アルゴリズムを作成します。

  1. 上記の目的を組み合わせる main 関数を作成します。

次に、このプロジェクトを実行するというアイデアを分析します。

まず赤外線モジュールの値を読み取る必要がありますが、赤外線モジュールには4つのインターフェースがあり、そのうちAOは特定の電圧値を読み取るポート、DOは0/1のみを出力するポートです。そこでAOを採用します。その値はADコードで読み取ることができます。非反射面に遭遇すると高い AD 値を出力し、そうでない場合は低くなります。ADを使用する理由は、DOポートを使用すると、有無を識別できるだけで、距離を制御できないためです。

2 番目は PID アルゴリズムです。初心者の私の力には限界があるので、私なしでは PID アルゴリズムしか書けません。ボ ジュンは微笑んだ。P は比例変化後のモーターの速度を指し、D は許容できる最終誤差を指します。詳細については、他の偉い人の PID の解説が非常に詳しく書かれているので読んでいただければと思いますが、今のところ自分のコードに完全に書き込むことはできていません。

最後に、モーターに入力すべき速度は PID アルゴリズムを通じてわかりますが、おそらくこれが考えられます。

ソリューションとソースコード:

1 つ目は AD の読み取りです。ここでは、コードの使い方が非常に簡単な江科大学の先生の stm32 チュートリアルを見ることをお勧めします (より重要な部分はコメントにマークされています。ps. コメントをマークすることは本当に良いことです)癖)。

#include "stm32f10x.h"                  // Device header
#include "AD.h"              

void AD_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);            //打开ADC
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                    //打开ADCCLK的6分频
    
    GPIO_InitTypeDef GPIO_InitStructure;                                    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //专属ADC的模式捏
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    

    
    //结构体初始化ADC
    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode= ADC_Mode_Independent;            //ADC1单打模式!
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right ;                    //右对齐就欧克啦
    ADC_InitStructure.ADC_ExternalTrigConv= ADC_ExternalTrigConv_None;        //只用软件触发啦
    ADC_InitStructure.ADC_ContinuousConvMode=DISABLE ;                //连续转换模式(ENABLE)
    ADC_InitStructure.ADC_NbrOfChannel= 1;                    //通道数目
    ADC_InitStructure.ADC_ScanConvMode= DISABLE;                    //扫描模式        看江科大啦
    ADC_Init(ADC1,&ADC_InitStructure);
    
    ADC_Cmd(ADC1,ENABLE);            //ADC准备就绪捏
    //但是我们要校准呢
    
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1)==SET);            //没校准开始给我站在这里
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1)==SET);                    //等待校准完成捏
    
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    
}

uint16_t AD_GetValue_D(void)
{
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);                //连续触发可放在上面,这样也不需要等到转化完成啦↓
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);                //转换完成变成1就退出啦
    return ADC_GetConversionValue(ADC1);                //因为读取后自动清除标志位,所以不用清除标志位啦
}
//若是连续触发模式,可以将软件触发的函数挪到初始化的最后,只需要触发一次即可~

uint16_t AD_GetValue_E(void)                    //这个就是连续转换用的函数啦
{
    return ADC_GetConversionValue(ADC1);
}

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
    ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);                //连续触发可放在上面,这样也不需要等到转化完成啦↓
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);                //转换完成变成1就退出啦
    return ADC_GetConversionValue(ADC1);                //因为读取后自动清除标志位,所以不用清除标志位啦
}

次にPID

#include "stm32f10x.h"                  // Device header
#include "AD.h"

void PID_Init(void)
{
    AD_Init();
}

float PID(uint16_t Aim,uint16_t D)        //Aim是目标,D是误差
{
    uint16_t Now;
    float Speed=0;
    Now=AD_GetValue(ADC_Channel_8);        //假如3500是目标,50是误差,则可以计算具体比例
    if(Now<(Aim-D))
    {
        Speed=((Now-Aim)/70)-50;
        return(Speed);        //返回的速度
    }
    else if(Now>(Aim+D))
    {
        Speed=((Now-Aim)/7)+30;
        return(Speed);
    }
    else {return 0;}
}

最後にメインコード

#include "stm32f10x.h"
#include "Delay.h"
#include "Motor.h"
#include "PID.h"
#include "OLED.h"
#include "AD.h"                  // Device header

int main(void)                //电机范围-100~100        
{
    float Speed;
    Motor_Init();
    PID_Init();
    OLED_Init();
    while(1)
    {
        Speed=PID(3500,75);
        Motor_L_SetSpeed(Speed);
        Motor_R_SetSpeed(Speed);
        OLED_ShowNum(1,1,Speed+100,3);
        OLED_ShowNum(2,1,AD_GetValue(ADC_Channel_8),4);
    }    
    
}

終わり:

この作業の実現により、超音波障害物を事前に回避するという考えに慣れることができ、将来の学習や研究を続けるのに便利です。また、非常に単純な PID アルゴリズムを自分で書いたことで、多くのアルゴリズムの微妙な点や、アルゴリズムに適応することの難しさを知ることができました。これは学習する上で避けられないハードルですが、根気強く続ければ必ず結果が得られます。

おすすめ

転載: blog.csdn.net/ChiShangying/article/details/128858013