碰撞问题:将圆弧的旋转中心放在一个半圆上,旋转直到碰到放置的半圆

Collision problem: placing the centre of rotation of arc on a semi-circle and rotating it till it touches the semi-circle on which it is placed

我以前写过一段代码,允许圆弧绕其给定的中心 (14,0) 旋转,但圆弧在与另一条曲线接触时停止旋转。

当我围绕 (14,0) 而不是 (8.5,0) 旋转圆弧时,此代码有效

我通过使用 linspace() 改变 theta,将圆弧的旋转放在 while 循环中。我使用 polyxpoly() 来查找交集。 while 循环的条件是,只要我有空数组,循环就会继续,但只要我从 polyxpoly() 获得一个值,我的循环就会停止。

绕(14,0)旋转时的代码

clc,clear
R = 5;                     % radius of a circle
r = 10;                     % radius of arc
aa = 60*pi/180;              % arc angle
ap = 0*pi/180;             % arc position angle

% defining the semi-circle about the origin

t = linspace(0,pi);
[x,y] = pol2cart(t,R);      % circle data

% Shifting circle centre to (3.5,0)
x=x+3.5;
y=y+0;

% defining the arc about the origin
t1 = linspace(0,aa)-aa/2+ap;
[x1,y1] = pol2cart(t1,r); % arc data

% shifting arc-lower-end to (14,0)
delx=14-x1(1); % Finding the x difference between arc-lower-end x-coordinate & 14
dely=0-y1(1); % Finding the y difference between arc-lower-end y-coordinate & 0

x1=x1+delx;
y1=y1+dely;

theta =linspace(0,pi,500);
i=1;
xc=[];
yc=[];

while isempty(xc)&& isempty(yc)

% create a matrix of these points, which will be useful in future calculations

v = [x1;y1];

% choose a point which will be the center of rotation

x_center = 14;

y_center = 0;

% create a matrix which will be used later in calculations

center = repmat([x_center; y_center], 1, length(x1));

% define a 60 degree counter-clockwise rotation matrix



R = [cos(theta(i)) -sin(theta(i)); sin(theta(i)) cos(theta(i))];

% do the rotation...

s = v - center;     % shift points in the plane so that the center of rotation is at the origin

so = R*s;           % apply the rotation about the origin

vo = so + center;   % shift again so the origin goes back to the desired center of rotation

% this can be done in one line as:

% vo = R*(v - center) + center

% pick out the vectors of rotated x- and y-data

x_rotated = vo(1,:);

y_rotated = vo(2,:);


[xc,yc] = polyxpoly(x_rotated,y_rotated,x,y)
[xc1,yc1] = polyxpoly(x1,y1,x,y)

i=i+1;
end


% make a plot
plot(x,y)
hold on 

plot(x1, y1, 'k-', x_rotated, y_rotated, 'r-', x_center, y_center, 'bo');

axis equal

以上代码的输出:

当我选择 (8.5,0) 作为圆弧旋转的中心时,polyxpoly() 在第一个实例中直接给我一个值,防止圆弧旋转。

以前,只要执行 while 循环,arc 就会一直旋转。当弧与半圆接触时,while 循环停止执行。但是当我将 (8.5,0) 作为半圆的旋转中心(我的要求)时,while 循环不会被执行,因为 polyxpoly() 会给出一个值,因为 (8.5,0) 位于半圆上,所以默认情况下是 arc从开始接触半圆。 有什么办法可以让 polyxpoly 忽略初始交点 (8.5,0),这样我就可以使圆弧围绕 (8.5,0)

旋转

polyxpoly 对于形状笨拙的多边形很有用,因为数学已经为您完成,但对于与圆的碰撞,它有点矫枉过正。数学很容易找到解析解。

以原点[0,0]为中心的圆周长上的每个点满足方程:x^2+y^2=R^2

由此,您可以推导出满足方程 x^2+y^2<=R^2 的每个点要么在圆内,要么就在圆的外围。这成为一个简单的碰撞测试条件。我们只需要在每次迭代时检查圆弧最后一点的坐标,看看它是否满足条件。

在代码中应用,对于最初的问题(围绕 [14,0] 旋转),它和 polyxpoly 一样有效(我怀疑它更快)。

%%
clc,clear
Rc  = 5;            % radius of a circle
Ra  = 10;           % radius of arc
aa = 60*pi/180;     % arc angle
ap =  0*pi/180;     % arc position angle
cc = [3.5 0] ;      % Circle center coordinates
ao = [14 0 ] ;      % Arc "origin"
% ao = [8.5 0 ] ;      % Arc "origin"

% defining the semi-circle about the origin
[xc,yc] = pol2cart( linspace(0,pi) , Rc );  % circle data
% Shifting circle centre to (3.5,0)
xc = xc + cc(1) ;
yc = yc + cc(2) ;

% defining the arc about the origin
[xa,ya] = pol2cart( linspace(0,aa)-aa/2+ap , Ra ) ; % arc data

% shifting arc-lower-end to (14,0) (Arc origin)
xa = xa - xa(1) + ao(1) ;
ya = ya - ya(1) + ao(2) ;

%% make a copy or the arc coordinates (to animate/rotate later)
xar = xa ;
yar = ya ;

%% make a plot
h.fig    = figure;
hold on
% Static elements
h.circle    = plot(xc,yc) ;
h.arcstart  = plot(xa,ya,'k--') ;
h.arcorig   = plot(ao(1),ao(2),'bo') ;
% dynamic element (the moving arc)
h.arcmov    = plot(xar,yar,'g-') ;

axis equal

%% Setup loop parameter
nStep = 500 ;
iStep = 1 ;
theta = linspace(0,pi,nStep);
collision_detected = false ;

%%
while iStep<=nStep && ~collision_detected

    %% counter-clockwise rotation matrix
    Rot = [ cos(theta(iStep)) -sin(theta(iStep)) ;
            sin(theta(iStep)) cos(theta(iStep))  ] ;

    % create a matrix of the arc points, already translated to the origin
    v = [xa-ao(1) ; ya-ao(2)];
    % apply rotation
    v = Rot * v ;
    % apply translation to put the arc back in place
    xar = v(1,:) + ao(1) ;
    yar = v(2,:) + ao(2) ;

    % Determine if the last point of the arc has touched or entered the circle
    % get the coordinate of the last point of the arc, translated so the circle
    % would appear to be centered on the origin
    xp = xar(end) - cc(1) ;
    yp = yar(end) - cc(2) ;

    if (xp^2+yp^2) <= Rc^2
        collision_detected = true ;
    end

    % update display
    set( h.arcmov , 'XData', xar , 'YData', yar ) ;
    if collision_detected
        set( h.arcmov , 'Color','r' )
        h.cp = plot( xar(end) , yar(end) , 'x' , 'LineWidth',2, 'MarkerSize',20 ) ;
    end
    drawnow

    iStep = iStep+1 ;

end

图解结果:


现在这适用于您定义的旋转原点的所有位置,除了您感兴趣的位置:[8.5 0]。如果你 运行 使用这些旋转原点坐标编写上面的脚本,你会看到弧只是旋转但从未停止。

这是因为在该配置中,圆弧端只会以非常特定的旋转角度 theta=pi/2 精确地擦过圆。当圆弧旋转 pi/2 时,它的终点就在圆本身上。点永远不会 进入 圆圈内。在pi/2之前或之后的任何旋转角度,该点都在圆之外。

由于我们定义的方式 theta = linspace(0,pi,nStep);,脚本无法检测到这个奇异的碰撞点。在我们要尝试的所有 theta 个值中,none 个正好是 pi/2。所以我们从不给脚本检测碰撞的机会。

有两种方法可以解决这个问题:

第一种方法很简单,但仅针对此问题非常具体,可能无法很好地概括。我们只需要在区间 [0 pi] 上重新定义 theta,但是有 奇数 个值,这样我们就可以确定向量的中值 theta 将正好是 pi/2.

因此将 theta 定义替换为:theta = linspace(0,pi,nStep+1); 足以使脚本解决这个特定问题:

虽然这是一种解决问题的 hacky 方法,因为它需要提前知道确切的解决方案,以确保脚本不会错过它。克服这类步长问题的更通用的方法是在条件中包含 tolerance。 因此,我们不会使用 theta 定义,而是在代码之上定义一个我们可以接受的 Tolerance 级别,例如:

Tolerance = 0.001 ;

我们会将碰撞检测条件更改为:

if (xp^2+yp^2) <= (Rc^2+Tolerance)
    collision_detected = true ;
end

对于您的示例,对于 nSteps=500,即使不重新定义 theta,这也足以检测碰撞(实际上 0.0005 的公差也可以)。但是请注意,如果您的基本步长定义得较大(例如 nSteps=100),则必须调整公差。

总而言之,我建议保留通用代码的两种实现。 Theta 的定义超过了 odd 个值, 引入了公差。