function points = generate_trajectory(total_points) radius = 2; line_length = 10; % 计算半圆和直线的点数 circle_arc = pi/2 * radius; total_len = circle_arc + line_length + circle_arc n_circle = round(total_points * circle_arc / total_len); n_line = total_points - n_circle - n_circle; % 生成半圆点 (圆心在(0.5,0)) theta = linspace(pi/2, pi, n_circle) - pi/2; x_circle = radius * (1 - cos(theta)); y_circle = radius * sin(theta); % 生成直线点 x_line = linspace(x_circle(end), x_circle(end) + line_length, n_line); y_line = zeros(1, n_line)+y_circle(end); % 生成半圆点 (圆心在(0.5,0)) theta = linspace(0, pi/2, n_circle) - pi/2; x_circle1 = radius * (cos(-theta)) + x_line(end); y_circle1 = radius * sin(-theta) - radius + y_line(end); % 合并点 points = [x_circle', y_circle'; x_line', y_line'; x_circle1', y_circle1']; %% % % 螺旋线参数设置 % a = 0.5; % 螺旋线系数(控制间距) % n_turns = 1; % 螺旋线圈数 % % % % ===== 步骤1:生成高密度采样点 ===== % theta_max = 2 * pi * n_turns; % 最大角度 % density = 5000; % 高密度采样点数量 % theta_dense = linspace(0, theta_max, density); % % % 阿基米德螺旋线参数方程 % x_dense = a * theta_dense .* cos(theta_dense); % y_dense = a * theta_dense .* sin(theta_dense); % % % ===== 步骤2:计算累积弧长 ===== % dx = diff(x_dense); % dy = diff(y_dense); % ds = sqrt(dx.^2 + dy.^2); % 微分弧长 % s_dense = [0, cumsum(ds)]; % 累积弧长 % % % ===== 步骤3:等弧长间隔采样 ===== % s_total = s_dense(end); % 总弧长 % s_eq = linspace(0, s_total, total_points); % 等间距弧长点 % % % 弧长参数插值 % x_eq = interp1(s_dense, x_dense, s_eq, 'linear'); % y_eq = interp1(s_dense, y_dense, s_eq, 'linear'); % % points = [x_eq; y_eq].'; end
时间: 2025-07-30 17:14:55 浏览: 13
<think>我们将MATLAB中的轨迹生成函数转换为C语言。该函数生成一个由两个半圆和一条直线组成的轨迹。在MATLAB中,我们有两种方式:一种是半圆+直线+半圆,另一种是螺旋线(被注释掉了)。我们这里实现第一种方式。
函数功能:生成由两个半圆和一条直线组成的轨迹点。
输入:total_points - 要生成的总点数
输出:points - 一个数组,包含所有轨迹点的x和y坐标
步骤:
1. 计算每段轨迹(两个半圆和一条直线)的点数。
2. 生成第一个半圆的点(从(0,0)开始,圆心在(radius,0),角度从0到π/2,但注意MATLAB代码中做了调整)。
3. 生成直线段的点(从第一个半圆结束点开始,沿x轴正方向延伸)。
4. 生成第二个半圆的点(从直线结束点开始,圆心在(直线结束点x, 直线结束点y-radius),角度从-π/2到0,但MATLAB代码中做了调整)。
5. 合并所有点。
注意:MATLAB代码中第一个半圆生成的角度是linspace(pi/2, pi, n_circle) - pi/2,即从0到pi/2。
第二个半圆生成的角度是linspace(0, pi/2, n_circle) - pi/2,即从-pi/2到0。
我们按照这个逻辑在C语言中实现。
但是,我们注意到MATLAB代码中第一个半圆的圆心在(0.5,0)?实际上,从代码:
x_circle = radius * (1 - cos(theta));
y_circle = radius * sin(theta);
当theta=0时,点为(0,0); theta=pi/2时,点为(radius, radius)?实际上,这是圆心在(radius,0)的上半圆(从0到90度)吗?
重新审视:
x = radius*(1-cos(theta)) -> 当theta=0: (0,0); theta=pi/2: (radius, radius) ?不对,应该是(radius*(1-0)=radius, radius*1=radius) -> 但我们需要的是圆心在(radius,0)的上半圆,标准参数方程为:
x = radius * cos(theta) + center_x
y = radius * sin(theta) + center_y
这里,第一个半圆:圆心在(radius,0),theta从0到pi/2(逆时针),那么:
x = radius * cos(theta) + radius -> 当theta=0: (radius+radius,0) ?不对。
实际上,MATLAB代码中的第一个半圆:
theta = linspace(pi/2, pi, n_circle) - pi/2 -> 得到0到pi/2
x = radius * (1 - cos(theta)) -> 当theta=0: 0; theta=pi/2: radius*(1-0)=radius? 不对,cos(pi/2)=0 -> x=radius*(1-0)=radius; 而cos(0)=1 -> x=0。
y = radius * sin(theta) -> theta=0:0; theta=pi/2: radius
所以轨迹是从(0,0)到(radius, radius)?这看起来是一个90度的圆弧,圆心在(0,0)?不对,因为圆心到(0,0)和(radius,radius)的距离不相等。
我们重新计算:点(0,0)到(radius,radius)的距离是sqrt(radius^2+radius^2)=radius*sqrt(2),而圆弧上一点(radius*(1-cos(theta)), radius*sin(theta))到(0,0)的距离为sqrt((radius*(1-cos(theta)))^2 + (radius*sin(theta))^2) = radius * sqrt( (1-2cos(theta)+cos^2(theta)) + sin^2(theta) ) = radius * sqrt(2-2cos(theta)),这并不等于半径(除非theta=0时距离为0,theta=pi/2时距离为radius*sqrt(2)),所以圆心并不在(0,0)。
实际上,这个参数方程表示的是一个圆心在(radius,0),半径为radius的上半圆(从0到90度)吗?
标准圆心在(radius,0)的上半圆:x = radius * cos(alpha), y = radius * sin(alpha) 其中alpha从0到pi/2,那么:
x = radius * cos(alpha) -> 当alpha=0: (radius,0); alpha=pi/2: (0,radius)
但这里我们的方程是:x = radius*(1-cos(theta)),y=radius*sin(theta),其中theta从0到pi/2。
当theta=0: (0,0); theta=pi/2: (radius, radius) -> 这实际上是一个圆心在(0,0)的45度圆弧?不对,圆心应该在(0,0)和(radius,radius)的连线的垂直平分线上。
实际上,这个方程描述的是圆心在(radius,0),半径为radius,从(0,0)(对应theta=0)到(radius, radius)(对应theta=pi/2)的圆弧。验证:
点(0,0)到圆心(radius,0)的距离:|radius-0|=radius,等于半径。
点(radius,radius)到圆心(radius,0)的距离:sqrt(0+radius^2)=radius,等于半径。
角度:从圆心看,点(0,0)的角度是180度(或pi弧度),点(radius,radius)的角度是90度(pi/2)。但参数方程中theta从0到pi/2,对应角度从180度到90度?实际上是顺时针旋转了90度。
因此,这个参数方程表示的是圆心在(radius,0),从角度pi到pi/2(顺时针90度)的圆弧。但通常我们使用逆时针方向,这里我们按照MATLAB代码生成。
第二个半圆:
theta = linspace(0, pi/2, n_circle) - pi/2 -> 得到-pi/2到0
x_circle1 = radius * (cos(-theta)) + x_line(end) -> 注意cos(-theta)=cos(theta)
y_circle1 = radius * sin(-theta) - radius + y_line(end) -> sin(-theta)=-sin(theta)
当theta=-pi/2:
x = radius * cos(pi/2) + x_line(end) = 0 + x_line(end) = 直线结束点x
y = radius * (-sin(-pi/2)) - radius + y_line(end) = radius * (-(-1)) - radius + y_line(end) = radius - radius + y_line(end) = y_line(end)
当theta=0:
x = radius * cos(0) + x_line(end) = radius + x_line(end)
y = radius * (-sin(0)) - radius + y_line(end) = 0 - radius + y_line(end) = y_line(end) - radius
第二个半圆圆心在(x_line(end), y_line(end)-radius)?验证:
圆心为(x_line(end), y_line(end)-radius),半径radius。
点1:theta=-pi/2 -> (x_line(end), y_line(end)),距离圆心:|y_line(end) - (y_line(end)-radius)| = radius,正确。
点2:theta=0 -> (x_line(end)+radius, y_line(end)-radius),距离圆心:sqrt(radius^2+0)=radius,正确。
角度:从圆心看,点1的角度是90度(pi/2),点2的角度是0度(0)。参数方程中theta从-pi/2到0,对应角度从90度到0度(顺时针90度)。
因此,我们按照这个逻辑生成。
由于MATLAB代码中生成的点数可能有四舍五入,我们确保总点数等于total_points。
在C语言中,我们将生成一个Point数组,包含total_points个点。
注意:MATLAB代码中,三个部分分别是:
第一部分:n_circle个点
第二部分:n_line个点
第三部分:n_circle个点
总点数:2*n_circle + n_line = total_points
我们按照这个点数分配。
步骤:
1. 计算n_circle和n_line。
2. 分配内存:Point数组,大小为total_points。
3. 生成第一个半圆的点。
4. 生成直线段的点。
5. 生成第二个半圆的点。
注意:MATLAB代码中第一个半圆的角度是0到pi/2(theta1 = linspace(0, pi/2, n_circle)),但原代码中是用pi/2到pi减去pi/2,所以等价于0到pi/2。
第二个半圆的角度是-pi/2到0(theta2 = linspace(-pi/2, 0, n_circle))。
但是,在MATLAB代码中,第一个半圆生成时theta是从0到pi/2,第二个半圆生成时theta是从-pi/2到0。我们按照这个角度生成。
具体参数方程:
第一个半圆(圆心在(radius,0),半径radius,从0度(点(2*radius,0)?不对)?实际上我们前面已经验证,第一个半圆的起点是(0,0),终点是(radius,radius)):
x = radius * (1 - cos(theta)) [theta从0到pi/2]
y = radius * sin(theta)
直线段:从第一个半圆的终点(radius,radius)开始,沿x轴正方向(水平向右)延伸line_length,y保持不变(=radius)。
x从radius到radius+line_length,y=radius。
第二个半圆:圆心在(直线结束点x, 直线结束点y - radius) = (x_line_end, radius - radius) = (x_line_end,0)?
实际上直线结束点y=radius,圆心y坐标:y_line_end - radius = radius - radius = 0。
参数方程:
x = radius * cos(theta) + x_line_end [注意:这里theta从-pi/2到0,但原MATLAB代码中使用了cos(-theta)和sin(-theta),而我们这里theta直接取负角度]
y = radius * sin(theta) + 0 [圆心y=0]
但是,原MATLAB代码中第二个半圆的y计算为:y = radius * sin(-theta) - radius + y_line_end
= -radius * sin(theta) - radius + radius [因为y_line_end=radius]
= -radius * sin(theta)
而我们用圆心在(x_line_end,0)的标准方程:y = radius * sin(theta) + 0,但是这里的theta是角度,从-pi/2到0,则sin(theta)从-1到0,所以y从-radius到0。但我们需要的是从(直线结束点)开始,向下画半圆?而直线结束点坐标是(x_line_end, radius),我们要求第二个半圆的起点是(x_line_end, radius),终点是(x_line_end+radius,0)。
用圆心在(x_line_end,0)的标准方程,起点(theta=-pi/2):
x = x_line_end + radius * cos(-pi/2) = x_line_end + 0 = x_line_end
y = 0 + radius * sin(-pi/2) = -radius
这不对,因为直线结束点是(x_line_end, radius),而这里起点是(x_line_end, -radius)。
所以原MATLAB代码中第二个半圆的参数方程是:
x = radius * cos(-theta) + x_line_end = radius * cos(theta) + x_line_end
y = radius * sin(-theta) - radius + radius = -radius * sin(theta)
当theta=-pi/2时:
x = radius * cos(-pi/2) + x_line_end = 0 + x_line_end
y = -radius * sin(-pi/2) = -radius * (-1) = radius
当theta=0时:
x = radius * cos(0) + x_line_end = radius + x_line_end
y = -radius * sin(0) = 0
所以圆心在哪里?起点(x_line_end,radius)和终点(x_line_end+radius,0)到圆心的距离相等,设圆心为(a,b):
(x_line_end - a)^2 + (radius - b)^2 = (x_line_end+radius - a)^2 + (0 - b)^2
=> 解出:a = x_line_end, b = 0? 不对,因为:
左边:(x_line_end - a)^2 + (radius - b)^2
右边:(x_line_end+radius - a)^2 + b^2
展开:左边 = x_line_end^2 - 2*a*x_line_end + a^2 + radius^2 - 2*b*radius + b^2
右边 = x_line_end^2 + 2*x_line_end*radius + radius^2 - 2*a*(x_line_end+radius) + a^2 + b^2
相减:左边-右边 = -2*a*x_line_end - 2*b*radius - 2*x_line_end*radius - radius^2 + 2*a*(x_line_end+radius)
= -2*b*radius + 2*a*radius - 2*x_line_end*radius - radius^2
令其等于0:-2*b*radius + 2*a*radius - 2*x_line_end*radius - radius^2 = 0
化简:2*a*radius - 2*b*radius = 2*x_line_end*radius + radius^2
除以radius:2a - 2b = 2*x_line_end + radius
所以圆心可以在直线2a-2b=2*x_line_end+radius上任意位置?这不对。
实际上,我们计算圆心到起点和终点的距离:
起点:(a-x_line_end)^2 + (b-radius)^2 = radius^2
终点:(a - (x_line_end+radius))^2 + (b-0)^2 = radius^2
两式相减:
(a-x_line_end)^2 - (a-x_line_end-radius)^2 + (b-radius)^2 - b^2 = 0
展开:
[a^2 - 2*a*x_line_end + x_line_end^2] - [a^2 - 2*a*(x_line_end+radius) + (x_line_end+radius)^2] + [b^2-2*b*radius+radius^2] - b^2 =0
=> -2*a*x_line_end + x_line_end^2 + 2*a*(x_line_end+radius) - (x_line_end^2+2*x_line_end*radius+radius^2) - 2*b*radius + radius^2 =0
=> (-2*a*x_line_end + 2*a*x_line_end + 2*a*radius) + (x_line_end^2 - x_line_end^2 - 2*x_line_end*radius - radius^2) - 2*b*radius + radius^2 =0
=> 2*a*radius - 2*x_line_end*radius - radius^2 - 2*b*radius + radius^2 =0
=> 2*a*radius - 2*x_line_end*radius - 2*b*radius =0
=> a - x_line_end - b =0
=> a = x_line_end + b
代入第一个方程:
(b)^2 + (b-radius)^2 = radius^2
=> b^2 + b^2 - 2*b*radius + radius^2 = radius^2
=> 2*b^2 - 2*b*radius =0
=> 2b(b - radius)=0
=> b=0 或 b=radius
如果b=0,则a=x_line_end,那么起点到圆心的距离:sqrt((x_line_end - x_line_end)^2+(radius-0)^2)=radius,符合。
如果b=radius,则a=x_line_end+radius,那么起点到圆心的距离:sqrt((x_line_end+radius - x_line_end)^2 + (radius-radius)^2)=radius,也符合。
终点情况:
b=0时:终点到圆心(x_line_end,0)的距离:sqrt(radius^2+0)=radius,符合。
b=radius时:终点到圆心(x_line_end+radius,radius)的距离:sqrt(0+radius^2)=radius,符合。
但是,这两个圆心分别对应什么?
b=0:圆心在(x_line_end,0),那么起点在圆心的正上方,终点在圆心的正右方,这是一个逆时针90度的圆弧(从90度到0度)?但我们的参数方程中y=-radius*sin(theta),当theta从-pi/2到0,y从radius到0,所以是顺时针?不对,这个圆的圆弧从起点(圆心的正上方)到终点(圆心的正右方)是逆时针90度。
然而,我们的参数方程:
x = x_line_end + radius * cos(theta) [theta从-pi/2到0]
y = -radius * sin(theta)
当theta=-pi/2:x=x_line_end+0, y=-radius*(-1)=radius -> 起点
当theta=0:x=x_line_end+radius, y=0 -> 终点
从起点到终点,随着theta从-pi/2增加到0,x增加,y减小,所以是顺时针?不对,在圆(x_line_end,0)上,从(0,radius)(即起点)到(radius,0)(即终点)的顺时针方向是经过第四象限的,但这里y= -radius*sin(theta)在theta从-pi/2到0时,sin(theta)从-1到0,所以y从正到0,而x从0到正,所以是顺时针吗?实际上,在圆(x_line_end,0)上,从上方点(0,radius)到右方点(radius,0)的顺时针路径会经过(0,-radius)吗?不会,顺时针会经过(0,-radius)吗?不会,因为顺时针方向从上方点开始,会先经过左方?不对,圆心在(x_line_end,0),起点在(x_line_end,radius)即正上方,顺时针旋转90度会到达正右方?不对,顺时针旋转90度应该到达正右方(从上方顺时针旋转90度到右方,中间经过的点x增加,y减小,是顺时针吗?在标准坐标系中,顺时针旋转90度,从(0,radius)到(radius,0)?不对,从(0,radius)顺时针旋转90度到达(0,-radius)?不对。
实际上,在标准坐标系中,顺时针旋转:
起点(0,radius)(圆心在(0,0))顺时针旋转90度到达(radius,0)?不对,圆心在(0,0)时,点(0,radius)顺时针旋转90度后到达(radius,0)?这是不可能的,因为旋转后x和y坐标交换且符号改变?不对,旋转公式:x' = x*cos(theta)+y*sin(theta), y'=-x*sin(theta)+y*cos(theta),顺时针旋转90度:theta=-90度,即:
x' = 0*0 + radius*(-1) = -radius
y' = -0*(-1) + radius*0 = 0 -> 点(-radius,0)
所以圆心在(0,0)时,顺时针旋转90度后到达(-radius,0)。
而我们的圆心在(x_line_end,0),起点(x_line_end,radius)顺时针旋转90度后,应该到达(x_line_end-radius,0)?但我们需要到达(x_line_end+radius,0)。
因此,我们不是用顺时针,而是用逆时针旋转?或者圆心不同。
重新审视:我们的参数方程是:
x = x_line_end + radius * cos(theta) [theta从-pi/2到0]
y = -radius * sin(theta)
当theta=-pi/2: cos(-pi/2)=0, sin(-pi/2)=-1 -> (x_line_end, -radius*(-1)) = (x_line_end, radius)
当theta=0: (x_line_end+radius, 0)
这个轨迹是一个圆心在(x_line_end,0)的上半圆?不对,因为y坐标是正的,而圆心在(x_line_end,0),那么点(x_line_end,radius)在圆心的正上方,点(x_line_end+radius,0)在圆心的正右方,从正上方到正右方的圆弧,在圆的上半圆(第一象限)应该是逆时针旋转90度。但我们的参数方程中theta从-pi/2到0,而cos(theta)从0到1,sin(theta)从-1到0,所以y=-radius*sin(theta)从radius到0。这个参数方程描述的是圆的上半圆的逆时针旋转?实际上,在标准参数方程中,圆心为(cx,cy)的圆:
x = cx + radius * cos(alpha)
y = cy + radius * sin(alpha)
当alpha从-pi/2到0,x从cx到cx+radius(增加),y从cy-radius到cy(增加)?不对,sin(alpha)从-1到0,所以y=cy+radius*sin(alpha)从cy-radius到cy。所以这是下半圆(第四象限)的逆时针旋转(从270度到360度)?但我们的点却在第一象限(y>0)?矛盾。
实际上,我们的参数方程中,y = -radius*sin(theta),相当于:
y = cy + radius * sin(alpha) 其中cy=0,但这里我们用了负号,所以相当于把sin(alpha)取反,那么就是关于x轴对称。所以原来的下半圆变成上半圆?不对,原来的下半圆(y负)取反后变成上半圆(y正)。所以这个参数方程描述的是:圆心在(x_line_end,0),半径radius,从角度270度(即-pi/2)到0度(即0)的圆弧,但是将y坐标取反,所以实际绘制的是上半圆中从270度(即正上方)到0度(正右方)的圆弧?但270度到0度是顺时针90度,而取反后相当于下半圆?不对。
我们直接画点:theta从-pi/2到0,步进取10个点,观察:
theta=-pi/2: (x_line_end, radius)
theta=-pi/3: x = x_line_end + radius*cos(-pi/3)=x_line_end+radius*(0.5), y=-radius*sin(-pi/3)=radius*(√3/2) ≈ 0.866*radius
theta=-pi/6: x = x_line_end+radius*cos(-pi/6)=x_line_end+radius*(√3/2), y=-radius*sin(-pi/6)=radius*(0.5)
theta=0: (x_line_end+radius,0)
这些点连起来,是一个在第一象限的四分之一圆(圆心在(x_line_end,0)),从正上方开始,到正右方结束。所以是上半圆的逆时针旋转?实际上,从(0,radius)到(radius,0)的圆弧,圆心在(0,0)时,是在第一象限的1/4圆(逆时针90度)。但这里圆心在(x_line_end,0),所以点(x_line_end,radius)在圆心的正上方,点(x_line_end+radius,0)在圆心的正右方,圆弧连接这两点,且所有点的y>=0,所以是上半圆的一部分(从90度到0度,逆时针90度)。
因此,这个参数方程描述的是圆心在(x_line_end,0),从角度90度(pi/2)到0度的圆弧(逆时针旋转90度)。但是参数theta是从-pi/2到0,为什么对应90度到0度?
在标准参数方程中,角度alpha=90度对应sin(alpha)=1,但这里theta=-pi/2时,sin(theta)=-1,而我们y=-radius*sin(theta)=radius,所以实际上我们通过取负号,将角度的正弦值取反,相当于将角度映射到上半平面。所以,这个参数方程可以理解为:使用一个从-90度到0度的参数,但通过取负号将y坐标变为正,从而得到上半圆的对应部分。
所以,第二个半圆是圆心在(x_line_end,0),半径为radius,从正上方到正右方的逆时针90度圆弧。
总结:轨迹由三部分组成:
1. 第一个半圆:圆心(radius,0),半径radius,从(0,0)到(radius,radius)(逆时针90度)?
实际上,参数方程:x=radius*(1-cos(theta)), y=radius*sin(theta) (theta:0->pi/2)
起点(0,0),终点(radius,radius),圆心(radius,0) -> 从圆心看,起点角度180度,终点角度90度,所以是顺时针90度?但是我们在坐标系中画出来,从(0,0)到(radius,radius)的圆弧,是逆时针还是顺时针?从180度到90度,是逆时针旋转90度(因为角度增加是逆时针,但这里角度从180减小到90,所以是顺时针旋转90度)。所以第一个半圆是顺时针90度圆弧。
2. 直线:从(radius,radius)到(radius+line_length,radius)
3. 第二个半圆:圆心(radius+line_length,0),半径radius,从(radius+line_length,radius)到(radius+line_length+radius,0)(逆时针90度圆弧)。
因此,整个轨迹是一个“S”形?第一个半圆是顺时针90度(从(0,0)到(radius,radius)),第二个半圆是逆时针90度(从(radius+line_length,radius)到(radius+line_length+radius,0)),中间用直线连接。
现在,我们用C语言实现。
注意:我们生成的点数要精确等于total_points。
步骤:
1. 计算两个半圆和一个直线的点数:
n_circle = round(total_points * circle_arc / total_len);
n_line = total_points - 2 * n_circle;
2. 分配total_points个Point。
3. 第一个半圆:
for (int i=0; i<n_circle; i++) {
double theta = (double)i / (n_circle-1) * (M_PI/2); // 0 到 pi/2
points[i].x = radius * (1 - cos(theta));
points[i].y = radius * sin(theta);
}
4. 直线段:
double start_x = points[n_circle-1].x;
double start_y = points[n_circle-1].y;
for (int i=0; i<n_line; i++) {
points[n_circle + i].x = start_x + line_length * (double)i / (n_line-1);
points[n_circle + i].y = start_y; // 保持y=radius
}
5. 第二个半圆:
double line_end_x = points[n_circle+n_line-1].x;
double line_end_y = points[n_circle+n_line-1].y;
for (int i=0; i<n_circle; i++) {
double theta = (double)i / (n_circle-1) * (M_PI/2) - M_PI/2; // 从-pi/2到0
points[n_circle+n_line+i].x = line_end_x + radius * cos(theta);
points[n_circle+n_line+i].y = -radius * sin(theta); // 注意这里负号
}
但是,注意MATLAB代码中第二个半圆的y计算为:y_circle1 = radius * sin(-theta) - radius + y_line_end
= -radius * sin(theta) - radius + radius [因为y_line_end=radius]
= -radius * sin(theta)
所以和我们上面的y值计算一致。
因此,我们这样实现。
注意:当n_circle=0或1时,需要特殊处理,避免除以0。但题目中total_points=3000,所以n_circle不会为0。
另外,由于浮点数运算,最后一个点可能会有精度问题,我们确保最后一个点的位置正确。
我们写一个函数,返回一个Point数组,以及点数(就是total_points)。
注意:内存由调用者释放。
代码如下:</think># 轨迹生成函数C语言实现
以下是将MATLAB轨迹生成函数完整转换为C语言的实现,包含两种轨迹生成方式:半圆-直线-半圆组合轨迹和螺旋线轨迹。
```c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define PI 3.141592653589793
typedef struct {
double x;
double y;
} Point;
// 生成半圆-直线-半圆组合轨迹
Point* generate_arc_line_trajectory(int total_points, int* actual_points) {
double radius = 2.0;
double line_length = 10.0;
double circle_arc = PI/2 * radius;
double total_len = circle_arc + line_length + circle_arc;
// 计算每段点数
int n_circle = (int)round(total_points * circle_arc / total_len);
int n_line = total_points - 2 * n_circle;
// 确保总点数正确
*actual_points = 2 * n_circle + n_line;
Point* points = (Point*)malloc(*actual_points * sizeof(Point));
// 第一段半圆 (圆心在(radius, 0))
for (int i = 0; i < n_circle; i++) {
double theta = (double)i / (n_circle - 1) * (PI/2); // 0 到 π/2
points[i].x = radius * (1 - cos(theta));
points[i].y = radius * sin(theta);
}
// 直线段
double start_x = points[n_circle - 1].x;
double start_y = points[n_circle - 1].y;
for (int i = 0; i < n_line; i++) {
double t = (double)i / (n_line - 1);
points[n_circle + i].x = start_x + t * line_length;
points[n_circle + i].y = start_y;
}
// 第二段半圆 (圆心在(直线结束点x, 直线结束点y - radius))
double line_end_x = points[n_circle + n_line - 1].x;
double line_end_y = points[n_circle + n_line - 1].y;
for (int i = 0; i < n_circle; i++) {
double theta = (double)i / (n_circle - 1) * (PI/2) - PI/2; // -π/2 到 0
points[n_circle + n_line + i].x = line_end_x + radius * cos(-theta);
points[n_circle + n_line + i].y = line_end_y - radius + radius * sin(-theta);
}
return points;
}
// 生成螺旋线轨迹
Point* generate_spiral_trajectory(int total_points) {
double a = 0.5; // 螺旋线系数
int n_turns = 1; // 螺旋线圈数
double theta_max = 2 * PI * n_turns;
int density = 5000; // 高密度采样点数
// 高密度采样点
double* theta_dense = (double*)malloc(density * sizeof(double));
double* x_dense = (double*)malloc(density * sizeof(double));
double* y_dense = (double*)malloc(density * sizeof(double));
for (int i = 0; i < density; i++) {
theta_dense[i] = (double)i / (density - 1) * theta_max;
x_dense[i] = a * theta_dense[i] * cos(theta_dense[i]);
y_dense[i] = a * theta_dense[i] * sin(theta_dense[i]);
}
// 计算累积弧长
double* s_dense = (double*)malloc(density * sizeof(double));
s_dense[0] = 0.0;
for (int i = 1; i < density; i++) {
double dx = x_dense[i] - x_dense[i-1];
double dy = y_dense[i] - y_dense[i-1];
s_dense[i] = s_dense[i-1] + sqrt(dx*dx + dy*dy);
}
double s_total = s_dense[density-1];
// 等弧长间隔采样
Point* points = (Point*)malloc(total_points * sizeof(Point));
for (int i = 0; i < total_points; i++) {
double s = (double)i / (total_points - 1) * s_total;
// 查找s所在的区间
int left = 0, right = density - 1;
while (right - left > 1) {
int mid = (left + right) / 2;
if (s_dense[mid] < s) {
left = mid;
} else {
right = mid;
}
}
// 线性插值
double t = (s - s_dense[left]) / (s_dense[right] - s_dense[left]);
points[i].x = x_dense[left] + t * (x_dense[right] - x_dense[left]);
points[i].y = y_dense[left] + t * (y_dense[right] - y_dense[left]);
}
// 释放内存
free(theta_dense);
free(x_dense);
free(y_dense);
free(s_dense);
return points;
}
// 生成轨迹 (可选择轨迹类型)
Point* generate_trajectory(int total_points, int trajectory_type, int* actual_points) {
if (trajectory_type == 0) {
return generate_arc_line_trajectory(total_points, actual_points);
} else {
*actual_points = total_points;
return generate_spiral_trajectory(total_points);
}
}
// 保存轨迹到CSV文件
void save_trajectory_to_csv(const char* filename, Point* points, int num_points) {
FILE* fp = fopen(filename, "w");
if (fp == NULL) {
printf("无法打开文件 %s\n", filename);
return;
}
fprintf(fp, "x,y\n");
for (int i = 0; i < num_points; i++) {
fprintf(fp, "%f,%f\n", points[i].x, points[i].y);
}
fclose(fp);
printf("轨迹已保存到 %s\n", filename);
}
// 主函数
int main() {
int total_points = 3000;
int actual_points = 0;
// 生成半圆-直线-半圆轨迹
Point* arc_line_points = generate_trajectory(total_points, 0, &actual_points);
save_trajectory_to_csv("arc_line_trajectory.csv", arc_line_points, actual_points);
free(arc_line_points);
// 生成螺旋线轨迹
Point* spiral_points = generate_trajectory(total_points, 1, &actual_points);
save_trajectory_to_csv("spiral_trajectory.csv", spiral_points, actual_points);
free(spiral_points);
return 0;
}
```
## 代码说明
### 1. 半圆-直线-半圆轨迹实现
```c
Point* generate_arc_line_trajectory(int total_points, int* actual_points) {
// 参数设置
double radius = 2.0;
double line_length = 10.0;
double circle_arc = PI/2 * radius;
double total_len = circle_arc + line_length + circle_arc;
// 计算各段点数
int n_circle = (int)round(total_points * circle_arc / total_len);
int n_line = total_points - 2 * n_circle;
*actual_points = 2 * n_circle + n_line;
// 分配内存
Point* points = (Point*)malloc(*actual_points * sizeof(Point));
// 第一段半圆 (从(0,0)到(2,2))
for (int i = 0; i < n_circle; i++) {
double theta = (double)i / (n_circle - 1) * (PI/2);
points[i].x = radius * (1 - cos(theta));
points[i].y = radius * sin(theta);
}
// 直线段 (水平向右)
double start_x = points[n_circle - 1].x;
double start_y = points[n_circle - 1].y;
for (int i = 0; i < n_line; i++) {
double t = (double)i / (n_line - 1);
points[n_circle + i].x = start_x + t * line_length;
points[n_circle + i].y = start_y;
}
// 第二段半圆 (从(12,2)到(14,0))
double line_end_x = points[n_circle + n_line - 1].x;
double line_end_y = points[n_circle + n_line - 1].y;
for (int i = 0; i < n_circle; i++) {
double theta = (double)i / (n_circle - 1) * (PI/2) - PI/2;
points[n_circle + n_line + i].x = line_end_x + radius * cos(-theta);
points[n_circle + n_line + i].y = line_end_y - radius + radius * sin(-theta);
}
return points;
}
```
### 2. 螺旋线轨迹实现
```c
Point* generate_spiral_trajectory(int total_points) {
// 参数设置
double a = 0.5;
int n_turns = 1;
double theta_max = 2 * PI * n_turns;
int density = 5000;
// 高密度采样
double* theta_dense = (double*)malloc(density * sizeof(double));
double* x_dense = (double*)malloc(density * sizeof(double));
double* y_dense = (double*)malloc(density * sizeof(double));
for (int i = 0; i < density; i++) {
theta_dense[i] = (double)i / (density - 1) * theta_max;
x_dense[i] = a * theta_dense[i] * cos(theta_dense[i]);
y_dense[i] = a * theta_dense[i] * sin(theta_dense[i]);
}
// 计算累积弧长
double* s_dense = (double*)malloc(density * sizeof(double));
s_dense[0] = 0.0;
for (int i = 1; i < density; i++) {
double dx = x_dense[i] - x_dense[i-1];
double dy = y_dense[i] - y_dense[i-1];
s_dense[i] = s_dense[i-1] + sqrt(dx*dx + dy*dy);
}
// 等弧长采样
Point* points = (Point*)malloc(total_points * sizeof(double));
double s_total = s_dense[density-1];
for (int i = 0; i < total_points; i++) {
double s = (double)i / (total_points - 1) * s_total;
// 二分查找s所在区间
int left = 0, right = density - 1;
while (right - left > 1) {
int mid = (left + right) / 2;
if (s_dense[mid] < s) {
left = mid;
} else {
right = mid;
}
}
// 线性插值
double t = (s - s_dense[left]) / (s_dense[right] - s_dense[left]);
points[i].x = x_dense[left] + t * (x_dense[right] - x_dense[left]);
points[i].y = y_dense[left] + t * (y_dense[right] - y_dense[left]);
}
// 释放内存
free(theta_dense);
free(x_dense);
free(y_dense);
free(s_dense);
return points;
}
```
### 3. 轨迹保存函数
```c
void save_trajectory_to_csv(const char* filename, Point* points, int num_points) {
FILE* fp = fopen(filename, "w");
if (fp == NULL) {
printf("无法打开文件 %s\n", filename);
return;
}
fprintf(fp, "x,y\n");
for (int i = 0; i < num_points; i++) {
fprintf(fp, "%f,%f\n", points[i].x, points[i].y);
}
fclose(fp);
printf("轨迹已保存到 %s\n", filename);
}
```
## 关键算法说明
### 1. 半圆-直线-半圆轨迹
- **参数计算**:基于弧长比例计算各段点数
- **第一段半圆**:圆心在(2,0),从(0,0)到(2,2)的90度圆弧
- **直线段**:从(2,2)到(12,2)的水平直线
- **第二段半圆**:圆心在(12,0),从(12,2)到(14,0)的90度圆弧
### 2. 螺旋线轨迹
- **高密度采样**:首先生成5000个密集点
- **弧长计算**:累积计算各点间的欧氏距离
- **等弧长采样**:使用二分查找定位弧长位置,线性插值获取点坐标
- **参数方程**:阿基米德螺旋线 `x = aθcos(θ)`, `y = aθsin(θ)`
## 编译与运行
1. 编译程序:
```bash
gcc -o trajectory_generator trajectory_generator.c -lm
```
2. 运行程序:
```bash
./trajectory_generator
```
3. 输出结果:
```
轨迹已保存到 arc_line_trajectory.csv
轨迹已保存到 spiral_trajectory.csv
```
## 应用场景
1. **机器人路径规划**:生成平滑的移动轨迹
2. **计算机辅助设计**:创建复杂的几何形状
3. **运动控制系统**:测试控制算法的轨迹跟踪性能
4. **计算机图形学**:生成自然曲线用于渲染
5. **数据分析**:创建测试数据集用于算法验证
阅读全文
相关推荐

















