ros moveit機械臂軌跡 三次插補

問題引入

  • 馬上要進入研究生階段的學習螟炫,跟導師研究機械臂的抓取問題,但本科階段大多數(shù)都在研究一些深度學習算法惭婿,所以我準備從零開始搭建一個自己的機械臂不恭,通過實踐來掌握機械臂抓取問題的整個體系。在軟件層面上我使用ros+moveit作為機械臂開發(fā)的主體财饥,moveit可以幫助我們輕松的完成正逆解以及軌跡的規(guī)劃换吧,是非常好的工具。
  • moveit的輸出一般是一系列的軌跡點钥星,包含每一個關節(jié)的位置position[],速度velocities[]以及加速度accelerations[],通過action接口進行通訊沾瓦,但是moveit直接輸出的軌跡點數(shù)量很少并且沒有進行插補,為了方便觀察我將moveit輸出的軌跡保存為.txt文件,使用matplotlib進行繪圖如下圖所示:
front_tpva.png

通過這張圖可以看到moveit輸出的軌跡并不圓滑贯莺,尤其是加速度曲線风喇,這代表電機的力矩變化很大,會造成機械臂在運動的過程中抖動缕探。

  • 所以我打算寫一個三次軌跡插補的算法對moveit輸出的軌跡進行插補魂莫,本篇文章的所有程序都脫離ros和moveit,我將moveit輸出的軌跡選取一個關節(jié)保存到tractory.txt文件中爹耗,文件形式如下圖:


    tractory.txt.png

第一行是時間耙考,第二行是位置,第三行是速度潭兽,第四行是加速度倦始,至于數(shù)據(jù)具體含義以及怎么使用moveit導出這個軌跡數(shù)據(jù)我在這篇文章中不去具體闡述。

  • 為了方便自己調試也便其他小伙伴復用山卦,本篇文章可以脫離機械臂的具體問題鞋邑,看作對tractory.txt文件中的數(shù)據(jù)進行三次插補。
    在介紹具體流程和代碼之前我把我的工程目錄放在下面账蓉。
工程目錄.png

實現(xiàn)流程

1.讀取tractory.txt文件并轉化為double數(shù)組

這一部分我感覺寫的有一些麻煩枚碗,因為我本身c++編程能力并不好,我本科是機械專業(yè)剔猿,之前編成用python比較多视译,這里誰有簡單實現(xiàn)可以分享以下!

struct String_data
{
    string t_str;
    string pos_str;
    string vel_str; 
    string acc_str;
};

class Deal_data
{
public:


    void load_data(const char * name)
    {
        ifstream openfile;
        openfile.open(name,ios::in);
        if (!openfile.is_open())
        {
            cout << "file can't open!" << endl;
        }
        string buf;
        int count = 0;
        while(getline(openfile,buf))
        {
            if (count == 0)
            {
                str_data.t_str = buf;
                
            }
            if(count==1)
            {
                str_data.pos_str = buf;
            }
            if(count==2)
            {
                str_data.vel_str = buf;
            }   
            if(count==3)
            {
                str_data.acc_str = buf;
            }   
            count ++;   
        }
    }

    void get_point_num()
    {
        int p = 0;
        int num = 0;
        int splite_position[100];
        string t_str_inner = str_data.t_str; 
        while(t_str_inner.find(",",p)!=string::npos)
        {
            p = t_str_inner.find(",",p);
            // int型數(shù)組用來儲存所有,的位置
            splite_position[num] = p;
            p = p + 1;
            num ++;
        }
        point_num = num;

    }

    double * get_data_t()
    {
        int p = 0;
        int num = 0;
        int splite_position[100];
        string t_str_inner = str_data.t_str; 
        while(t_str_inner.find(",",p)!=string::npos)
        {
            p = t_str_inner.find(",",p);
            splite_position[num] = p;
            p = p + 1;
            num ++;
        }
        char t_char[point_num];
        double * t = new double[point_num];
        for (int i=0;i<point_num;i++)
        {
            int end = splite_position[i];
            if(i==0)
            {
                // 字符串分割
                string str = t_str_inner.substr(0,end);
                // char轉double,并存入數(shù)組
                t[i] = atof(str.c_str());
                // cout << t[i] << endl;
            }
            else
            {
                int start = splite_position[i-1]+1;
                string str = t_str_inner.substr(start,end - start); 
                t[i] = atof(str.c_str());
                // cout << t[i] << endl;
            }
        }
        return t;
    }

    double * get_data_pos()
    {
        int p = 0;
        int num = 0;
        int splite_position[100];
        string pos_str_inner = str_data.pos_str; 
        while(pos_str_inner.find(",",p)!=string::npos)
        {
            p = pos_str_inner.find(",",p);
            // int型數(shù)組用來儲存所有,的位置
            splite_position[num] = p;
            p = p + 1;
            num ++;
        }
        char t_char[point_num];
        double * pos = new double[point_num];
        for (int i=0;i<point_num;i++)
        {
            int end = splite_position[i];
            if(i==0)
            {
                string str = pos_str_inner.substr(0,end);
                pos[i] = atof(str.c_str());
            }
            else
            {
                int start = splite_position[i-1]+1;
                string str = pos_str_inner.substr(start,end - start); 
                pos[i] = atof(str.c_str());
            }
        }
        return pos;
    }
    double * get_data_vel()
    {
        int p = 0;
        int num = 0;
        int splite_position[100];
        string vel_str_inner = str_data.vel_str; 
        while(vel_str_inner.find(",",p)!=string::npos)
        {
            p = vel_str_inner.find(",",p);
            // int型數(shù)組用來儲存所有,的位置
            splite_position[num] = p;
            p = p + 1;
            num ++;
        }
        char t_char[point_num];
        double * vel = new double[point_num];
        for (int i=0;i<point_num;i++)
        {
            int end = splite_position[i];
            if(i==0)
            {
                string str = vel_str_inner.substr(0,end);
                vel[i] = atof(str.c_str());
            }
            else
            {
                int start = splite_position[i-1]+1;
                string str = vel_str_inner.substr(start,end - start); 
                vel[i] = atof(str.c_str());
            }
        }
        return vel;
    }

    double * get_data_acc()
    {
        int p = 0;
        int num = 0;
        int splite_position[100];
        string acc_str_inner = str_data.acc_str; 
        while(acc_str_inner.find(",",p)!=string::npos)
        {
            p = acc_str_inner.find(",",p);
            // int型數(shù)組用來儲存所有,的位置
            splite_position[num] = p;
            p = p + 1;
            num ++;
        }
        char t_char[point_num];
        double * acc = new double[point_num];
        for (int i=0;i<point_num;i++)
        {
            int end = splite_position[i];
            if(i==0)
            {
                string str = acc_str_inner.substr(0,end);
                acc[i] = atof(str.c_str());
            }
            else
            {
                int start = splite_position[i-1]+1;
                string str = acc_str_inner.substr(start,end - start); 
                acc[i] = atof(str.c_str());
            }
        }
        return acc;
    }
    int point_num;
private:
    struct String_data str_data;

};

1.進行軌跡三次插補

插補的具體實現(xiàn)我參照了古月老師的方法归敬。邊界條件都為0酷含。
cublicSpline.h頭文件

#ifndef _CUBIC_SPLINE_H_
#define _CUBIC_SPLINE_H_
 
class cubicSpline
{
public:
    typedef enum _BoundType
    {
        BoundType_First_Derivative,
        BoundType_Second_Derivative
    }BoundType;
 
public:
    cubicSpline();
    ~cubicSpline();
 
    void initParam();
    void releaseMem();
 
    bool loadData(double *x_data, double *y_data, int count, double bound1, double bound2, BoundType type);
    bool getYbyX(double &x_in, double &y_out);
 
protected:
    bool spline(BoundType type);
 
protected:
    double *x_sample_, *y_sample_;
    double *M_;
    int sample_count_;
    double bound1_, bound2_;
};
#endif /* _CUBIC_SPLINE_H_ */

cublicSpine.cpp源文件

/* 三次樣條插補 */
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include "cubicSpline.h"

using namespace std;
/* 初始化輸入輸出速度加速度 */
double acc = 0, vel = 0;
double x_out = 0, y_out = 0;

/* 三次樣條無參構造 */
cubicSpline::cubicSpline()
{
}
/* 析構 */
cubicSpline::~cubicSpline()
{
    releaseMem();
}
/* 初始化參數(shù) */
void cubicSpline::initParam()
{
    x_sample_ = y_sample_ = M_ = NULL;
    sample_count_ = 0;
    bound1_ = bound2_ = 0;
}
/* 釋放參數(shù) */
void cubicSpline::releaseMem()
{
    delete x_sample_;
    delete y_sample_;
    delete M_;
 
    initParam();
}
/* 加載關節(jié)位置數(shù)組等信息 */
bool cubicSpline::loadData(double *x_data, double *y_data, int count, double bound1, double bound2, BoundType type)
{
    if ((NULL == x_data) || (NULL == y_data) || (count < 3) || (type > BoundType_Second_Derivative) || (type < BoundType_First_Derivative))
    {
        return false;
    }
 
    initParam();
 
    x_sample_ = new double[count];
    y_sample_ = new double[count];
    M_        = new double[count];
    sample_count_ = count;
 
    memcpy(x_sample_, x_data, sample_count_*sizeof(double));
    memcpy(y_sample_, y_data, sample_count_*sizeof(double));
 
    bound1_ = bound1;
    bound2_ = bound2;
 
    return spline(type);
}
/* 計算樣條插值 */
bool cubicSpline::spline(BoundType type)
{
    if ((type < BoundType_First_Derivative) || (type > BoundType_Second_Derivative))
    {
        return false;
    }
 
    //  追趕法解方程求二階偏導數(shù)
    double f1=bound1_, f2=bound2_;
 
    double *a=new double[sample_count_];                //  a:稀疏矩陣最下邊一串數(shù)
    double *b=new double[sample_count_];                //  b:稀疏矩陣最中間一串數(shù)
    double *c=new double[sample_count_];                //  c:稀疏矩陣最上邊一串數(shù)
    double *d=new double[sample_count_];
 
    double *f=new double[sample_count_];
 
    double *bt=new double[sample_count_];
    double *gm=new double[sample_count_];
 
    double *h=new double[sample_count_];
 
    for(int i=0;i<sample_count_;i++)
        b[i]=2;                                //  中間一串數(shù)為2
    for(int i=0;i<sample_count_-1;i++)
        h[i]=x_sample_[i+1]-x_sample_[i];      // 各段步長
    for(int i=1;i<sample_count_-1;i++)
        a[i]=h[i-1]/(h[i-1]+h[i]);
    a[sample_count_-1]=1;
 
    c[0]=1;
    for(int i=1;i<sample_count_-1;i++)
        c[i]=h[i]/(h[i-1]+h[i]);
 
    for(int i=0;i<sample_count_-1;i++)
        f[i]=(y_sample_[i+1]-y_sample_[i])/(x_sample_[i+1]-x_sample_[i]);
 
    for(int i=1;i<sample_count_-1;i++)
        d[i]=6*(f[i]-f[i-1])/(h[i-1]+h[i]);
 
    //  追趕法求解方程
    if(BoundType_First_Derivative == type)
    {
        d[0]=6*(f[0]-f1)/h[0];
        d[sample_count_-1]=6*(f2-f[sample_count_-2])/h[sample_count_-2];
 
        bt[0]=c[0]/b[0];
        for(int i=1;i<sample_count_-1;i++)
            bt[i]=c[i]/(b[i]-a[i]*bt[i-1]);
 
        gm[0]=d[0]/b[0];
        for(int i=1;i<=sample_count_-1;i++)
            gm[i]=(d[i]-a[i]*gm[i-1])/(b[i]-a[i]*bt[i-1]);
 
        M_[sample_count_-1]=gm[sample_count_-1];
        for(int i=sample_count_-2;i>=0;i--)
            M_[i]=gm[i]-bt[i]*M_[i+1];
    }
    else if(BoundType_Second_Derivative == type)
    {
        d[1]=d[1]-a[1]*f1;
        d[sample_count_-2]=d[sample_count_-2]-c[sample_count_-2]*f2;
 
        bt[1]=c[1]/b[1];
        for(int i=2;i<sample_count_-2;i++)
            bt[i]=c[i]/(b[i]-a[i]*bt[i-1]);
 
        gm[1]=d[1]/b[1];
        for(int i=2;i<=sample_count_-2;i++)
            gm[i]=(d[i]-a[i]*gm[i-1])/(b[i]-a[i]*bt[i-1]);
 
        M_[sample_count_-2]=gm[sample_count_-2];
        for(int i=sample_count_-3;i>=1;i--)
            M_[i]=gm[i]-bt[i]*M_[i+1];
 
        M_[0]=f1;
        M_[sample_count_-1]=f2;
    }
    else
        return false;
 
    delete a;
    delete b;
    delete c;
    delete d;
    delete gm;
    delete bt;
    delete f;
    delete h;
 
    return true;
}
/* 得到速度和加速度數(shù)組 */
bool cubicSpline::getYbyX(double &x_in, double &y_out)
{
    int klo,khi,k;
    klo=0;
    khi=sample_count_-1;
    double hh,bb,aa;
 
    //  二分法查找x所在區(qū)間段
    while(khi-klo>1)
    {
        k=(khi+klo)>>1;
        if(x_sample_[k]>x_in)
            khi=k;
        else
            klo=k;
    }
    hh=x_sample_[khi]-x_sample_[klo];
 
    aa=(x_sample_[khi]-x_in)/hh;
    bb=(x_in-x_sample_[klo])/hh;
 
    y_out=aa*y_sample_[klo]+bb*y_sample_[khi]+((aa*aa*aa-aa)*M_[klo]+(bb*bb*bb-bb)*M_[khi])*hh*hh/6.0;
 
    //test
    acc = (M_[klo]*(x_sample_[khi]-x_in) + M_[khi]*(x_in - x_sample_[klo])) / hh;
    vel = M_[khi]*(x_in - x_sample_[klo]) * (x_in - x_sample_[klo]) / (2 * hh)
          - M_[klo]*(x_sample_[khi]-x_in) * (x_sample_[khi]-x_in) / (2 * hh)
          + (y_sample_[khi] - y_sample_[klo])/hh
          - hh*(M_[khi] - M_[klo])/6;
    //test end
 
    return true;
}

3.main.cpp以及CMakeLists.txt

別忘了把讀取tractory.txt數(shù)據(jù)的程序,放到主函數(shù)的上面汪茧,我在上面寫過了就不放上去了椅亚。

#include<iostream>
#include<stdio.h>
#include<stddef.h>
#include<string.h>
#include<fstream>
#include<vector>
#include "cubicSpline.h"
double acc = 0, vel = 0;
double x_out = 0, y_out = 0;
int main()
{
    setlocale(LC_ALL,"");
    char file_name[] = "/home/zhw/slam/learn_c++/TRY_SPLINE/src/tractory.txt";
    Deal_data opf;
    opf.load_data(file_name);
    opf.get_point_num();
    int point_num = opf.point_num;
    cout << opf.point_num << endl;
    double * time = opf.get_data_t();
    double * pos = opf.get_data_pos();
    double * vel_ = opf.get_data_vel();
    double * acc_ = opf.get_data_acc();

    cubicSpline spline;
    // lumbar test
    spline.loadData(time,pos, point_num, 0, 0, cubicSpline::BoundType_First_Derivative);
    double rate = (time[point_num-1] - time[0])/(point_num*4);
    double deal_pos[point_num*4];
    double deal_vel[point_num*4];
    double deal_acc[point_num*4];
    double time_from_start_[point_num*4];
    for (int k = 0; k <= point_num*4 ; k++) {
        printf("[---位置、速度舱污、加速度---]");
        printf("%0.9f, %0.9f, %0.9f\n",y_out, vel, acc);
        spline.getYbyX(x_out, y_out);
        time_from_start_[k] = x_out;
        deal_pos[k] = y_out;
        deal_vel[k] = vel;
        deal_acc[k] = acc;
        x_out += rate;
    }
    FILE *f;
    f = fopen("/home/zhw/slam/learn_c++/TRY_SPLINE/deal_tractory.txt","a");
    for(int j=0;j<=point_num*4;j++)
    {
        fprintf(f,"%f,",time_from_start_[j]);//6
    }
    fprintf(f,"\n");
    for(int j=0;j<=point_num*4;j++)
    {
        fprintf(f,"%f,",deal_pos[j]);//7
    }
    fprintf(f,"\n");
    for(int j=0;j<=point_num*4;j++)
    {
        fprintf(f,"%f,",deal_vel[j]);//8
    }
    fprintf(f,"\n");
    for(int j=0;j<=point_num*4;j++)
    {
        fprintf(f,"%f,",deal_acc[j]);//9
    }
    fprintf(f,"\n");
    fclose(f);

    return 0;
}

這段程序中包含了將插值后的數(shù)據(jù)保存到deal_tractory.txt文件中呀舔。

cmake_minimum_required(VERSION 3.0.2)
include_directories(${CMAKE_SOURCE_DIR}/include)
add_library(cubicSpline SHARED cubicSpline.cpp)
add_executable(main main.cpp)
target_link_libraries(main cubicSpline)

結果

最后把插值前插值后的對比圖show一下!@┑啤媚赖!


deal_tpva.png

感覺整體來看變化不是很大,就是軌跡點多了一些珠插,我這里插值了四倍惧磺,還是放到機械臂上跑一下看一下變化,這里我的機械臂還沒有搭建完成捻撑,搭建完成再測試把磨隘!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末缤底,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子番捂,更是在濱河造成了極大的恐慌个唧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件设预,死亡現(xiàn)場離奇詭異徙歼,居然都是意外死亡,警方通過查閱死者的電腦和手機絮缅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門鲁沥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耕魄,你說我怎么就攤上這事∨硭” “怎么了吸奴?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缠局。 經(jīng)常有香客問我则奥,道長,這世上最難降的妖魔是什么狭园? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任读处,我火速辦了婚禮,結果婚禮上唱矛,老公的妹妹穿的比我還像新娘罚舱。我一直安慰自己,他們只是感情好绎谦,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布管闷。 她就那樣靜靜地躺著,像睡著了一般窃肠。 火紅的嫁衣襯著肌膚如雪包个。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天冤留,我揣著相機與錄音碧囊,去河邊找鬼。 笑死纤怒,一個胖子當著我的面吹牛糯而,可吹牛的內容都是我干的。 我是一名探鬼主播肪跋,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼歧蒋,長吁一口氣:“原來是場噩夢啊……” “哼土砂!你這毒婦竟也來了?” 一聲冷哼從身側響起谜洽,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤萝映,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阐虚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序臂,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年实束,在試婚紗的時候發(fā)現(xiàn)自己被綠了奥秆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡咸灿,死狀恐怖构订,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情避矢,我是刑警寧澤悼瘾,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站审胸,受9級特大地震影響亥宿,放射性物質發(fā)生泄漏。R本人自食惡果不足惜砂沛,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一烫扼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碍庵,春花似錦映企、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至马绝,卻和暖如春豆赏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背富稻。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工掷邦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人椭赋。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓抚岗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哪怔。 傳聞我的和親對象是個殘疾皇子宣蔚,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容