Повышение точности решения трансцендентного уравнения

У меня есть определенная кинематика как часть более сложной машины, и мне нужно вычислить некоторые физические параметры, которыеочень сложно (скорее как невозможно) измерить с должнымточность с инструментами, которые у меня есть в моем распоряжении

[кинематика]

На первый взгляд это просто1 степень свободы руки (черный), который может вращаться вокругx ось. У него есть вес, чтобы заставить его идти всегда вверх, пока он не достигнет конечной точки механики (уголa0) или какая-нибудь трубка (синяя) с радиусомr0, Центр вращения рычага находится наy0, Трубку можно перенести на любуюy(t) рост.

[использование]

Это используется для измерения радиуса трубы для дальнейшей обработки. Радиус может быть вычислен (с помощью базовой гониометрии), что приводит к уравнению внизу изображения. Константыa0,y0,z0 очень трудно измерить (это внутри сложного оборудования), поэтому точность измерения расстояний составляет минимум0.1 mm и угол0.1 deg и даже это сомнительно.

[Калибровка]

Поэтому я решил попробовать вычислить эти параметры из набора измерений, выполненных самой машиной (автокалибровка). Итак, у меня есть калибровочная трубка с известным радиусомr0, Все зеленые параметры могут быть обработаны как константы. Теперь я размещаю трубку вдольy ось, чтобы покрыть как можно больше углов руки. К сожалению, диапазон только о20 degrees (для текущей настройки машины) запоминание измеренногоa(t) для предустановкиy(t) ... какn точечный набор данных. Это дает мне системуn трансцендентные уравнения. Из этого я пытаюсь / угадываю "все" возможностиa0,y0,z0 помня лучшее решение (ближе всего кr0)

[приближение a0, y0, z0]

аппроксимация основана на этом моем классе:

//---------------------------------------------------------------------------
class approx
    {
public:
    double a,aa,a0,a1,da,*e,e0;
    int i,n;
    bool done,stop;

    approx()            { a=0.0; aa=0.0; a0=0.0; a1=1.0; da=0.1; e=NULL; e0=NULL; i=0; n=5; done=true; }
    approx(approx& a)   { *this=a; }
    ~approx()           {}
    approx* operator = (const approx *a) { *this=*a; return this; }
    //approx* operator = (const approx &a) { ...copy... return this; }

    void init(double _a0,double _a1,double _da,int _n,double *_e)
        {
        if (_a0<=_a1) { a0=_a0; a1=_a1; }
        else          { a0=_a1; a1=_a0; }
        da=fabs(_da);
        n =_n ;
        e =_e ;
        e0=-1.0;
        i=0; a=a0; aa=a0;
        done=false; stop=false;
        }
    void step()
        {
        if ((e0<0.0)||(e0>*e)) { e0=*e; aa=a; }         // better solution
        if (stop)                                       // increase accuracy
            {
            i++; if (i>=n) { done=true; a=aa; return; } // final solution
            a0=aa-fabs(da);
            a1=aa+fabs(da);
            a=a0; da*=0.1;
            a0+=da; a1-=da;
            stop=false;
            }
        else{
            a+=da; if (a>a1) { a=a1; stop=true; }       // next point
            }
        }
    };
//---------------------------------------------------------------------------

Он выполняет поиск по всему диапазону одной переменной на каком-то начальном этапе, затем находит точку минимального отклонения. После этого измените диапазон и шаг, чтобы закрыть область этой точки и рекурсивно увеличить точность.

Само решение выглядит так:

// (global) input data
#define _irc_calib_n 100
#define _irc_approx_n 5
int    irc_calib_ix; // number of measured points
double irc_calib_y[_irc_calib_n]; // y(t)
double irc_calib_a[_irc_calib_n]; // a(t)
double irc_calib_r; // calibration tube radius + arm radius

// approximation
int ix=0;
double e,a,deg=M_PI/180.0;
approx aa,ay,az;
//           min       max       step     recursions    ErrorOfSolutionVariable
for (aa.init(-90.0*deg,+90.0*deg,10.0*deg,_irc_approx_n,&e);!aa.done;aa.step())
for (ay.init(  0.0    ,200.0    ,10.0    ,_irc_approx_n,&e);!ay.done;ay.step())
for (az.init( 50.0    ,400.0    ,10.0    ,_irc_approx_n,&e);!az.done;az.step())
    {
    for (e=0.0,ix=0;ix<_irc_calib_n;ix++) // test all measured points (e is cumulative error)
        {
        a=irc_calib_a[ix]+aa.a;
        if (a> pi) a-=pi2;
        if (a<-pi) a+=pi2;
        if (fabs(a)>0.5*pi) { e=100.0; break; } // ignore too far angles
        e+=fabs(+(cos(a)*(irc_calib_y[ix]-ay.a))
                -(sin(a)*(az.a))
                -(irc_calib_r));
        }
    }
// here aa.a,ay.a,az.a holds the result

Это приводит к решению, близкому к измеренным значениям, но внутри моделирования результат все еще недостаточно точен. Он составляет от 0,1 мм до 0,5 мм в зависимости от количества точек и диапазона углов. Если я правильно измеритьz0 и игнорировать его приближение, то точность значительно повышается, оставляяy0 без ошибок (в симуляции) иa0 с погрешностью около 0,3 градуса

Q1, как я могу еще больше повысить точность решения?

Я не могу увеличить угловой диапазон. Количество очков лучше всего вокруг100 чем больше, тем выше точность, но выше 150 результат нестабилен (для некоторых радиусов он полностью выключен). Понятия не имею почему. Количество рекурсий выше6 не имеет большого эффекта

Может помочь взвешивания отклонений в зависимости от углового расстояния от0 degree ? Но к сожалениюa(t) диапазон не обязательно включает0 degrees

желаемая точность0.01 mm заy0,z0 а также0.01 degree заa0

Q2 есть что-то, что я пропустил?

Как неправильно вложенные аппроксимации или математическое упрощение или другой подход

[заметки]

Угол должен быть в формеa(t)+a0 потому что измеряется IRC с сбросом ПО (16000 steps/round). Это сбрасывается, когда вa0 Положение Я не считаю вибрации и эксцентриситет калибровочной трубки, о которых они уже позаботились, и моя первая цель - сделать эту работу в симуляции без них. тубаy(t) могут быть размещены по собственному желанию иa(t) Измерение может быть сделано по желанию.

Сейчас процесс сканирования калибровки указывает вдольy ось (движение отa0 вниз). Вычисление с6 рекурсии35 секунд (так что наберитесь терпения).5 рекурсии22 секунд

[edit1] здесь, как делается симуляция

approx aa; double e;
for (aa.init(-90.0*deg,+90.0*deg,10.0*deg,6,&e);!aa.done;aa.step())
 e=fabs(+(cos(aa.a)*(y(t)-y0))
        -(sin(aa.a)*(z0))
        -(irc_calib_r));
if (aa.a<a0) aa.a=a0;

[edit2] некоторые значения

Просто понял, что у меня было только4 рекурсии в коде моделирования, чтобы соответствовать точности входного IRC, то должно быть6 . рекурсии После изменения (также в предыдущем редактировании) вот некоторые результаты

                | a0[deg]| y0[mm] | z0[mm] | 
    simulated   | -7.4510|191.2590|225.9000|
    z0 known    | -7.4441|191.1433|225.9000|
    z0 unknown  | -7.6340|191.8074|225.4971|

Так что точность сz0 измеряется почти в желаемом диапазоне, но сz0 неизвестно ошибка все еще~10 раз больше, чем нужно. Увеличение точности моделирования не имеет эффекта выше6 рекурсии, а также нет смысла, потому что реальные входные данные также не будут более точными.

Вот смоделированные / измеренные точки для тестирования с вышеизложенными симуляциями:

 ix   a [deg]    y [mm]
  0   -0.2475 +105.7231 
  1   -0.4500 +104.9231 
  2   -0.6525 +104.1231 
  3   -0.8550 +103.3231 
  4   -1.0575 +102.5231 
  5   -1.2600 +101.7231 
  6   -1.4625 +100.9231 
  7   -1.6650 +100.1231 
  8   -1.8675  +99.3231 
  9   -2.0700  +98.5231 
 10   -2.2725  +97.7231 
 11   -2.4750  +96.9231 
 12   -2.6775  +96.1231 
 13   -2.8575  +95.3077 
 14   -3.0600  +94.5154 
 15   -3.2625  +93.7231 
 16   -3.4650  +92.9308 
 17   -3.6675  +92.1385 
 18   -3.8700  +91.3462 
 19   -4.0725  +90.5538 
 20   -4.2750  +89.7615 
 21   -4.4877  +88.9692 
 22   -4.6575  +88.1769 
 23   -4.8825  +87.3615 
 24   -5.0850  +86.5154 
 25   -5.2650  +85.7000 
 26   -5.4675  +84.9077 
 27   -5.6700  +84.1154 
 28   -5.8725  +83.3231 
 29   -6.0750  +82.5308 
 30   -6.2775  +81.7000 
 31   -6.5025  +80.8462 
 32   -6.6825  +80.0462 
 33   -6.8850  +79.2538 
 34   -7.0875  +78.4615 
 35   -7.2900  +77.6538 
 36   -7.5159  +76.7692 
 37   -7.6725  +75.9769 
 38   -7.8750  +75.1846 
 39   -8.1049  +74.3692 
 40   -8.2800  +73.5000 
 41   -8.4825  +72.7077 
 42   -8.6850  +71.9154 
 43   -8.9100  +71.0308 
 44   -9.0900  +70.2231 
 45   -9.2925  +69.4308 
 46   -9.5175  +68.5462 
 47   -9.6975  +67.7462 
 48   -9.9000  +66.9462 
 49  -10.1025  +66.0615 
 50  -10.3148  +65.2692 
 51  -10.4850  +64.3769 
 52  -10.6875  +63.5846 
 53  -10.9125  +62.7462 
 54  -11.0925  +61.9077 
 55  -11.2950  +61.0846 
 56  -11.4975  +60.2231 
 57  -11.7000  +59.3923 
 58  -11.9025  +58.5308 
 59  -12.1288  +57.6692 
 60  -12.3075  +56.8385 
 61  -12.5100  +55.9462 
 62  -12.7125  +55.1538 
 63  -12.9150  +54.2615 
 64  -13.1175  +53.4000 
 65  -13.2975  +52.5769 
 66  -13.5000  +51.6846 
 67  -13.7025  +50.7923 
 68  -13.9050  +50.0000 
 69  -14.1075  +49.1077 
 70  -14.3100  +48.2154 
 71  -14.5350  +47.3615 
 72  -14.7150  +46.5308 
 73  -14.9175  +45.6385 
 74  -15.1200  +44.7462 
 75  -15.3225  +43.8538 
 76  -15.5250  +42.9615 
 77  -15.7490  +42.0692 
 78  -15.9075  +41.2769 
 79  -16.1100  +40.3846 
 80  -16.3125  +39.4923 
 81  -16.5150  +38.6000 
 82  -16.7175  +37.7077 
 83  -16.9200  +36.8154 
 84  -17.1225  +35.9231 
 85  -17.3250  +34.9308 
 86  -17.5275  +34.0385 
 87  -17.7300  +33.1462 
 88  -17.9325  +32.2538 
 89  -18.1350  +31.3615 
 90  -18.3405  +30.4692 
 91  -18.5175  +29.4769 
 92  -18.7200  +28.5846 
 93  -18.9225  +27.6923 
 94  -19.1250  +26.8000 
 95  -19.3275  +25.8077 
 96  -19.5300  +24.9154 
 97  -19.7325  +23.9231 
 98  -19.9350  +23.0308 
 99  -20.1375  +22.1385 

[edit3] обновление прогресса

некоторые разъяснения для @Ben

как это устроено

цветное уравнение под первым изображением дает радиусr0 сделано из 2-х объединенных90 degree треугольники (базовая тригонометрия)

красный материал:

y(t) это моторное положение, и это известноa(t) также известно состояние IRC

зеленый материал:

a0,y0,z0 являются механическими размерами и известны, но не точные, поэтому я измеряю многоa(t) для разных позицийy(t) с известной калибровочной трубкойr0 и вычислитьa0,y0,z0 с большей точностью из этого

дальнейшее повышение точности

На самом деле мне удалось получить более точное измерениеy1=y0+z0*cos(a0) от специального калибровочного движения с точностью вокруг0.03 mm и лучше. Это высота пересечения руки вa0 положение и трубкаy ось движения. Он измеряется и интерполируется из ситуации, когда рука впервые контактирует, когда трубка идет сверху вниз, но реальное положение должно быть пересчитано по используемому радиусу иa0... потому что точка контакта не на этой оси ... (еслиr0=0.0). Это также исключает один цикл аппроксимации из калибровки, потому чтоy1,a0,z0 являются зависимыми и могут быть вычислены друг от друга. Также устранение двойного алиасинга из измерения IRC из-за прерывистого способа измерения иa(t),y(t) позиции очень помогли повысить точность и стабильность вычислений (на реальной машине). Я не могу достоверно оценить точность прямо сейчас, потому что, анализируя множество измеренных циклов, я обнаружил некоторые механические проблемы на машине, поэтому я жду, пока она отремонтируется. В любом случае калибровка против точности моделирования дляr0=80.03 mm с учетом обоих подходов и_irc_calib_n=30 сейчас:

    ;      computed     simulated  |delta|
    a0=  -6.915840 ;  -6.916710   +0.000870 deg
    y0=+186.009765 ;+186.012822   +0.003057 mm
    y1=+158.342452 ;+158.342187   +0.000264 mm
    z0=+228.102470 ;+228.100000   +0.002470 mm

Чем больше калибровкаr0 меньшая точность (из-за более ограниченногоa(t) диапазон) это вычисляя всеa0,y0,(y1),z1 ничего не измеряется прямо или известно. Это уже приемлемо, но, как я уже писал ранее, нужно проверить машину, когда она будет готова. Просто чтобы завершить, вот как выглядят смоделированные измерения сейчас:

[edit4] см.Как работает поиск приближения

Ответы на вопрос(1)

Ваш ответ на вопрос