MATLAB模拟退火算法优化包围盒(OBB)问题

    科技2025-01-16  23

    MATLAB模拟退火算法优化包围盒(OBB)问题

    一、实现效果

    注意:本文只涉及OBB坐标计算部分,图形显示由OpenGL处理,本文不作介绍

    二、算法设计

    众所周知,模拟退火算法的核心在于如何选择与更新解空间以及设计成本函数。确定了这几个部分,接下来只需要套用算法框架即可。

    1、待优化参数(解空间)的确定

    对于OBB问题,有两种参数选择方案: 一是,直接以包围盒三条邻边顶点及原点坐标为参数: 二是,在确定单位立方体的基础上将原点坐标、xyz三轴缩放、xyz三轴旋转作为参数 第一种 优点在于能直接得出最终结果(绝对坐标)不需要进行换算,但是问题在于每次随机更新坐标,都会使得ox、oy、oz向量不再相互垂直,需要添加额外的约束条件,浪费计算力。 第二种 的优点很明显,默认三轴垂直,不再需要考虑该约束条件,但是要求出绝对坐标还需要额外换算。 本文选择第二种方案作为解空间。 下面给出各参数的初始化代码: init.m

    clear all; %% 模拟退火基本参数初始化 T=80;%初温 Tend=0.0001;%末温 searchCount=100;%单温内搜索次数 q=0.98; %% 解空间初始化 O=[rand*2-1,rand*2-1,rand*2-1];%原点 % 三轴缩放 Xzoom=abs(randn(1,1)); Yzoom=abs(randn(1,1)); Zzoom=abs(randn(1,1)); % 三轴旋转 Xroll=randi(360); Yroll=randi(360); Zroll=randi(360); %% 记录器初始化 log_count=1; costLog=[]; OLog=[0,0,0]; xLog=[0,0,0]; yLog=[0,0,0]; zLog=[0,0,0];

    2、更新解空间

    模拟退火要获得较精确的解需要在高温时更新解的范围大,来跳出局部最优解;在低温时更新解的范围小,以找到精确的解。 由此可知,更新解的函数需要与温度有关,代码如下: getNew.m

    % z=[Xzoom,Yzoom,Zzoom] % r=[Xroll,Yroll,Zroll] function [o,zoom,roll] = getNew(O,z,r,T) % 获取新解 o=[O(1)+(rand*2-1)*T,O(2)+(rand*2-1)*T,O(3)+(rand*2-1)*T]; zoom=[abs(z(1)+randn(1,1)*T),abs(z(2)+randn(1,1)*T),abs(z(3)+randn(1,1)*T)]; roll=[r(1)+(rand*2-1)*T,r(2)+(rand*2-1)*T,r(3)+(rand*2-1)*T]; end

    3、成本函数

    OBB问题要求包围盒尽可能小,尽可能贴近模型边界,于是选择用体积来表示成本。此外,还有一个重要的约束条件:模型的每个点都不能在包围盒的范围外。 其中f(x)为约束条件函数,当x满足约束条件,则无附加成本;当x不满足条件,则成本无穷大。 设模型中的任意点为P,x为OP在OX轴上的投影与|OX|的比值。若x在(0,1)之间,则表示P点在包围盒内。 其中O、X为包围盒原点和X轴顶点的绝对坐标。 但是我们计算的参数均为变换参数,需要将其转化为绝对坐标才能进行运算,下面是转换代码: getVertex.m

    function [O,vx,vy,vz] = getVertex(o,z,r) %变换->点 vx0=[o(1)+z(1),o(2),o(3)]; vy0=[o(1),o(2)+z(2),o(3)]; vz0=[o(1),o(2),o(3)+z(3)]; rx=[1,0,0 ; 0,cosd(r(1)),-sind(r(1)) ; 0,sind(r(1)),cosd(r(1))];%绕x轴旋转矩阵 ry=[cosd(r(2)),0,sind(r(2)) ; 0,1,0 ; -sind(r(2)),0,cosd(r(2))];%绕y轴旋转矩阵 rz=[cosd(r(3)),-sind(r(3)),0 ; sind(r(3)),cosd(r(3)),0 ; 0,0,1];%绕z轴旋转矩阵 O=o*rx*ry*rz; vx=vx0*rx*ry*rz; vy=vy0*rx*ry*rz; vz=vz0*rx*ry*rz; end

    在获取了顶点坐标以后就可以计算通过上面的公式计算成本函数了,代码如下:(注意:模型点坐标保存在points矩阵中,这里不做展示) goal.m

    function [cost] = goal(o,x,y,z,points) [row,~]=size(points); cons=0; for i= 1:row p=points(i,:);%提取第i行数据 c1=isInLine(o,x,p); c2=isInLine(o,y,p); c3=isInLine(o,z,p); if c1 ~= 0 || c2 ~= 0 || c3 ~= 0 % 判断是否在OBB内部 cons=1e10; break end end ox=norm(x-o); %取模 oy=norm(y-o); oz=norm(z-o); cost=ox*oy*oz+cons;

    isInLine.m

    %% 判断点是否在OBB内,在则返回0,不在则返回1 function [value] = isInLine(o,n,p) o_p=p-o; o_n=n-o; o_n1=o_n/norm(n-o); x=(dot(o_p,o_n1))/norm(n-o); if x > 0 && x <1 value=0; else value=1; end end

    三、模拟退火代码

    main.m

    run init; [o0,zoom0,roll0]=getNew(O,[Xzoom,Yzoom,Zzoom],[Xroll,Yroll,Zroll],T); [O0,x0,y0,z0]=getVertex(o0,zoom0,roll0); cost0=goal(O0,x0,y0,z0,points); cost_final=cost0; while T>Tend for i=1:searchCount [oNew,zoomNew,rollNew] = getNew(o0,zoom0,roll0,T); [ONew,xNew,yNew,zNew]=getVertex(oNew,zoomNew,rollNew); cost=goal(ONew,xNew,yNew,zNew,points); if cost < cost0 % 如果新解成本值小于当前解的成本 o0 = oNew; % 更新当前解为新解 zoom0=zoomNew; roll0=rollNew; cost0 = cost; else p = exp(-(cost - cost0)/T); % 根据Metropolis准则计算一个概率 if rand(1) < p o0 = oNew; % 更新当前解为新解 zoom0=zoomNew; roll0=rollNew; cost0 = cost; end end if cost<cost_final O_final=ONew; x_final=xNew; y_final=yNew; z_final=zNew; cost_final=cost; % 记录解 costLog(log_count)=cost; OLog=cat(1,OLog,O_final); xLog=cat(1,xLog,x_final); yLog=cat(1,yLog,y_final); zLog=cat(1,zLog,z_final); log_count=log_count+1; end end T=T*q; end
    Processed: 0.011, SQL: 8