ROS機(jī)器人底盤(21)-關(guān)于運(yùn)動(dòng)控制方向的補(bǔ)充

1.概述

使用PIBOT提供的小車已經(jīng)完成了程序和硬件的調(diào)試屿愚,如果需要移植到自己的小車兼蜈,可能會(huì)遇到PID調(diào)速不正常,解算得到運(yùn)動(dòng)結(jié)果不一致,反解得到里程有問題等,原因就是電機(jī)上的電機(jī)接線次序、編碼器AB相次序,以及控制板輸出口跟電機(jī)次序寥假、,這里需要注意下面幾點(diǎn)即可

  • a. 給定電機(jī)接口函數(shù)的輸入?yún)?shù)正負(fù)與電機(jī)方向的關(guān)系
  • b.電機(jī)索引關(guān)系
  • c.電機(jī)轉(zhuǎn)向得到編碼器值正負(fù)關(guān)系

2.PIBOT運(yùn)動(dòng)解算和PID

void Robot::do_kinmatics(){
    if (!do_kinmatics_flag){
        for(int i=0;i<MOTOR_COUNT;i++){
            pid[i]->clear();
            encoder[i]->get_increment_count_for_dopid();
        }
        return;
    }
    
    static unsigned long last_millis=0;
    if (Board::get()->get_tick_count()-last_millis>=Data_holder::get()->parameter.do_pid_interval){
        last_millis = Board::get()->get_tick_count();
        
        for(int i=0;i<MOTOR_COUNT;i++){
            feedback[i] = encoder[i]->get_increment_count_for_dopid();
        }
#if PID_DEBUG_OUTPUT
    #if MOTOR_COUNT==2
        printf("input=%ld %ld feedback=%ld %ld\r\n", long(input[0]*1000), long(input[1]*1000), 
                                                        long(feedback[0]), long(feedback[1]));
    #endif
    #if MOTOR_COUNT==3
        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
    #if MOTOR_COUNT==4
        printf("input=%ld %ld %ld %ld feedback=%ld %ld %ld %ld\r\n", long(input[0]*1000), long(input[1]*1000), long(input[2]*1000), long(input[3]*1000), 
                                                        long(feedback[0]), long(feedback[1]), long(feedback[2]), long(feedback[3]));
    #endif
#endif
        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{
            for(int i=0;i<MOTOR_COUNT;i++){
                output[i] = pid[i]->compute(Data_holder::get()->parameter.do_pid_interval*0.001);
            }
        }

        for(int i=0;i<MOTOR_COUNT;i++){
            Data_holder::get()->pid_data.input[i] = int(input[i]);
            Data_holder::get()->pid_data.output[i] =  int(feedback[i]);
        }

#if PID_DEBUG_OUTPUT
    #if MOTOR_COUNT==2
        printf("output=%ld %ld\r\n\r\n", output[0], output[1]);
    #endif
    #if MOTOR_COUNT==3
        printf("output=%ld %ld %ld\r\n\r\n", output[0], output[1], output[2]);
    #endif
    #if MOTOR_COUNT==4
        printf("output=%ld %ld %ld %ld\r\n\r\n", output[0], output[1], output[2], output[3]);
    #endif
#endif
        for(int i=0;i<MOTOR_COUNT;i++){
            motor[i]->control(output[i]);
        }

        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;
            }
        }
    }
}

運(yùn)動(dòng)控制是在Robot類中的do_kinmatics函數(shù)實(shí)現(xiàn)的莽鸿,大概流程

  • 根據(jù)解算到各個(gè)輪子的速度轉(zhuǎn)換到對(duì)應(yīng)編碼器的值和編碼器的輸出計(jì)算PID
  • 根據(jù)PID結(jié)果控制電機(jī)
  • 超時(shí)判斷

上面說到的解算就是在Robot類中的update_velocity函數(shù)實(shí)現(xiàn)的

void Robot::update_velocity(){
    short vx = min(max(Data_holder::get()->velocity.v_liner_x, -(short(Data_holder::get()->parameter.max_v_liner_x))), short(Data_holder::get()->parameter.max_v_liner_x));
    short vy = min(max(Data_holder::get()->velocity.v_liner_y, -(short(Data_holder::get()->parameter.max_v_liner_y))), short(Data_holder::get()->parameter.max_v_liner_y));
    short vz = min(max(Data_holder::get()->velocity.v_angular_z, -(short(Data_holder::get()->parameter.max_v_angular_z))), short(Data_holder::get()->parameter.max_v_angular_z));

    float vel[3]={vx/100.0, vy/100.0, vz/100.0};
    float motor_speed[MOTOR_COUNT]={0};
    model->motion_solver(vel, motor_speed);


    for(int i=0;i<MOTOR_COUNT;i++){
        input[i] = motor_speed[i]*short(Data_holder::get()->parameter.encoder_resolution)/(2*__PI)*short(Data_holder::get()->parameter.do_pid_interval)*0.001;
    }


#if DEBUG_ENABLE
    printf("vx=%d %d motor_speed=%ld %ld\r\n", vx, vz, long(motor_speed[0]*1000), long(motor_speed[1]*1000));
#endif

    last_velocity_command_time = Board::get()->get_tick_count();
    do_kinmatics_flag = true;
}

通過調(diào)用運(yùn)動(dòng)模型接口調(diào)用運(yùn)動(dòng)解算(model->motion_solver(vel, motor_speed))昧旨,完成從控制的全局速度(下發(fā)的角速度和線速度)到各個(gè)輪子速度的轉(zhuǎn)換,最終轉(zhuǎn)換為在do_pid_interval時(shí)間內(nèi)編碼器的變化值

3.PIBOT電機(jī)方向和順序

3.1電機(jī)方向與pwm_value值關(guān)系

PIBOT定義所有電機(jī)控制給正值時(shí)(motor[i]->control(pwm_value))祥得,從電機(jī)輸出軸方向看電機(jī)順時(shí)針轉(zhuǎn)兔沃。
對(duì)應(yīng)到差分小車apollo,

motor[0]->control(2000);//控制左電機(jī)向后
motor[1]->control(2000);//控制右電機(jī)向前

對(duì)與小車就是在逆時(shí)針運(yùn)動(dòng)

再如

motor[0]->control(-2000);//控制左電機(jī)向前
motor[1]->control(2000);//控制右電機(jī)向前

對(duì)與小車就是在向前運(yùn)動(dòng)

總結(jié)就是control給定正值,輸出軸看電機(jī)順時(shí)針轉(zhuǎn)動(dòng)级及,反之給定負(fù)值逆時(shí)針轉(zhuǎn)動(dòng)

3.2電機(jī)順序

上面可以看出來motor[i]中的索引i

apollo中左電機(jī)為0乒疏,右電機(jī)為1
zeus中前電機(jī)為0,左電機(jī)為1饮焦,右電機(jī)為2
hadeshera中右后電機(jī)為0怕吴,右前電機(jī)為1,左前電機(jī)為2县踢,左后電機(jī)為3
具體編號(hào)源碼kinematic_models文件夾中相應(yīng)文件前右說明

3.3 編碼器

這里編碼器值是一個(gè)程序計(jì)數(shù)值转绷,一個(gè)方向轉(zhuǎn)動(dòng)編碼器值會(huì)累加,反方向會(huì)減少
PIBOT定義上面2.1中給定正值時(shí)硼啤,編碼器值累加议经,給定負(fù)是編碼器值減少

4.驗(yàn)證測(cè)試

如果上面有點(diǎn)晦澀,那直接按照如下方式測(cè)試即可
apollo為例

4.1 測(cè)試方向

do_kinmatics直接motor[0]->control(2000); return;

** 這里2000相對(duì)于PWM最大值來的Arduino最大1024 STM32最大設(shè)置為5000**

觀察左電機(jī)電機(jī)是否向后

分別查看下面各個(gè)控制與實(shí)際電機(jī)轉(zhuǎn)動(dòng)情況
motor[0]->control(2000) 左電機(jī)向后
motor[0]->control(-2000) 左電機(jī)向前
motor[1]->control(2000) 右電機(jī)向前
motor[1]->control(-2000) 右電機(jī)向后

如果得不到相應(yīng)的結(jié)果,根據(jù)情況調(diào)整:
a. motor[0]->control時(shí)右電機(jī)轉(zhuǎn)動(dòng)煞肾,可能考慮左右電機(jī)接線反了
b. motor[1]->control(xx) ,哪個(gè)電機(jī)是對(duì)的咧织,但發(fā)現(xiàn)不對(duì)〖龋可以調(diào)整電機(jī)接線习绢,也可以調(diào)整程序的方向控制

PIBOT提供相關(guān)的宏MOTORx_REVERSE可以不需要對(duì)換電機(jī)接線(PIN1和PIN6),達(dá)到調(diào)整方向的目的

4.2測(cè)試編碼器

恢復(fù)會(huì)正常程序并新增調(diào)試的輸出,連接調(diào)試串口蝙昙,打開串口調(diào)試工具
對(duì)應(yīng)到程序的輸出


觀察串口調(diào)試助手中的total_count=xx yy輸出闪萄,xx隨著左輪的向前轉(zhuǎn)動(dòng)越來越小,反之越來越大耸黑;yy隨著右輪的向前越來越大桃煎,反之越來越小

如果得不到相應(yīng)的結(jié)果,根據(jù)情況調(diào)整:
a. 左電機(jī)轉(zhuǎn)動(dòng)yy變化或者右電機(jī)轉(zhuǎn)動(dòng)xx變化大刊,那應(yīng)該是2個(gè)電機(jī)編碼器反了,需要調(diào)換下
b. 左電機(jī)轉(zhuǎn)動(dòng)xx變化三椿,但相反缺菌。應(yīng)該是編碼器AB相接線反了;右電機(jī)同理

PIBOT提供相關(guān)的宏ENCODERx_REVERSE可以不需要對(duì)換電機(jī)編碼器接線(PIN3和PIN4),達(dá)到調(diào)整方向的目的

5.程序修改

最新程序已經(jīng)支持直接修改配置方式搜锰, 具體請(qǐng)參考6.配置修改

固件程序提供了直接的宏伴郁,針對(duì)電機(jī)反向或者編碼器反向的問題
例如STM32F1電機(jī)方向反轉(zhuǎn)宏,修改宏定義



STM32F4編碼器方向反轉(zhuǎn)宏(在param.mk文件)


6. 配置修改

目前最新程序已經(jīng)支持動(dòng)態(tài)修改無需修改代碼重新編譯,

# 分別開啟2個(gè)終端執(zhí)行下面命令
pibot_bringup
pibot_configure
exchange.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蛋叼,隨后出現(xiàn)的幾起案子焊傅,更是在濱河造成了極大的恐慌,老刑警劉巖狈涮,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狐胎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡歌馍,警方通過查閱死者的電腦和手機(jī)握巢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來松却,“玉大人暴浦,你說我怎么就攤上這事∠停” “怎么了歌焦?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)砚哆。 經(jīng)常有香客問我独撇,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任券勺,我火速辦了婚禮绪钥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘关炼。我一直安慰自己程腹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布儒拂。 她就那樣靜靜地躺著寸潦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪社痛。 梳的紋絲不亂的頭發(fā)上见转,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音蒜哀,去河邊找鬼斩箫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛撵儿,可吹牛的內(nèi)容都是我干的乘客。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼淀歇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼易核!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浪默,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤牡直,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怔球,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年咽白,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡约急,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厌蔽,到底是詐尸還是另有隱情,我是刑警寧澤摔癣,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布奴饮,位于F島的核電站,受9級(jí)特大地震影響戴卜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜投剥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一师脂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧江锨,春花似錦吃警、人聲如沸啄育。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至完疫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盛龄。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啊鸭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓赠制,卻偏偏與公主長(zhǎng)得像挟憔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绊谭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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