• <li id="00i08"><input id="00i08"></input></li>
  • <sup id="00i08"><tbody id="00i08"></tbody></sup>
    <abbr id="00i08"></abbr>
  • 新聞中心

    EEPW首頁 > 嵌入式系統 > 設計應用 > 基于51單片機的六足仿生機器人

    基于51單片機的六足仿生機器人

    作者: 時間:2018-08-06 來源:網絡 收藏

    一、整體框架:

    (1)設計功能:

    本文引用地址:http://www.czjhyjcfj.com/article/201808/385490.htm

    ①能完成多方向行走以及其他的自定義的動作。(前進,后撤,左右轉,避障);

    ②可自動避障;

    ③通過手機藍牙下令他的下一步動作。

    (2)功能框架:


    (3)使用器材:

    ①STC89C52單片機、74LS04(反相器);

    ②藍牙串口通信模塊;

    ③超聲波測距模塊;

    ④9G舵機18個;

    ⑤PVC線槽若干(模具);

    ⑥PCB轉印板;

    ⑦螺絲螺母若干。

    ⑦keil3軟件

    二、工作原理:

    (1)藍牙串口通訊模塊:

    藍牙串口通訊模塊接收手機藍牙軟件發送字符串信號,單片機通過串口通訊協議處理藍牙模塊接收到的信息,再根據信息的內容來判斷機器人將進行的下一步行動。

    (2)超聲波測距模塊:

    超聲波模塊向某一方向發射超聲波,在發射時刻的同時開始計時(傳出低電平),超聲波在空氣中傳播,途中碰到障礙物就立即返回來,超聲波接收器收到反射波就立即停止計時(回到高電平),根據低電平的長短來計算測量距離。(超聲波在空氣中的傳播速度為340m/s,根據計時器記錄的時間t,就可以計算出發射點距障礙物的距離(s),即:s=340t/2)

    (3)舵機控制:

    控制電路板接受來自信號線的控制信號,控制電機轉動,電機帶動一系列齒輪組,減速后傳動至輸出舵盤。舵機的輸出軸和位置反饋電位計是相連的,舵盤轉動的同時,帶動位置反饋電位計,電位計將輸出一個電壓信號到控制電路板,進行反饋,然后控制電路板根據所在位置決定電機轉動的方向和速度,從而達到目標停止。舵機的控制信號周期為20MS的脈寬調制(PWM)信號,其中脈沖寬度從0.5-2.5MS,相對應的舵盤位置為0-180度,呈線性變化。也就是說,給他提供一定的脈寬,它的輸出軸就會保持一定對應角度上,無論外界轉矩怎么改變,直到給它提供一個另外寬度的脈沖信號,它才會改變輸出角度到新的對應位置上。

    在我們的作品中,18路舵機分成2組,分別用一個內部定時器來控制,產生對應舵機的PWM信號(首先定時器1生成第一個舵機的脈寬,再生成第二個舵機的,到第9個舵機為止,然后定時器2以同樣方式生成剩余的9個舵機的PWM信號,以此往復)。

    三、制作過程:

    (1)仿真原理圖:


    (2)PCB制作:


    (3)硬件搭建:

    《a》肢體制作:

    材料:PVC線槽,PVC板

    ①模型制作:(純手工割出來的)


    ②舵機改造:


    ③整體:


    四、調試以及問題解決:

    ①結構問題:

    我們認為,整體的外形結構是決定作品成敗的關鍵。經過多種材料的試驗,最終我們選擇了容易裁剪、硬度基本滿足的PVC線槽來改裝拼接肢體,軀體使用更厚的塑料板。經歷一周的純手工加工改造后,完成了整個模型的制作。

    ②供電問題:

    由于我們使用的是9G舵機,性能較差,扭力不夠,無法支撐起我們設計的電源與穩壓模塊,最后放棄了內嵌的電源,使用實驗室的可調電源箱通過電線來供電,無法獨立開來也是我們的唯一遺憾。

    ③機器抖動問題

    由于89C52只有6個內部中斷,遠遠無法滿足18個舵機的控制,并且其他功能模塊也要使用到內部中斷。所以我們將18路舵機分成了2組,初始時一個接一個舵機(每個舵機20ms周期)來發送PWM,但這也產生了發送一次18路PWM的總周期長度太大(18*20=360ms),足以產生被人眼所察覺的抖動。經過反復研究,讓當前舵機的PWM信號在上一個PWM信號的低電平處開始產生高電平(在上一個PWM的高電平結束后)如下圖,大大縮短了18路舵機一次動作的總周期長度(經過18路后,總周期長度為一個PWM的周期長度約20ms),使抖動無法被人眼所觀察。

    代碼挺多,給出主要的舵機控制代碼,代碼看不懂沒關系,后面有解釋:

    #include《reg52.h》

    #include《intrins.h》

    #include《dongzuo.h》

    #define ucharunsigned char

    #define uintunsigned int

    //PWM

    sbit PWM0 = P1^0;

    sbit PWM1 = P1^1;

    sbit PWM2 = P1^2;

    sbit PWM3 = P1^3;

    sbit PWM4 = P1^4;

    sbit PWM5 = P1^5;

    sbit PWM6 = P3^4;

    sbit PWM7 = P3^5;

    sbit PWM8 = P3^6;

    sbit PWM9 = P3^7;

    sbit PWM10 = P2^0;

    sbit PWM11 = P2^1;

    sbit PWM12 = P2^2;

    sbit PWM13 = P2^3;

    sbit PWM14 = P2^4;

    sbit PWM15 = P2^5;

    sbit PWM16 = P2^6;

    sbit PWM17 = P2^7;

    //超聲波測距

    sfr T2MOD = 0XC9; //定時器2模式控制寄存器地址

    sbit Trig =P3^2;

    sbit Echo =P3^3;

    unsigned intdistance;

    uchar DZCS =0x11; //控制動作

    uchar buf;

    uchar sd=3;

    bit flag=0; //是否發送字符

    bit CSB =0; //超聲波啟動控制位

    bit HZ=0; //后退后左轉控制位

    uchar PWMscan =0;

    uchar PWMscan1 =0;

    uchar PWMval[]={//初始姿態

    0xf8,0x8f,0xf7,0x05,0xf9,0x8c,/*5*/ 0xfa,0x0d,0xf8,0x0b,0xf9,0x67,/*b*/ 0xfa,0xd4,0xf7,0x94,0xf9,0xcb,/*11*/

    0xfa,0xad,0xfc,0xdd,0xfb,0x58,/*17*/ 0xfa,0xe9,0xfc,0xfc,0xfb,0x39,/*1d*/ 0xfc,0x18,0xfc,0xca,0xfb,0x00/*23*/

    };

    void delay(uint a)

    {

    uchar j;

    for(a;a》0;a--)

    for(j=0;j《112;j++)

    ;

    }

    void task00()

    {

    if(PWMscan==1) //第1路PWM。

    {

    PWM0=1;

    TH0=PWMval[0];

    TL0=PWMval[1];

    }

    else if(PWMscan==2) //第2路PWM。

    {

    PWM0=0;

    PWM1=1;

    TH0=PWMval[2];

    TL0=PWMval[3];

    }

    else if(PWMscan==3) //第3路PWM。

    {

    PWM1=0;

    PWM2=1;

    TH0=PWMval[4];

    TL0=PWMval[5];

    }

    else if(PWMscan==4) //第4路PWM。

    {

    PWM2=0;

    PWM3=1;

    TH0=PWMval[6];

    TL0=PWMval[7];

    }

    else if(PWMscan==5) //第5路PWM。

    {

    PWM3=0;

    PWM4=1;

    TH0=PWMval[8];

    TL0=PWMval[9];

    }

    else if(PWMscan==6) //第6路PWM。

    {

    PWM4=0;

    PWM5=1;

    TH0=PWMval[10];

    TL0=PWMval[11];

    }

    else if(PWMscan==7) //第7路PWM。

    {

    PWM5=0;

    PWM6=1;

    TH0=PWMval[12];

    TL0=PWMval[13];

    }

    else if(PWMscan==8) //第8路PWM。

    {

    PWM6=0;

    PWM7=1;

    TH0=PWMval[14];

    TL0=PWMval[15];

    }

    else if(PWMscan==9) //第9路PWM。

    {

    PWM7=0;

    PWM8=1;

    TH0=PWMval[16];

    TL0=PWMval[17];

    }

    else if(PWMscan==10) //給一定低電平,將周期拉長

    {

    PWM8=0;

    TH0=0xFF;

    TL0=0xd2;

    PWMscan=0;

    TR0 = 0; //關定時器0,開定時器1

    TR1 = 1;

    }

    PWMscan++;

    }

    void task01()

    {

    if(PWMscan1==1) //第10路PWM。

    {

    PWM9=1;

    TH1=PWMval[18];

    TL1=PWMval[19];

    }

    else if(PWMscan1==2) //第11路PWM。

    {

    PWM9=0;

    PWM10=1;

    TH1=PWMval[20];

    TL1=PWMval[21];

    }

    else if(PWMscan1==3) //第12路PWM。

    {

    PWM10=0;

    PWM11=1;

    TH1=PWMval[22];

    TL1=PWMval[23];

    }

    else if(PWMscan1==4) //第13路PWM。

    {

    PWM11=0;

    PWM12=1;

    TH1=PWMval[24];

    TL1=PWMval[25];

    }

    else if(PWMscan1==5) //第14路PWM。

    {

    PWM12=0;

    PWM13=1;

    TH1=PWMval[26];

    TL1=PWMval[27];

    }

    else if(PWMscan1==6) //第15路PWM。

    {

    PWM13=0;

    PWM14=1;

    TH1=PWMval[28];

    TL1=PWMval[29];

    }

    else if(PWMscan1==7) //第16路PWM。

    {

    PWM14=0;

    PWM15=1;

    TH1=PWMval[30];

    TL1=PWMval[31];

    }

    else if(PWMscan1==8) //第17路PWM。

    {

    PWM15=0;

    PWM16=1;

    TH1=PWMval[32];

    TL1=PWMval[33];

    }

    else if(PWMscan1==9) //第18路PWM。

    {

    PWM16=0;

    PWM17=1;

    TH1=PWMval[34];

    TL1=PWMval[35];

    }

    else if(PWMscan1==10) //給一定低電平,將周期拉長

    {

    PWM17=0;

    TH1=0xFf;//b1 //這是一個大概的值,由于每一組的PWMval的總和(PWMval中定時器的間隔的總和就是一個周期)不一致,

    //所以會導致周期不一定是20ms,但大概可以控制在20ms左右,也是因為周期的不固定,所以才需要

    TL1=0xd2;//e0 //調整每一個舵機的實際的占空比。

    PWMscan1=0;

    TR0 = 1;//開定時器0

    TR1 = 0;//關定時器1

    }

    PWMscan1++;

    }

    void TImer0()interrupt 1

    {

    task00();//控制前9路PWM

    }

    void TImer1()interrupt 3

    {

    task01();//控制后9路PWM

    }

    在實際過程中,或許是由于舵機的質量問題,又或者是其他問題,舵機的角度控制總是難以運用原理上的公式來控制角度,都是實際操作,手動調整高電平的寬度,當達到合適的值的時候,然后再把相應的代碼記錄下來。

    單片機的高電平寬度是通過定時器的兩個寄存器控制的,所以操作舵機的轉動就變成操作定時器的寄存器,再具體一點就是要得到TH、TL兩個值。(定時器高低位的差值對應高電平的寬度)

    在代碼上,在控制第幾路舵機的時候,TH、TL的值已經定死了為哪一個PWMval[?],比如第18路:

    TH1=PWMval[34];

    TL1=PWMval[35];

    這將決定此時第18路舵機的轉動角度是多少,那么怎么控制下一次該舵機的轉動角度呢?答案很簡單,就是把PWMval[34];PWMval[35];的值修改一下就可以了,其他的舵機同樣是這個道理。所以,機器人的一個姿態就可以變為這樣:機器人姿態→18路舵機的角度→18個TH、TL的值→一個36個元素的數組PWMval的值。

    所以,一個動作姿態就可以用這樣一個函數來確定:

    void DZ(ucharPWM[])//動作

    {

    uchar i;

    for(i=0;i《36;i++)

    PWMval[i]=PWM[i];

    }

    明白了這個之后,就是對每一個姿態收集數據了,在制作過程,我是把TH和TL的兩個值顯示在數碼管上,然后記錄下來的。

    后面又加入了藍牙控制模塊,超聲波測距,發現的定時器不太夠用,改成了52系列的單片機,還一個定時器即用藍牙模塊,又用超聲波測距,現在想來真佩服自己。給出控制代碼,大家自行研究:

    //***************************中斷初始化**************************

    void Init()

    {

    TMOD |= 0x11;//定時器0、1

    ET0 = 1;//使能定時器0中斷

    TR0 = 1;//開啟定時器0,定時器1中斷在定時器0開始后才打開

    ET1 = 1;//使能定時器1中斷

    IT1 = 0;//外部中斷1,低電平觸發 (邊沿高變低)

    EX1 = 1;//開外部中斷1

    //定時器2用于波特率的產生

    SCON=0x50;

    PCON=0x00;

    RCAP2H=0xFF;

    RCAP2L=0xDC;//設置波特率為9600

    T2CON=0x34;//將定時器2設置為波特率發生器(接收和發送都用TImer2) //此處包括啟動T2

    ES=1; //串口中斷

    EA = 1;//開總中斷

    }

    void TImer0()interrupt 1

    {

    task00();//控制前9路PWM

    }

    void timer1()interrupt 3

    {

    task01();//控制后9路PWM

    }

    void serial() interrupt 4

    {

    EA=0; //其余中斷全停

    if(RI)

    {

    RI=0; //清除串行接受標志位

    flag=1;

    buf=SBUF; //從串口緩沖區取得數據 (i-0x30)將ASCLL碼轉換成數字

    switch(buf)

    {

    case 0x00: DZCS=0x00;break; //向前走

    case 0x01: DZCS=0x01;break; //向后走

    case 0x02: DZCS=0x02;break; //左轉

    case 0x03: DZCS=0x03;break; //右轉

    case 0x04: DZCS=0x04;break; //橫著左

    case 0x05: DZCS=0x05;break; //橫著右

    case 0x06: DZCS=0x06;break; //揮爪子

    case 0x07: sd++;break; //減速,其實就是每個姿態中的延時不一樣

    case 0x08: sd--;break; //加速

    case 0xff: CSB=!CSB;break; //啟動關閉超聲波壁障

    default:

    DZCS=0x11;break; //

    }

    }

    EA = 1; //打開總中斷

    }

    void start()// 超聲波測距啟動函數

    {

    uchar i;

    Trig=1;

    for(i=0;i《20;i++)

    {

    _nop_();

    }

    Trig=0;

    }

    void count()// 超聲波測距函數

    {

    unsigned int time,timeH,timeL;

    timeH=TH1;

    timeL=TL1;

    time=timeH*256+timeL;

    distance=time*1.7/100;

    }

    void Inter()interrupt 2//外部中斷1在次完成測距以及相應的后續操作

    {

    EA =0;

    ET0=0; //關定時器中斷0

    TH1=0;

    TL1=0;

    TR1 =1; //檢測到距離開啟定時器1

    while(!Echo); //當echo為零時等待,中斷flag跳出等待

    TR1 =0; //關閉定時器1

    count(); //計算距離

    if(((10《distance)(distance《30))||HZ) //當距離小于5cm時,變換動作哦(在中斷中變換平面感應

    {

    DZCS=0x02; //向左

    HZ=0;

    }

    if(distance《10) //當距離小于10cm時,變換動作哦(在中斷中變換曲面感應

    {

    DZCS=0x01; //后退

    HZ=1; //后退后左轉標志

    }

    if(distance》30) //當距離小于40cm時,變換動作哦(在中斷中變換

    {

    DZCS=0x00; //向前

    HZ=0;

    }

    TR1=1;

    ET0=1;

    EA = 1;

    }

    void main()

    {

    Init();

    while(1)

    {

    uchar DZCST;//,i;

    if(CSB)

    start();

    if(DZCST!=DZCS)//動作發生改變,則回到平衡

    DZ(PH1);

    if(sd==0)

    sd=1;

    switch(DZCS)

    {

    case0x00:DZXQ(sd);break;

    case0x01:DZXH(sd);break;

    case0x02:DZXZ(sd);break;

    case0x03:DZXY(sd);break;

    case0x04:DZHZZ(sd);break;

    case0x05:DZHZY(sd);break;

    case0x06:DZZZ(sd);break;

    default:

    DZ(PH1);

    }

    DZCST=DZCS;

    }

    }

    『本文轉載自網絡,版權歸原作者所有,如有侵權請聯系刪除』



    關鍵詞: 51單片機 MCU

    評論


    相關推薦

    技術專區

    關閉
    主站蜘蛛池模板: 浦江县| 林州市| 隆尧县| 石首市| 沧源| 长寿区| 枞阳县| 山阴县| 玉田县| 丹东市| 浦北县| 成武县| 如皋市| 长春市| 南和县| 报价| 永兴县| 五大连池市| 井陉县| 扎囊县| 肃宁县| 普安县| 调兵山市| 平陆县| 遵化市| 大姚县| 那坡县| 诸暨市| 彭阳县| 汝城县| 栾川县| 上饶市| 金堂县| 新泰市| 长兴县| 北票市| 衡水市| 五指山市| 醴陵市| 天津市| 柳州市|