ROS機(jī)器人底盤(7)-PIBOT的Firmware的代碼分析(1)

包列表

說(shuō)明 備注
AFMotor Adafruit shield電機(jī)驅(qū)動(dòng)板驅(qū)動(dòng)
Board 板子資源接口及Mega2560中的實(shí)現(xiàn) 0
DataHolder 關(guān)鍵數(shù)據(jù)存儲(chǔ)類 0
Encoder 編碼器接口及Mega2560中編碼器實(shí)現(xiàn) 0
KinematicModels 機(jī)器人模型類 針對(duì)不同型號(hào)機(jī)型的解算
Motor 電機(jī)驅(qū)動(dòng)接口類及AF電機(jī)驅(qū)動(dòng)與一般驅(qū)動(dòng)板的實(shí)現(xiàn) 0
PID PID運(yùn)算類 0
Transport 通訊接口類與實(shí)現(xiàn) 0
robot robot調(diào)度類 0

main分析

直接貼源碼蛹找,無(wú)需贅述

#include "Arduino.h"
#include "robot.h"
void setup()
{
    Robot::get()->init();
}
void loop()
{   
    Robot::get()->check_command();
    Robot::get()->do_kinmatics();
    Robot::get()->calc_odom();
}

robot文件分析

robot.h

#ifndef PIBOT_ROBOT_H_
#define PIBOT_ROBOT_H_

#include "dataframe.h"

//根據(jù)機(jī)器人模型選擇相應(yīng)的解算類頭文件
#if ROBOT_MODEL == ROBOT_MODEL_DIFF//差分輪
#include "differential.h"
#endif

#if ROBOT_MODEL == ROBOT_OMNI_3//全向三輪
#include "omni3.h"
#endif

class MotorController;//電機(jī)控制器接口類
class Encoder;          //編碼器接口類
class PID;              //PID運(yùn)算接口類
class Transport;        //通訊接口類
class Dataframe;        //通訊協(xié)議數(shù)據(jù)包接口類
class Model;            //機(jī)器人模型接口類

class Robot:public Notify{
    public:
        //單例模式
        static Robot* get(){
            static Robot robot;
            return &robot;
        }
        
        //初始化
        void init();

        //檢測(cè)上位機(jī)命令
        void check_command();
        
        //運(yùn)動(dòng)學(xué)控制疚察,下發(fā)的速度轉(zhuǎn)換為各個(gè)輪子速度平且進(jìn)行PID控制
        void do_kinmatics();
        
        //根據(jù)個(gè)輪子編碼器反饋給出機(jī)器人位置姿態(tài)及實(shí)時(shí)速度信息
        void calc_odom();

        //Notify接口實(shí)現(xiàn)膝擂, 針對(duì)某些消息關(guān)注的回調(diào)函數(shù)
        void update(const MESSAGE_ID id, void* data);
    private:
        Robot(){}
        void init_motor();//初始化電機(jī)相關(guān)
        void init_trans();//初始化通訊相關(guān)
    private:
        void clear_odom();//清除累計(jì)的位置姿態(tài)信息
        void update_velocity();//更新下發(fā)的實(shí)時(shí)控制速度
    private:
        MotorController* motor[MOTOR_COUNT];//電機(jī)控制器接口
        Encoder*        encoder[MOTOR_COUNT];//編碼器接口
        PID*            pid[MOTOR_COUNT];
        float           input[MOTOR_COUNT];//PID間隔時(shí)間內(nèi)期望的encoder增加或減少的個(gè)數(shù)
        float           feedback[MOTOR_COUNT];//PID間隔時(shí)間內(nèi)反饋的encoder增加或減少的個(gè)數(shù)

        Transport*      trans;//通訊接口
        Dataframe*      frame;//通訊數(shù)據(jù)包接口
        Model*          model;//機(jī)器人模型接口
        bool            do_kinmatics_flag; //進(jìn)行運(yùn)動(dòng)控制的標(biāo)記

        Odom            odom;//機(jī)器人位置姿態(tài)實(shí)時(shí)速度信息

        unsigned long   last_velocity_command_time;//上次下發(fā)速度的時(shí)間點(diǎn)
};

#endif

robot.cpp關(guān)鍵代碼

初始化

void Robot::init(){
    Data_holder::get()->load_parameter();//加載EPPROM中的配置機(jī)器人信息

#if DEBUG_ENABLE
    Board::get()->usart_debug_init();//調(diào)試串口初始化 printf通過(guò)串口3輸出
#endif

    printf("RobotParameters: %d %d %d %d %d %d %d %d %d %d %d %d\r\n", 
        Data_holder::get()->parameter.wheel_diameter, Data_holder::get()->parameter.wheel_track,  Data_holder::get()->parameter.encoder_resolution, 
        Data_holder::get()->parameter.do_pid_interval, Data_holder::get()->parameter.kp, Data_holder::get()->parameter.ki, Data_holder::get()->parameter.kd, Data_holder::get()->parameter.ko, 
        Data_holder::get()->parameter.cmd_last_time, Data_holder::get()->parameter.max_v_liner_x, Data_holder::get()->parameter.max_v_liner_y, Data_holder::get()->parameter.max_v_angular_z);

    printf("init_motor\r\n");
    init_motor();

    printf("init_trans\r\n");
    init_trans();

    printf("pibot startup\r\n");
}

電機(jī)及編碼器相關(guān)初始化

void Robot::init_motor(){

#if MOTOR_COUNT>0//根據(jù)配置的個(gè)數(shù)定義編碼器钠龙、電機(jī)控制器及PID的具體實(shí)現(xiàn)斩跌,MOTOR_COUNT在具體的機(jī)器人模型中定義
    #if MOTOR_CONTROLLER == COMMON_CONTROLLER//根據(jù)使用的電機(jī)控制器選擇電機(jī)控制器的實(shí)現(xiàn) 這里CommonMotorController與AFSMotorController都MotorController接口類的實(shí)現(xiàn)
    static CommonMotorController motor1(MOTOR_1_PWM_PIN, MOTOR_1_DIR_A_PIN, MOTOR_1_DIR_B_PIN, MAX_PWM_VALUE);
    #elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
    static AFSMotorController motor1(MOTOR_1_PORT_NUM, MAX_PWM_VALUE);
    #endif
    
    //EncoderImp是Encoder接口類的實(shí)現(xiàn)
    static EncoderImp encoder1(MOTOR_1_ENCODER_A_PIN, MOTOR_1_ENCODER_B_PIN);
    //PID計(jì)算接口脖苏,給定參數(shù)為期望的數(shù)據(jù)地址渊抽、反饋的數(shù)據(jù)地址以及各個(gè)PID參數(shù)值
    static PID pid1(&input[0], &feedback[0], float(Data_holder::get()->parameter.kp)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.ki)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif

#if MOTOR_COUNT>1
    #if MOTOR_CONTROLLER == COMMON_CONTROLLER
    static CommonMotorController motor2(MOTOR_2_PWM_PIN, MOTOR_2_DIR_A_PIN, MOTOR_2_DIR_B_PIN, MAX_PWM_VALUE);
    #elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
    static AFSMotorController motor2(MOTOR_2_PORT_NUM, MAX_PWM_VALUE);
    #endif
    static EncoderImp encoder2(MOTOR_2_ENCODER_A_PIN, MOTOR_2_ENCODER_B_PIN);
    static PID pid2(&input[1], &feedback[1], float(Data_holder::get()->parameter.kp)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.ki)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif

#if MOTOR_COUNT>2
    #if MOTOR_CONTROLLER == COMMON_CONTROLLER
    static CommonMotorController motor3(MOTOR_3_PWM_PIN, MOTOR_3_DIR_A_PIN, MOTOR_3_DIR_B_PIN, MAX_PWM_VALUE);
    #elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
    static AFSMotorController motor3(MOTOR_3_PORT_NUM, MAX_PWM_VALUE);
    #endif

    static EncoderImp encoder3(MOTOR_3_ENCODER_A_PIN, MOTOR_3_ENCODER_B_PIN);
    static PID pid3(&input[2], &feedback[2], float(Data_holder::get()->parameter.kp)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.ki)/Data_holder::get()->parameter.ko, 
                                            float(Data_holder::get()->parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif

#if MOTOR_COUNT>0
    motor[0] = &motor1;//接口指向具體實(shí)現(xiàn)
    encoder[0] = &encoder1;
    pid[0] = &pid1;
#endif

#if MOTOR_COUNT>1
    motor[1] = &motor2;
    encoder[1] = &encoder2;
    pid[1] = &pid2;
#endif

#if MOTOR_COUNT>2
    motor[2] = &motor3;
    encoder[2] = &encoder3;
    pid[2] = &pid3;
#endif

#if ROBOT_MODEL == ROBOT_MODEL_DIFF//根據(jù)配置的機(jī)器人模型選擇解算類的實(shí)現(xiàn),這個(gè)Differential與Omni3是運(yùn)動(dòng)解算接口Model的實(shí)現(xiàn)
    static Differential diff(Data_holder::get()->parameter.wheel_diameter*0.0005, Data_holder::get()->parameter.wheel_track*0.0005);
    model = &diff;
#endif

#if ROBOT_MODEL == ROBOT_OMNI_3
    static Omni3 omni3(Data_holder::get()->parameter.wheel_diameter*0.0005, Data_holder::get()->parameter.wheel_track*0.0005);
    model = &omni3;
#endif

    //初始化電機(jī)驅(qū)動(dòng)器
    for (int i=0;i<MOTOR_COUNT;i++){
        motor[i]->init();
    }

    do_kinmatics_flag = false;

    memset(&odom, 0 , sizeof(odom));
    memset(&input, 0 , sizeof(input));
    memset(&feedback, 0 , sizeof(feedback));

    last_velocity_command_time = 0;
}

通訊相關(guān)初始化

void Robot::init_trans(){
    static USART_transport _trans(MASTER_USART, 115200);//使用串口作為通訊接口
    static Simple_dataframe    _frame(&_trans);//使用Simple_dataframe協(xié)議數(shù)據(jù)包實(shí)現(xiàn)數(shù)據(jù)打包解包
    trans = &_trans;
    frame = &_frame;

    trans->init();
    frame->init();

    //注冊(cè)相關(guān)消息的通知篙耗, 收到該消息this->update回調(diào)會(huì)被調(diào)用
    frame->register_notify(ID_SET_ROBOT_PARAMTER, this);
    frame->register_notify(ID_CLEAR_ODOM, this);
    frame->register_notify(ID_SET_VELOCITY, this);
}

上位命令處理

void Robot::check_command(){
    unsigned char ch=0;
    if (trans->read(ch)){//從通訊口中讀取數(shù)據(jù)
        //printf("%02x ", ch);
        if (frame->data_recv(ch)){//使用數(shù)據(jù)包接收和解析數(shù)據(jù)
            //printf("\r\n");
            frame->data_parse();
        }
    }
}

運(yùn)動(dòng)處理


void Robot::do_kinmatics(){
    if (!do_kinmatics_flag){//該標(biāo)記收到上位的命令會(huì)置true迫筑, 超時(shí)停止會(huì)置false
        for(int i=0;i<MOTOR_COUNT;i++){
            pid[i]->clear();//停止后清除pid變量值
            encoder[i]->get_increment_count_for_dopid();//讀取掉停止時(shí)編碼器的變化至,放置手動(dòng)轉(zhuǎn)動(dòng)電機(jī)導(dǎo)致下次啟動(dòng)pid時(shí)的異常
        }
        return;
    }
    
    static unsigned long last_millis=0;
    //根據(jù)配置PID間隔時(shí)間進(jìn)行pid運(yùn)算
    if (Board::get()->get_tick_count()-last_millis>=Data_holder::get()->parameter.do_pid_interval){
        last_millis = Board::get()->get_tick_count();
        //得到PID間隔時(shí)間反饋編碼器的值
        for(int i=0;i<MOTOR_COUNT;i++){
            feedback[i] = encoder[i]->get_increment_count_for_dopid();
        }
#ifdef PID_DEBUG_OUTPUT
        printf("input=%ld %ld %ld feedback=%ld %ld %ld\r\n", long(input[0]*1000), long(input[1]*1000), long(input[2]*1000), 
                                                    long(feedback[0]), long(feedback[1]), long(feedback[2]));
#endif
        //判斷超時(shí)宗弯,則無(wú)需繼續(xù)PID運(yùn)算
        bool stoped=true;
        for(int i=0;i<MOTOR_COUNT;i++){
            if (input[i] != 0 || feedback[i] != 0){
                stoped = false;
                break;
            }
        }

        short output[MOTOR_COUNT]={0};
        if (stoped){
            for(int i=0;i<MOTOR_COUNT;i++){
                output[i] = 0;
            }
            do_kinmatics_flag = false;
        }else{
            //計(jì)算得到輸出PWM  input[i]在update回調(diào)通知中給定
            for(int i=0;i<MOTOR_COUNT;i++){
                output[i] = pid[i]->compute(Data_holder::get()->parameter.do_pid_interval*0.001);
            }
        }

#ifdef PID_DEBUG_OUTPUT
        printf("output=%ld %ld %ld\r\n\r\n", output[0], output[1], output[2]);
#endif
        //控制各個(gè)電機(jī)
        for(int i=0;i<MOTOR_COUNT;i++){
            motor[i]->control(output[i]);
        }

        //超時(shí)判斷
        if (Board::get()->get_tick_count()-last_velocity_command_time>Data_holder::get()->parameter.cmd_last_time){
            for(int i=0;i<MOTOR_COUNT;i++){
                input[i] = 0;
            }
        }
    }
}

計(jì)算里程位置姿態(tài)

void Robot::calc_odom(){

    static unsigned long last_millis=0;
    //每間CALC_ODOM_INTERVAL隔時(shí)間計(jì)算
    if (Board::get()->get_tick_count()-last_millis>=CALC_ODOM_INTERVAL){
        last_millis = Board::get()->get_tick_count();
#ifdef ODOM_DEBUG_OUTPUT        
        long total_count[MOTOR_COUNT]={0};
        for(int i=0;i<MOTOR_COUNT;i++){
            total_count[i] = encoder[i]->get_total_count();
        }

        printf("total_count=%ld %ld\r\n", total_count[0], total_count[1]);
#endif
        float dis[MOTOR_COUNT] = {0};
        //得到間隔時(shí)間內(nèi)個(gè)輪子行徑的距離(m)
        for(int i=0;i<MOTOR_COUNT;i++){
            dis[i] = encoder[i]->get_increment_count_for_odom()*__PI*Data_holder::get()->parameter.wheel_diameter*0.001/Data_holder::get()->parameter.encoder_resolution;
#ifdef ODOM_DEBUG_OUTPUT
            printf(" %ld ", long(dis[i]*1000000));
#endif
        }
        
        //通過(guò)使用的模型接口到當(dāng)前里程信息
        model->get_odom(&odom, dis, CALC_ODOM_INTERVAL);

#ifdef ODOM_DEBUG_OUTPUT
        printf(" x=%ld y=%ld yaw=%ld", long(odom.x*1000), long(odom.y*1000), long(odom.z*1000));
        printf("\r\n");
#endif
        //更新至數(shù)據(jù)存儲(chǔ)中
        Data_holder::get()->odom.v_liner_x = odom.vel_x*100;
        Data_holder::get()->odom.v_liner_y = odom.vel_y*100;
        Data_holder::get()->odom.v_angular_z = odom.vel_z*100;
        Data_holder::get()->odom.x = odom.x*100;
        Data_holder::get()->odom.y = odom.y*100;
        Data_holder::get()->odom.yaw = odom.z*100;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末脯燃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒙保,更是在濱河造成了極大的恐慌辕棚,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件追他,死亡現(xiàn)場(chǎng)離奇詭異坟募,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)邑狸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門懈糯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人单雾,你說(shuō)我怎么就攤上這事赚哗。” “怎么了硅堆?”我有些...
    開(kāi)封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵屿储,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我渐逃,道長(zhǎng)够掠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任茄菊,我火速辦了婚禮疯潭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘面殖。我一直安慰自己竖哩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布脊僚。 她就那樣靜靜地躺著相叁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辽幌。 梳的紋絲不亂的頭發(fā)上增淹,一...
    開(kāi)封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音舶衬,去河邊找鬼埠通。 笑死,一個(gè)胖子當(dāng)著我的面吹牛逛犹,可吹牛的內(nèi)容都是我干的端辱。 我是一名探鬼主播梁剔,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舞蔽!你這毒婦竟也來(lái)了荣病?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渗柿,失蹤者是張志新(化名)和其女友劉穎个盆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體朵栖,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颊亮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陨溅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片终惑。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖门扇,靈堂內(nèi)的尸體忽然破棺而出雹有,到底是詐尸還是另有隱情,我是刑警寧澤臼寄,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布霸奕,位于F島的核電站,受9級(jí)特大地震影響吉拳,放射性物質(zhì)發(fā)生泄漏质帅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一留攒、第九天 我趴在偏房一處隱蔽的房頂上張望临梗。 院中可真熱鬧,春花似錦稼跳、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至票彪,卻和暖如春红淡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背降铸。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工在旱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人推掸。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓桶蝎,卻偏偏與公主長(zhǎng)得像驻仅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子登渣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • tess4.0中主要的數(shù)據(jù)結(jié)構(gòu) Page analysis result: PAGE_RES (ccstruct/...
    RobertY閱讀 2,437評(píng)論 1 6
  • 2017年6月7日 星期三 晴(農(nóng)歷五月十四) 放學(xué)接了譽(yù)仔之后噪服,讓他和他的好朋友仲浩宇多玩了一會(huì),想著...
    譽(yù)仔媽媽閱讀 258評(píng)論 8 3
  • 九月金秋胜茧,歲在戊子粘优。慕學(xué)后生,齊聚燕京呻顽。故都涵若雹顺,納八方之俊秀,容四海之賢才廊遍。吾輩小子嬉愧,忝居于此,求學(xué)書院昧碉,習(xí)為師...
    柴子恒閱讀 289評(píng)論 0 0
  • 你走過(guò) 倩影 似夢(mèng)中走來(lái) 迷醉煙火 夜色 揮灑著銀灰 掩埋寂寞 那一刻 心跳 不受控制著 步伐 不自覺(jué)跟著 江山如...
    夕辰沭閱讀 488評(píng)論 2 3