ros moveit机械臂轨迹 三次插补

问题引入

  • 马上要进入研究生阶段的学习,跟导师研究机械臂的抓取问题,但本科阶段大多数都在研究一些深度学习算法,所以我准备从零开始搭建一个自己的机械臂,通过实践来掌握机械臂抓取问题的整个体系。在软件层面上我使用ros+moveit作为机械臂开发的主体,moveit可以帮助我们轻松的完成正逆解以及轨迹的规划,是非常好的工具。
  • moveit的输出一般是一系列的轨迹点,包含每一个关节的位置position[],速度velocities[]以及加速度accelerations[],通过action接口进行通讯,但是moveit直接输出的轨迹点数量很少并且没有进行插补,为了方便观察我将moveit输出的轨迹保存为.txt文件,使用matplotlib进行绘图如下图所示:
front_tpva.png

通过这张图可以看到moveit输出的轨迹并不圆滑,尤其是加速度曲线,这代表电机的力矩变化很大,会造成机械臂在运动的过程中抖动。

  • 所以我打算写一个三次轨迹插补的算法对moveit输出的轨迹进行插补,本篇文章的所有程序都脱离ros和moveit,我将moveit输出的轨迹选取一个关节保存到tractory.txt文件中,文件形式如下图:


    tractory.txt.png

第一行是时间,第二行是位置,第三行是速度,第四行是加速度,至于数据具体含义以及怎么使用moveit导出这个轨迹数据我在这篇文章中不去具体阐述。

  • 为了方便自己调试也便其他小伙伴复用,本篇文章可以脱离机械臂的具体问题,看作对tractory.txt文件中的数据进行三次插补。
    在介绍具体流程和代码之前我把我的工程目录放在下面。
工程目录.png

实现流程

1.读取tractory.txt文件并转化为double数组

这一部分我感觉写的有一些麻烦,因为我本身c++编程能力并不好,我本科是机械专业,之前编成用python比较多,这里谁有简单实现可以分享以下!

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型数组用来储存所有,的位置
            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,并存入数组
                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型数组用来储存所有,的位置
            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型数组用来储存所有,的位置
            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型数组用来储存所有,的位置
            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.进行轨迹三次插补

插补的具体实现我参照了古月老师的方法。边界条件都为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();
}
/* 初始化参数 */
void cubicSpline::initParam()
{
    x_sample_ = y_sample_ = M_ = NULL;
    sample_count_ = 0;
    bound1_ = bound2_ = 0;
}
/* 释放参数 */
void cubicSpline::releaseMem()
{
    delete x_sample_;
    delete y_sample_;
    delete M_;
 
    initParam();
}
/* 加载关节位置数组等信息 */
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;
    }
 
    //  追赶法解方程求二阶偏导数
    double f1=bound1_, f2=bound2_;
 
    double *a=new double[sample_count_];                //  a:稀疏矩阵最下边一串数
    double *b=new double[sample_count_];                //  b:稀疏矩阵最中间一串数
    double *c=new double[sample_count_];                //  c:稀疏矩阵最上边一串数
    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;                                //  中间一串数为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;
}
/* 得到速度和加速度数组 */
bool cubicSpline::getYbyX(double &x_in, double &y_out)
{
    int klo,khi,k;
    klo=0;
    khi=sample_count_-1;
    double hh,bb,aa;
 
    //  二分法查找x所在区间段
    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数据的程序,放到主函数的上面,我在上面写过了就不放上去了。

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

这段程序中包含了将插值后的数据保存到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

感觉整体来看变化不是很大,就是轨迹点多了一些,我这里插值了四倍,还是放到机械臂上跑一下看一下变化,这里我的机械臂还没有搭建完成,搭建完成再测试把!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容