数据结构大作业2:三维运动轨迹的描述

本次实现的是一个三维运动轨迹的描述,我实现了匀速运动,匀加速运动。对于曲线运动,考虑到的情况是加速度和初速度方向不同的匀加速运动。

约束条件是运动轨迹不允许发生位置的突变,但速度和加速度可以突变,由自己自行分配。

在实现上,对于某一个运动段定义了一个Track类。而对于整个运动轨迹的操作,由LIst模板类派生了普通类Track_List。主要是由于约束条件的存在,需要更新后继结点,因此对某些函数进行了重载。

比起上一个大作业这个相对而言比较简单,主要是确定一个大的思路,之后就是在写的过程中逐渐改变自己的想法并且进行优化了。
比如这次对于存放采样数据的区域用哪种类型的就进行了多次尝试。

实现思路一:
首先我想使用上一章学的Vector模板,毕竟是一个封装好的容器。但是碰到的问题是使用Vector的话,我希望在一开始就对track进行初始化,但是不知道怎么调用构造函数。就是说,在一个A类中,有一个B类的对象b,我应该在什么时候调用构造函数。查询以后可采用先在私有成员中声明一个Vector类型的指针,之后在Track的构造函数中构造一个Vector类型的实例,再用拷贝构造函数将其赋给Track类中的Vector类型私有成员变量实现初始化。但是总觉得这样很复杂。
数据结构大作业2:三维运动轨迹的描述_第1张图片
实现思路二:
声明一个XYZ类型结构体指针,动态分配一维数组存放,分配在构造函数中进行。这样确实比较方便,但是在后续的编写过程中,主要是每对列表进行一次修改,就需要更新后续所有结点的位置,这样的话每个动态分配的数组的大小都需要发生改变,需要先释放再重新分配内存。

实现思路三:
既然题目要求的是将数据采样后直接输出,那么我可以认为采样的数据无需保存在每一个结点中。因此我采用在输出到文件时,对每个结点临时分配一个数组,将采样数据临时存放,再输出到文件后直接释放。

List.h和listNode.h之前博客已经写过,此处略。

Track.h

//最多考虑到加速度带来的运动变化,即加速度的一阶导为0
#ifndef _TRACK_H_
#define _TRACK_H_
#include 
#include 
#include "List.h"
using namespace std;

struct XYZ{
    double _x;
    double _y;
    double _z; 
};//表示一个三维数据

class Track{
private:
    XYZ _ori_pos;//该运动轨迹段起始xyz位置
    XYZ _final_pos;//该运动轨迹段最终xyz位置
    XYZ _vel;//运动速度
    XYZ _accel;//加速度
    double _time; //该运动轨迹段的持续时间
    double _freq;//采样频率
    int _sp_num;//采样点的个数
public:
    Track(XYZ vel,XYZ accel,double time,double freq);//构造函数
    Track(){}//无参默认构造函数
    ~Track(){}//析构函数,因为构造时无动态申请内存,故无需显式声明析构函数
    Track(const Track &obj);//拷贝构造函数
    void init(XYZ vel,XYZ accel,double time,double freq);//用于初始化某个节点,主要用于构造函数和修改运动参数中使用
    void Uniform_motion(XYZ *sample);//匀速运动,本质是xyz三个方向的速度均保持不变,即三个方向加速度为0
    void Uniform_acceleration_motion(XYZ *sample);//匀加速运动,本质是合加速度的大小和方向均不变
    XYZ ori_pos(){return _ori_pos;}
    XYZ final_pos(){return _final_pos;}
    XYZ vel(){return _vel;}
    XYZ accel(){return _accel;}
    double time(){return _time;}
    double freq(){return _freq;}
    int sp_num(){return _sp_num;} //给外部接口访问私有成员变量
    void change_ori_pos(struct XYZ pos);
};

Track::Track(XYZ vel,XYZ accel,double time,double freq){
    _ori_pos._x = 0;
    _ori_pos._y = 0;
    _ori_pos._z = 0; // 设置初始位置为(0,0,0)
    init(vel,accel,time,freq);
}//调用init进行构造
Track::Track(const Track &obj){ //拷贝构造函数
    _ori_pos = obj._ori_pos;
    _final_pos = obj._final_pos;//起始时终端位置还不知道
    _vel = obj._vel;
    _accel = obj._accel;
    _time = obj._time;
    _freq = obj._freq;
    _sp_num = _time*_freq + 1;
}
void Track::init(XYZ vel,XYZ accel,double time,double freq){
    _vel = vel;
    _accel = accel;
    _time = time;
    _freq = freq;
    _sp_num = _time*_freq + 1;
    if(_accel._x == 0 && _accel._y == 0 && _accel._z == 0){// 构造函数中不用分配采样区的内存
        _final_pos._x = _ori_pos._x + _vel._x*_time;        //但需要得到末状态的位置,用于更新后继结点
        _final_pos._y = _ori_pos._y + _vel._y*_time;
        _final_pos._z = _ori_pos._z + _vel._z*_time;
    }
    else{
        _final_pos._x = _ori_pos._x + 0.5*_accel._x*_time*_time + _vel._x*_time;
        _final_pos._y = _ori_pos._y + 0.5*_accel._y*_time*_time + _vel._y*_time;
        _final_pos._z = _ori_pos._z + 0.5*_accel._z*_time*_time + _vel._z*_time;
    }
}
void Track::Uniform_motion(XYZ *sample){//匀速运动  x = x0+v*t
    double t = 1/_freq;
    sample[0]._x = _ori_pos._x;
    sample[0]._y = _ori_pos._y;
    sample[0]._z = _ori_pos._z;
    for(int i = 1;i < _sp_num;i++){ //给每个采样点赋值
        sample[i]._x = sample[i-1]._x + _vel._x*t;
        sample[i]._y = sample[i-1]._y + _vel._y*t;
        sample[i]._z = sample[i-1]._z + _vel._z*t;
    }
}
void Track::Uniform_acceleration_motion(XYZ *sample){ //匀加速运动,若加速度方向和初速度方向不同则为曲线运动,相同则为匀加速直线运动
    double t = 1/_freq;
    sample[0]._x = _ori_pos._x;
    sample[0]._y = _ori_pos._y;
    sample[0]._z = _ori_pos._z;
    for(int i = 1;i < _sp_num;i++){ //外层是给每个采样点赋值
        sample[i]._x = _ori_pos._x + 0.5*_accel._x*i*t*i*t + _vel._x*i*t;
        sample[i]._y = _ori_pos._y + 0.5*_accel._y*i*t*i*t + _vel._y*i*t;
        sample[i]._z = _ori_pos._z + 0.5*_accel._z*i*t*i*t + _vel._z*i*t;
    }
}
void Track::change_ori_pos(struct XYZ pos){
    _ori_pos._x = pos._x;
    _ori_pos._y = pos._y;
    _ori_pos._z = pos._z;
}//用于更新后继结点


class Track_List:public List<Track>{ //从模板类List派生普通类Track_List,用于完成一些特定的操作,比如更新后继结点
public:
    //默认调用父类无参数构造函数
    ListNodePosi(Track) insertAsFirst(Track const& e);//后插
    ListNodePosi(Track) insertAsLast(Track const& e);//后插
    ListNodePosi(Track) insertAfter ( ListNodePosi(Track) p, Track const& e); //将e当作p的后继插入(After)
    ListNodePosi(Track) insertBefore ( ListNodePosi(Track) p, Track const& e); //将e当作p的前驱插入(Before)
    ListNodePosi(Track) Track_Change(ListNodePosi(Track) p,XYZ vel,XYZ accel,double time,double freq); //改变第num个结点的运动轨迹参数,但是不能改位置,只能由前驱结点决定
    void update(ListNodePosi(Track) p); //更新位置
    void Track_Out(ListNodePosi(Track) p);
    void Track_List_Out();
    Track remove(ListNodePosi(Track) p);
    XYZ get_ori_pos(ListNodePosi(Track) p){return p->data.ori_pos();}
    XYZ get_final_pos(ListNodePosi(Track) p){return p->data.final_pos();}
    XYZ get_vel(ListNodePosi(Track) p){return p->data.vel();}
    XYZ get_accel(ListNodePosi(Track) p){return p->data.accel();}
    double get_time(ListNodePosi(Track) p){return p->data.time();}
    double get_freq(ListNodePosi(Track) p){return p->data.freq();}
};

ListNodePosi(Track) Track_List::insertAsFirst(Track const& e){
    _size++;   //e当作头节点插入
    header->insertAsSucc ( e );
    update(header->succ);
    return header->succ;
}
ListNodePosi(Track) Track_List::insertAsLast(Track const& e){
    _size++;   //e当作末节点插入
    trailer->insertAsPred ( e );
    update(trailer->pred->pred);//从尾结点的前两个结点开始更新,即只更新最后一个结点
    return trailer->pred;
}
ListNodePosi(Track) Track_List::insertAfter ( ListNodePosi(Track) p, Track const& e){ //将e当作p的后继插入(After)
    _size++; 
    p->insertAsSucc ( e );
    update(p);
    return p->succ;
}
ListNodePosi(Track) Track_List::insertBefore ( ListNodePosi(Track) p, Track const& e){ //将e当作p的前驱插入(Before)
    _size++; 
    p->insertAsPred ( e );
    update(p->pred->pred);
    return p->pred;
}
ListNodePosi(Track) Track_List::Track_Change(ListNodePosi(Track) p,XYZ vel,XYZ accel,double time,double freq){ //改变第num个结点的运动轨迹参数
    p->data.init(vel,accel,time,freq);
    return p;
}
Track Track_List::remove(ListNodePosi(Track) p){
    Track e = p->data; //备份待删除节点的数值(假定T类型可直接赋值)
    ListNodePosi(Track) q = p->pred;
    q->succ = p->succ; 
    p->succ->pred = q; //后继、前驱
    delete p; 
    _size--; //释放节点,更新规模
    update(q);//需要更新
    return e;
}
void Track_List::update(ListNodePosi(Track) p){ //更新后续节点的初始位置
    p = p->succ;
    while(p != trailer){ //未到达尾结点
        p->data.change_ori_pos(p->pred->data.final_pos());//某结点的初始位置应该为前驱结点的最终位置
        p = p->succ;
    } 
}
void Track_List::Track_Out(ListNodePosi(Track) p){
    ofstream outfile("Track_Data.txt",ios::out);
    XYZ *Sampling_points = new XYZ[p->data.sp_num()]; //分配一个采样点个数大小的空间
    if(p->data.accel()._x == 0 && p->data.accel()._y == 0 && p->data.accel()._z == 0){//三个方向加速度均为0
        p->data.Uniform_motion(Sampling_points);
    }
    else{
        p->data.Uniform_acceleration_motion(Sampling_points);
    }
    for(int i = 0;i<p->data.sp_num();i++){
        outfile<<Sampling_points[i]._x;
        outfile<<" ";
        outfile<<Sampling_points[i]._y;
        outfile<<" ";
        outfile<<Sampling_points[i]._z<<endl;
    }
    delete[] Sampling_points;
    p = p->succ;
    outfile.close();
}
void Track_List::Track_List_Out(){
    ofstream outfile("All_Track_Data.txt",ios::out);
    outfile<<0<<" "<<0<<" "<<0<<endl;
    ListNodePosi(Track) p = header->succ;
    while(p != trailer){
        XYZ *Sampling_points = new XYZ[p->data.sp_num()]; //分配一个采样点个数大小的空间
        if(p->data.accel()._x == 0 && p->data.accel()._y == 0 && p->data.accel()._z == 0){//三个方向加速度均为0
            p->data.Uniform_motion(Sampling_points);
        }
        else{
            p->data.Uniform_acceleration_motion(Sampling_points);
        }
        for(int i = 1;i<p->data.sp_num();i++){
            outfile<<Sampling_points[i]._x;
            outfile<<" ";
            outfile<<Sampling_points[i]._y;
            outfile<<" ";
            outfile<<Sampling_points[i]._z<<endl;
        }
        delete[] Sampling_points; //输出完采样点后及时释放申请的空间
        p = p->succ;//去操作下一个结点
    }
    outfile.close();
}

#endif

mian.cpp

#include "Track.h"
#include 
using namespace std;
int main(){
    //首结点默认起始位置为(0,0,0) 
    XYZ vel1 = {1,1,1};
    XYZ accel1 = {0,0,0};
    XYZ vel2 = {1,1,0};
    XYZ accel2 = {0,0,-1};
    XYZ vel3 = {1,0,1};
    XYZ accel3 = {1,0,-1};
    Track tra1(vel1,accel1,5,20),tra2(vel2,accel2,5,30),tra3(vel3,accel3,5,20);
    Track_List L;
    L.insertAsLast(tra1);
    L.insertAsLast(tra2);
    L.insertAsLast(tra3);
    //L.remove(L.List_Access(2));
    L.Track_List_Out();//输出完整运动轨迹数据
    L.Track_Out(L.List_Access(2));//输出第二段运动轨迹数据
    L.Track_Change(L.List_Access(2),vel1,accel1,5,33);
    cout << L.get_freq(L.List_Access(2));
    return 0;
}

demo.m

通过matlab进行运动轨迹图像的绘制

clc;
A = load("Track_Data.txt");
B = load("All_Track_Data.txt");
xa=A(:,1);
ya=A(:,2);
za=A(:,3);
figure(1);
plot3(xa,ya,za);
grid on;
xlabel('x');
ylabel('y');
zlabel('z');
hold on;
xb=B(:,1);
yb=B(:,2);
zb=B(:,3);
figure(2);
plot3(xb,yb,zb);
grid on;
xlabel('x');
ylabel('y');
zlabel('z');
hold on;

运行效果

数据结构大作业2:三维运动轨迹的描述_第2张图片
其实这还算不上c++和matlab混合编程,本来想编译.m文件为c++的库直接调用,但是gcc编译器版本不太对,因此就没有继续尝试。
参考:ubuntu下C++如何调用matlab程序

输出文件:

数据结构大作业2:三维运动轨迹的描述_第3张图片
数据结构大作业2:三维运动轨迹的描述_第4张图片
输出均为n×3的矩阵

你可能感兴趣的:(数据结构,c++)