本次实现的是一个三维运动轨迹的描述,我实现了匀速运动,匀加速运动。对于曲线运动,考虑到的情况是加速度和初速度方向不同的匀加速运动。
约束条件是运动轨迹不允许发生位置的突变,但速度和加速度可以突变,由自己自行分配。
在实现上,对于某一个运动段定义了一个Track类。而对于整个运动轨迹的操作,由LIst
模板类派生了普通类Track_List
。主要是由于约束条件的存在,需要更新后继结点,因此对某些函数进行了重载。
比起上一个大作业这个相对而言比较简单,主要是确定一个大的思路,之后就是在写的过程中逐渐改变自己的想法并且进行优化了。
比如这次对于存放采样数据的区域用哪种类型的就进行了多次尝试。
实现思路一:
首先我想使用上一章学的Vector模板,毕竟是一个封装好的容器。但是碰到的问题是使用Vector
的话,我希望在一开始就对track进行初始化,但是不知道怎么调用构造函数。就是说,在一个A类中,有一个B类的对象b,我应该在什么时候调用构造函数。查询以后可采用先在私有成员中声明一个Vector类型的指针,之后在Track的构造函数中构造一个Vector类型的实例,再用拷贝构造函数将其赋给Track类中的Vector类型私有成员变量实现初始化。但是总觉得这样很复杂。
实现思路二:
声明一个XYZ类型结构体指针,动态分配一维数组存放,分配在构造函数中进行。这样确实比较方便,但是在后续的编写过程中,主要是每对列表进行一次修改,就需要更新后续所有结点的位置,这样的话每个动态分配的数组的大小都需要发生改变,需要先释放再重新分配内存。
实现思路三:
既然题目要求的是将数据采样后直接输出,那么我可以认为采样的数据无需保存在每一个结点中。因此我采用在输出到文件时,对每个结点临时分配一个数组,将采样数据临时存放,再输出到文件后直接释放。
//最多考虑到加速度带来的运动变化,即加速度的一阶导为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
#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;
}
通过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;
其实这还算不上c++和matlab混合编程,本来想编译.m文件为c++的库直接调用,但是gcc编译器版本不太对,因此就没有继续尝试。
参考:ubuntu下C++如何调用matlab程序