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

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