在简单地形上小车运动轨迹的数学表达(二)

在简单地形上小车运动轨迹的数学表达(二)

图形学课上的小小总结


前言

在上一篇文章中已经把小车四个轮子的位置都计算出来了。在小车的运动过程中,光是小车运动是不实际的,现实生活中车体会随着地形有起有伏,而且运动是在紧附在轮子上的。因此,可以根据轮子的坐标进而得到小车车体[*]的运动轨迹。

[*] 车体是指不包括车轮部分,后文也是如此。

前提

  1. 车体是个平行六面体(实际为长方体)。

  2. 不考虑地形比触碰到车体的情况。

根据上一篇文章的内容,可以得到这样的结果(图-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,则需要对地形曲线做积分计算,计算过程如下:

在已知了旋转角度θ的情况下,有两种思路可供解决这个问题:

  1. 对曲线上的点进行距离累加计算,直到累加的距离大于s。

  2. 对曲线弧进行第一类曲线积分计算。

针对于第一种方法:

其就是分段求距离,化曲为直。

	//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

提供在线的公式编辑

提供在线的函数绘制

Markdownpad

3/31/2017 11:05:41 PM

原文地址:https://www.cnblogs.com/leihui/p/6654016.html