在简单地形上小车运动轨迹的数学表达(二)
图形学课上的小小总结
前言
在上一篇文章中已经把小车四个轮子的位置都计算出来了。在小车的运动过程中,光是小车运动是不实际的,现实生活中车体会随着地形有起有伏,而且运动是在紧附在轮子上的。因此,可以根据轮子的坐标进而得到小车车体[*]的运动轨迹。
[*] 车体是指不包括车轮部分,后文也是如此。
前提
-
车体是个平行六面体(实际为长方体)。
-
不考虑地形比触碰到车体的情况。
根据上一篇文章的内容,可以得到这样的结果(图-1)
问题一:如何做出车体的运动轨迹?
该车体所拥有的特点是与前轮后轮所在轴垂直(图-2)(图还凑合。。。啦):
其实解决办法很简单,由于O0和O1坐标已经确定下来了,故这两点所在直线的斜率也有了,再已知车高H,车宽W(图中未标识),这样就可以得到同侧的四个点坐标,将这四个点坐标沿着Z方向平移即可得车体的八个点顶点坐标。
下面是计算过程:
假设后轮圆心所在坐标为O0(x0,y0),后轮的同侧车顶坐标为T(x1,y1),K0为O0点与O1点所在直线的斜率,K1为O0点与T点所在直线的斜率,有:
同理,前轮的同侧顶部坐标也能计算得出。
使用c++代码实现如下(实现所用的变量名称和假设推理的不一样):
/*
点集顺序如下
1-----------4
| |
2-----------3
*/
void get_car_coordinate(CC3DPoint &_point2, CC3DPoint &_point3, float _height, CC3DPoint &_point1, CC3DPoint &_point4){
float k0, k1, cos_alpha, sin_alpha;
k0 = (_point3.y - _point2.y) / (_point3.x - _point2.x);
if (k0 == 0.0){
_point1.x = _point2.x;
_point1.y = _point2.y + _height;
_point1.z = _point2.z;
_point4.x = _point3.x;
_point4.y = _point3.y + _height;
_point4.z = _point3.z;
return;
}
k1 = -1 / k0;
cos_alpha = sqrt(1 / ((k1 * k1) + 1));
sin_alpha = sqrt((k1 * k1) / (1 + k1*k1));
_point1.y = _point2.y + _height * sin_alpha;
_point4.y = _point3.y + _height * sin_alpha;
_point1.z = _point2.z;
_point4.z = _point2.z;
if (k0 > 0){
_point1.x = _point2.x - _height * cos_alpha;
_point4.x = _point3.x - _height * cos_alpha;
}
else{
_point1.x = _point2.x + _height * cos_alpha;
_point4.x = _point3.x + _height * cos_alpha;
}
}
根据这四个点绘制的效果如下:
将这四个坐标沿着Z轴进行平移可得到另一边的四个点,依据这八个点绘得图像可得:
这样,随着地形起伏的车体也就能够画出来了。
问题二:如何按照指定位移前进?
车的移动位移取决于轮子的旋转角度,角度决定着其运动的位移。设轮子每次转动的角度为θ,则轮子转动θ向前行进的路程s=θ/360*2πR,R为轮子半径。但地形不是直线,而是曲线,故要想车实际运动的路程等于s,则需要对地形曲线做积分计算,计算过程如下:
在已知了旋转角度θ的情况下,有两种思路可供解决这个问题:
-
对曲线上的点进行距离累加计算,直到累加的距离大于s。
-
对曲线弧进行第一类曲线积分计算。
针对于第一种方法:
其就是分段求距离,化曲为直。
//s : 为指定路程;_search_inc: 为递增变量;_x : 搜索起始;
// 累加的距离和
float sum = 0.0f;
// 前一个参考坐标
float x0 = _x, y0 = f(x0);
// 递增坐标
float tmp_x = x0, tmp_y = 0.0f;
// 当前计算距离
float dis = 0.0f;
while (true){
tmp_x += _search_inc;
tmp_y = f(tmp_x);
dis = dis_between_points(x0, y0, tmp_x, tmp_y);
x0 = tmp_x;
y0 = tmp_y;
sum += dis;
if (sum >= s){
// Already saved by _offset_x.
return;
}
else{
_offset_x = tmp_x;
}
}
针对第二种方法:
根据第一类曲线积分的方法可得:
float tmp_x = _x, sum = 0.0f, dx = _search_inc;
while (sum < s) {
tmp_x += dx;
sum += sqrt(1 + pow(derived_f(tmp_x), 2)) * abs(dx);
}
fprintf(stdout, "test_x : %lf
", tmp_x);
关于这两种方法的比较:
-
第一种需要计算累计距离,而每次计算的差值都→0,故由于机器浮点数计算而产生的误差会累积,而且计算量大。
-
第二种基于积分的计算,其本意是用积分计算积分,也存在计算的误差但是相比第一种的在直接机器计算上要小得多,计算量小。
-
我会使用第二种,理由是代码简短,基于优美的数学公式。
如此,在设定了轮子旋转角度的情况下,接着计算在曲线上的长度对应的x方向位移,使之画出一个轮子。
总结:
-
根据前一篇文章和这篇文章,总结了小车在简单已知地形上的运动轨迹。
-
但是这种小车的局限性实在是太大了,只能在x方向前后移动,而且地形函数十分简单,或者说不太复杂。
-
我还见识到了数值计算的力量,很多时候精确解并不是一件轻松的事情,可以尝试换个角度,使用近似解,照样可以达到满意的效果。
-
感觉还是学的太少,有些思维还是太局限,得继续学习与努力。
Thanks
3/31/2017 11:05:41 PM