root/branches/atorf/NXC/MotorControl2/NextGenMotor.nxc @ 588

Revision 588, 18.8 KB (checked in by atorf, 4 years ago)
Line 
1/*
2%
3% The NXT Program MotorControl enables precise motor movement via direct
4% commands. It listens to "NXT BT messages", interprets their content (own user-
5% defined "protocol", if you will), and carries out highly precise motor actions
6% in a different thread for each motor, allowing the upper level program (in
7% this case, MATLAB) to carry on with execution...
8%
9% Signature
10%   Author: Linus Atorf (see AUTHORS)
11%   Date: 2009/04/02
12%   Copyright: 2007-2009, RWTH Aachen University
13%
14%
15% ***********************************************************************************************
16% *  This file is part of the RWTH - Mindstorms NXT Toolbox.                                    *
17% *                                                                                             *
18% *  The RWTH - Mindstorms NXT Toolbox is free software: you can redistribute it and/or modify  *
19% *  it under the terms of the GNU General Public License as published by the Free Software     *
20% *  Foundation, either version 3 of the License, or (at your option) any later version.        *
21% *                                                                                             *
22% *  The RWTH - Mindstorms NXT Toolbox is distributed in the hope that it will be useful,       *
23% *  but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS  *
24% *  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
25% *                                                                                             *
26% *  You should have received a copy of the GNU General Public License along with the           *
27% *  RWTH - Mindstorms NXT Toolbox. If not, see <http://www.gnu.org/licenses/>.                 *
28% ***********************************************************************************************
29*/
30
31
32
33#include "MotorFunctions.nxc"
34#include "SpeedFromPosLookup.nxc"
35#include "SpeedFromPosLookup.nxc"
36#include "SpeedMonitor.nxc"
37
38
39
40#define OUTBOX 1
41
42#define SPEEDHISTORY 2
43
44#define LASTSTEPBRAKINGDEG 2
45#define ENDGAMEBRAKINGSTARTDIST 10
46
47
48// CONSTANT LOOP TIMING
49#define LOOP_DURATION 24
50
51
52// Driving "stages" or "phases" during controlled motor loop...
53#define STAGE_RAMPUP 1
54#define STAGE_DRIVING 2
55#define STAGE_BRAKING 3
56#define STAGE_ENDGAME 4
57
58
59// PID parameters
60#define PIDUPSCALING 10000
61// HAVE TO BE UPDATED ALL THE TIME MANUALLY, depend on PIDUPSCALING!!!
62#define HARDCODED_KP -500
63#define HARDCODED_KD -2500
64/*  // this was the "original formula"
65    long KP =  -1 * PIDUPSCALING / 20;  // -1/40 to -1/60 ????
66    long KD =  -1 * PIDUPSCALING /  4;  // -1/2  to -1/3
67*/
68
69
70
71// --- DEBUGGING OPTIONS -----------
72#define ENABLEDEBUGGING_LCD
73#define ENABLEDEBUGGING_REMOTE
74// ---------------------------------
75
76
77
78//TODO optimize message-parsing (ReceiveRemoteNumber, etc?)
79
80//TODO remember what happens when CHANGING SPEED during RUNTIME?!?!!?!!
81
82
83
84inline long GetAbsBrakingDistFromAbsSpeed(long absSpeed) {
85    // absolute max realistic speed with full batteries (not akkus) is 1100 deg per sec
86    return absSpeed / 4;
87}//end function
88
89inline long GetSgnBrakingDistFromSgnSpeed(long sgnSpeed) {
90    // absolute max realistic speed with full batteries (not akkus) is 1100 deg per sec
91    return sgnSpeed / 4;
92}//end function
93
94
95
96void RunMotor(byte port, int power, long tacholimit, bool speedreg, bool endbrake) {
97
98// **** THINGS NOT IN THIS FUNCTION
99// - decision if NO CONTROLLED LOOP is needed (classic way?!)
100// - defer as much decision etc as possible to MATLAB, see below
101
102// **** More lookups needed:
103// - Brakingdist from Speed
104// - Absolute acceleration power during rampup (in discrete steps like 10, 20, etc)
105
106
107// --- Some DECLARATIONS -- not all of them, this is timecritical
108    long i;
109
110    int  powerSgn       = sign(power);
111    long tachoTarget    = powerSgn * tacholimit;
112
113
114// --- Reset counters etc
115    ResetErrorCorrectionAndBlockCount(port);
116
117
118// --- Decide if SIMPLE FUNCTION needed?
119//     * For small powers!
120//     * For (very?) short distances???
121
122// --- Start driving (important that its EARLY in this function -- for direct commands)
123//     * Runstate RUNNING
124//     * power = +-1
125//     * tacholimit already...
126    MotorCmdSingle(port, powerSgn, tacholimit, speedreg);
127
128
129// --- Declare and Initialize various variables
130//     * STAGE is RAMPUP
131    int  curStage = STAGE_RAMPUP;
132
133    int  absPower = abs(power);
134
135    long absBrakingDist;
136    long brakingStartSpeed;
137    long brakingStartTacho;
138
139    long loopStartTick;
140    long waitEndTick;
141
142    //PID stuff
143    long err_0 = 0;  // current error (t =   0)
144    long err_1 = 0;  // prev.   error (t = - T)
145    long curPIDPowerUpscaled = absPower * PIDUPSCALING;
146    long newAbsPower;
147
148    // speed stuff
149    long curSpeed;
150    int  SpeedLogIndex = 0;
151    long TachoCountLog[SPEEDHISTORY];
152    long TickCountLog[SPEEDHISTORY];
153
154    // init speedlog
155    for(i = 0; i < SPEEDHISTORY; i++) {
156        TachoCountLog[i] = 0; // should still be 0, was reset just a moment ago
157        TickCountLog[i]  = CurrentTick(); //careful, check if this works...
158    }//end for
159
160
161#ifdef ENABLEDEBUGGING_LCD
162    long loopCount = 0;
163    long lastTick  = CurrentTick();
164    long lastLoopTime;
165#endif// - - - - - - - - - -
166
167#ifdef ENABLEDEBUGGING_REMOTE
168    string tmp;
169    string tmp1;
170    string tmp2;
171    string tmp3;
172#endif// - - - - - - - - - -
173
174
175
176// --- MAIN LOOP ---------------------
177    while(true) {
178   
179//    * record time
180        loopStartTick = CurrentTick();
181       
182        #ifdef ENABLEDEBUGGING_LCD
183            loopCount++;
184            lastLoopTime = CurrentTick() - lastTick;
185            lastTick     = CurrentTick();
186        #endif// - - - - - - - - - -
187       
188
189//    * check STOP CONDITIONS?
190//      - already there?
191//      - direct command stopped us?
192        if ( abs(tachoTarget - MotorTachoCount(port)) >= 2 ) {
193            // we are almost already there :-)
194            // or even too far :-/
195        }//end if
196       
197        if ( MotorPower(port) == 0 ) {
198            // direct command must have stopped us!
199        }//end if
200       
201
202//    * RECORD SPEED
203//      - with this long loop execution time, use speed history of 2
204//      - 2 vars, toggle between history...
205        curSpeed = ((MotorTachoCount(port) - TachoCountLog[SpeedLogIndex]) * 1000) / (CurrentTick() - TickCountLog[SpeedLogIndex]);
206
207        //TODO optimize speed logging by using only 2 history values and toggling between them
208
209        // record speedlog data to previous index...
210        i = SpeedLogIndex - 1;
211        if (i < 0) i = SPEEDHISTORY-1;
212        TachoCountLog[i] = MotorTachoCount(port);
213        TickCountLog[i]  = CurrentTick();
214
215        // move current index for next reading
216        SpeedLogIndex++;
217        if (SpeedLogIndex > SPEEDHISTORY-1) SpeedLogIndex = 0;
218
219
220//    * if RAMPUP STAGE:
221//      - increase power (set according value from lookup / count loop-passes)
222//      - enter NEXT STAGE when power is reached...
223//      - maybe enable SPEED REG if not already done?
224        if (curStage == STAGE_RAMPUP) {
225       
226            //TODO do a real manual nice rampup here
227            // for now we set to fullspeed right away
228            UpdatePower(port, power);
229            curStage = STAGE_DRIVING;
230
231
232        }//end if
233       
234//    * if DRIVING STAGE:
235//      - look up current braking distance from current speed
236//      - decide: stage change? time to brake?
237//      - enter braking stage, set braking dist!
238//      - disable speed reg if necessary...
239        if (curStage == STAGE_DRIVING) {
240       
241            absBrakingDist = GetAbsBrakingDistFromAbsSpeed(abs(curSpeed));
242            if ( abs(tachoTarget -  MotorTachoCount(port)) <= absBrakingDist ) {
243                // ok, we're close enough, start braking!
244                curStage = STAGE_BRAKING;
245                // record this for later down (for speed lookup)
246                brakingStartSpeed = curSpeed;
247                brakingStartTacho = powerSgn * (tacholimit - absBrakingDist);
248                //TODO disable SPEED REG if needed!!!
249            }//end if
250
251
252        }//end if
253       
254//    * if BRAKING STAGE:
255//     ( - check endbraking special mode conditions )
256//     (    . maybe skip PID if time for endgame?   )
257//      - PID control
258//        . calc current error
259//        . calc new setpoint
260//        . use KP and KD constants as MACROS (#define), not VARS!
261//        . clip power and set
262        if (curStage == STAGE_BRAKING) {
263
264            // shift/calculate all errors for current cycle
265            err_1 = err_0;
266            err_0 = curSpeed - GetIdealSpeedFromPos(abs(MotorTachoCount(port) - brakingStartTacho), brakingStartSpeed, absBrakingDist);
267
268
269            curPIDPowerUpscaled = curPIDPowerUpscaled
270                                + (KD * (err_0 - err_1))
271                                + (KP * err_1);
272
273
274            //TODO clip these values!
275            newAbsPower = curPIDPowerUpscaled / PIDUPSCALING;
276            if (newAbsPower > 100) {
277                newAbsPower = 100;
278            } else if (newAbsPower < 1) {
279                newAbsPower = 1;
280            }//end if
281           
282            //finally set new power!
283            UpdatePower(port, sgn * newAbsPower);
284           
285           
286
287            #ifdef ENABLEDEBUGGING_REMOTE
288                tmp1 = NumToStr(err_0);
289                tmp2 = NumToStr(idealSpeed);
290                tmp3 = NumToStr(curSpeed);
291                tmp = StrCat(tmp1, "|", tmp2, "|", tmp3);
292                SendMessage(OUTBOX, tmp);
293            #endif// - - - - - - - - - -
294           
295        }//end if
296
297//    * WAIT
298//      - burn constant CPU time by waiting for max delay...
299//      - keep on checking tachocount for ENDGAME MODE?
300//      - (MAYBE keep on checking direct command cancellation?)
301        waitEndTick = loopStartTick + LOOP_DURATION;
302        while (CurrentTick() < waitEndTick) {
303
304            if ( abs(tachoTarget - MotorTachoCount(port)) <  ENDGAMEBRAKINGSTARTDIST) {
305               curStage = STAGE_ENDGAME;
306               break;
307            }//end if
308           
309        }//end while
310
311//    * if SPECIAL ENDGAME BRAKING
312//      - FAST & SIMPLE INNER LOOP!!!
313//        . check position
314//        . enable HARD BRAKE if necessary, jump out of loop
315        if (curStage == STAGE_ENDGAME) {
316       
317            // fast inner loop now!
318            // don't forget TIMEOUT or something, avoid possible lock up
319           
320        }//end if
321
322
323
324    }//end while
325// --- END MAIN LOOP ---------------------
326
327// --- if loop exited manually by direct command, exit void!
328
329// --- monitor braking (as in old MotorControl)
330//     * release when necessary at full stop
331
332// --- Motor off / idle, over & out?
333
334
335}//end void
336
337
338
339
340
341
342// -------------------------------------------------
343// main function
344task main(){
345
346
347    byte port = OUT_B;
348    byte power = 100;
349    bool speedreg = false;
350
351    // total dist to move
352    long TotalDist = 1000;
353    // length of braking process = when to start braking
354    long BrakingDist = 220;
355    // how long should the braking take?
356    long BrakingTime = 3000;
357
358    long MinPower = 0;
359    long MaxPowerOffset = 0;
360
361
362
363    // saving performance:
364    long NormalDrivingDist = (TotalDist - BrakingDist);
365
366    // Start of deceleration in ticks...
367    long DecelStart;
368    long DecelStop;
369
370
371    long t; // braking since...
372    long x; // current pos...
373
374    int NewSpeed;
375
376    string tmp = "";
377    string tmp1 = "";
378    string tmp2 = "";
379    string tmp3 = "";
380
381    long curPIDPower;
382    long curPIDPowerUpscaled;
383
384    long err_0 = 0;  // current error (t =   0)
385    long err_1 = 0;  // prev.   error (t = - T)
386
387    //
388    long KP =  -1 * PIDUPSCALING / 20;  // -1/40 to -1/60 ????
389    long KD =  -1 * PIDUPSCALING /  4;  // -1/2  to -1/3
390   
391   
392    /* stable working PD with wait(4) one thread...
393    long P =  -1 * PIDUPSCALING /  3;  // -1/2  to -1/3
394    long I =  -1 * PIDUPSCALING / 30;  // -1/40 to -1/60 ????
395    long D =  -0 * PIDUPSCALING /  3;  // -1 to -2
396    /*
397
398
399    /* working slow set for looptime = 18
400    long P =  (-1 * PIDUPSCALING) /  6;  // -1/2  to -1/3
401    long I =  (-1 * PIDUPSCALING) / 15;  // -1/40 to -1/60 ????
402    long D =  (-1 * PIDUPSCALING) /  4;  // -1 to -2
403    */
404
405    /*
406    long P =  (-1 * PIDUPSCALING) /  (3 * 18);  // -1/2  to -1/3
407    long I =  (-1 * PIDUPSCALING) / (25 * 18);  // -1/40 to -1/60 ????
408    long D =  (-2 * PIDUPSCALING) /  (3 * 18);  // -1 to -2
409    */
410
411
412    long loopCount = 0;
413
414
415    int SpeedLogIndex = 0;
416    long TachoCountLog[SPEEDHISTORY];
417    long TickCountLog[SPEEDHISTORY];
418
419    long tmpTachoCount;
420    long tmpTickCount;
421
422    long initSpeed = -1;
423    long curSpeed = 0;
424    long curTick;
425    long LastTick;
426    long LastLoopTime;
427
428
429    long idealSpeed = 0;
430
431    int i;
432
433    // performance-stuff
434    //int SPEEDHISTORY_MINUS1 = SPEEDHISTORY - 1;
435
436
437// **** Init
438    //SetPosPredictionParams(BrakingDist, BrakingTime);
439    InitSpeedFromPosLUT();
440
441    start SpeedMonitor_A_;
442    start SpeedMonitor_B_;
443    start SpeedMonitor_C_;
444    Wait(20);
445
446
447// **** Init / Reset motor
448    MotorOff(port);
449    ResetErrorCorrectionAndBlockCount(port);
450
451    Wait(1100);
452
453
454// **** Start moving!
455    MotorCmdEx(port, power, TotalDist, 0, speedreg, false, OUT_RUNSTATE_RUNNING);
456
457
458
459
460
461// **** Normal drive, wait for braking
462    while(MotorTachoCount(port) <= (TotalDist - BrakingDist)) {
463
464        // record position and time once at brakingdist before braking starts...
465        if ((initSpeed == -1) && (MotorTachoCount(port) >= (TotalDist - 2*BrakingDist))) {
466            tmpTachoCount = MotorTachoCount(port);
467            tmpTickCount = CurrentTick();
468            initSpeed = 0;
469        }//end if
470
471    }//end while
472
473    /*
474    // calc real init speed
475    if (CurrentTick() - tmpTickCount) > 0 {
476        initSpeed = ((MotorTachoCount(port) - tmpTachoCount) * 1000) / (CurrentTick() - tmpTickCount);
477    }//end if
478    */
479
480    // init speed history log
481    for(i = 0; i < SPEEDHISTORY; i=i+1) {
482        TachoCountLog[i] = tmpTachoCount;
483        TickCountLog[i] = tmpTickCount;
484    }//end for
485
486
487    // initSpeed was used as flag before, now a real value:
488    // TODO FIXME: check against *** div by 0 ***
489    initSpeed = ((MotorTachoCount(port) - tmpTachoCount) * 1000) / (CurrentTick() - tmpTickCount);
490
491
492// **** Send new output command for debug info
493    //MotorCmdEx(port, power, BrakingDist , 0, speedreg, false, OUT_RUNSTATE_RUNNING);
494
495// ****
496
497
498// **** Deceleration
499
500
501
502    DecelStart = CurrentTick();
503    LastTick = CurrentTick() - 20;
504
505    // deceleration control loop
506    t = 0;
507    curPIDPower = power;
508    curPIDPowerUpscaled = curPIDPower * PIDUPSCALING;
509   
510    while(MotorTachoCount(port) < (TotalDist - 0)) {
511
512        loopCount++;
513        LastLoopTime = CurrentTick() - LastTick;
514        LastTick = CurrentTick();
515
516        // calc vars we need...
517        //curTick = CurrentTick();
518        x = MotorTachoCount(port) - NormalDrivingDist;
519
520        // calc speed
521        //TODO WARNING FIXME: make sure div by 0 never happens...
522        curSpeed = ((MotorTachoCount(port) - TachoCountLog[SpeedLogIndex]) * 1000) / (CurrentTick() - TickCountLog[SpeedLogIndex]);
523
524
525        // record speedlog data to previous index...
526        i = SpeedLogIndex - 1;
527        if (i < 0) i = SPEEDHISTORY-1;
528        TachoCountLog[i] = MotorTachoCount(port);
529        TickCountLog[i] = CurrentTick();
530
531
532        // move current index for next reading
533        SpeedLogIndex++;
534        if (SpeedLogIndex > SPEEDHISTORY-1) SpeedLogIndex = 0;
535
536
537
538
539        idealSpeed = GetIdealSpeedFromPos(x, initSpeed, BrakingDist);
540        //idealSpeed = GetIdealSpeedFromPos(x, 100, BrakingDist);
541
542
543        // shift/calculate all errors for current cycle
544        err_1 = err_0;
545        err_0 = curSpeed - idealSpeed;
546
547
548        // calculate new output
549        //TODO try to leave a version of curPIDPower always upscaled, to avoid 1 multiplcation
550        /*
551        curPIDPower = (curPIDPower * PIDUPSCALING
552              +  (    KD * (err_0 - err_1)
553                    + KP * err_1
554                  )
555                ) / PIDUPSCALING;
556         */
557         
558       
559        curPIDPowerUpscaled = curPIDPowerUpscaled
560                            + (KD * (err_0 - err_1))
561                            + (KP * err_1);
562
563
564
565
566
567
568
569        tmp1 = NumToStr(err_0);
570        tmp2 = NumToStr(idealSpeed);
571        tmp3 = NumToStr(curSpeed);
572        tmp = StrCat(tmp1, "|", tmp2, "|", tmp3);
573        SendMessage(OUTBOX, tmp);
574
575
576
577
578        NewSpeed = curPIDPowerUpscaled / PIDUPSCALING;
579        //NewSpeed = idealSpeed;
580
581
582        //TODO decided wether NewSpeed 0 or 1 is ok...?
583        if (NewSpeed > 100) {
584            NewSpeed = 100;
585            //curPIDPower = 100;
586        } else if (NewSpeed < 1) {
587            NewSpeed = 1;
588            //curPIDPower = 0;
589        }//end if
590
591        /* but be careful...
592        if (NewSpeed == 0) {
593            NewSpeed = 0;
594        } else if (NewSpeed > 100) {
595            NewSpeed = 100;
596        } else if (NewSpeed < 1) {
597            NewSpeed = 0;
598        }//end if
599        */
600
601
602        // finally, set that power value:
603        UpdatePower(port, NewSpeed);
604
605
606        if (MotorTachoCount(port) >= (TotalDist - LASTSTEPBRAKINGDEG)) {
607            // jump into final fast waiting loop...
608            break;
609        }//end if
610
611        // check if braking time is exceeded...
612        if (t >= BrakingTime) {
613            break;
614        } else {
615            //tmp = NumToStr(t);
616            //TextOut(0,LCD_LINE1, tmp);
617        }//end if
618
619        //Wait(4);
620
621    }//end while
622
623
624// **** Hard stop
625    // wait the final tick...
626    while((MotorTachoCount(port) < TotalDist)) {
627        // nothing...
628    }//end while
629
630    DecelStop = CurrentTick();
631
632    MotorBrake(port);
633
634    //TextOut(0,LCD_LINE1, "Braked...");
635    Wait(1000);
636
637    MotorOff(port);
638
639// **** Debug statistics about precision
640
641    tmp = NumToStr(loopCount);
642    tmp = StrCat("loops=", tmp);
643    TextOut(0,LCD_LINE1, tmp);
644
645    tmp = NumToStr((DecelStop - DecelStart) / loopCount);
646    tmp = StrCat("t/loop=", tmp);
647    TextOut(0,LCD_LINE2, tmp);
648
649    tmp = NumToStr((loopCount * 1000) / (DecelStop - DecelStart));
650    tmp = StrCat("loops/s=", tmp);
651    TextOut(0,LCD_LINE3, tmp);
652
653    tmp = NumToStr(LastLoopTime);
654    tmp = StrCat("lastloop=", tmp);
655    TextOut(0,LCD_LINE4, tmp);
656
657    tmp = NumToStr(TotalDist);
658    tmp = StrCat("goal=", tmp);
659    TextOut(0,LCD_LINE6, tmp);
660
661    tmp = NumToStr(MotorTachoCount(port));
662    tmp = StrCat("real=", tmp);
663    TextOut(0,LCD_LINE7, tmp);
664
665
666    until (ButtonPressed(BTNCENTER, false));
667
668}//end main task
Note: See TracBrowser for help on using the browser.