Rotation Count Question

HomeForumsMonoBrick EV3 FirmwareRotation Count Question

Viewing 14 posts - 1 through 14 (of 14 total)
  • Author
    Posts
  • #3645
    Author Image
    Devirex
    Participant

    Hello,

    first of all thanks for the great firmware. I’ve set my EV3 up from a Windows PC with no problems.
    I tried to run following function which should speed up the motor linearly to 100 and stop at 3600 deg.

    
    public void myStart(){
      Motor motor = new Motor(MotorPort.OutA);
      motor.On(0);
      for(int i = 0; i < 1000; i++){
          motor.SetPower(i); 
          System.Threading.Thread.Sleep(10);
      }
      while(motor.GetTachoCount() <= 3600){
          System.Threading.Thread.Sleep(1);
      }
      motor.Brake();
    }
    

    The function works but the motor stops something between 3605 and 3630. Is there another way to get a running motor stopped exactly on a position?

    #3650
    Author Image
    Anders Søborg
    Keymaster

    Hi

    Thanks for trying the MonoBrick Firmware.

    There is no need to poll the tacho count. Simply use the functions already provided.

    
    LcdConsole.WriteLine ("Creating a step profile");
    motor.SpeedProfileStep(40,100, 1500, 100, true);
    motor.Off(); 
    System.Threading.Thread.Sleep(2000);
    LcdConsole.WriteLine ("Move to zero");
    motor.MoveTo(40, 0, true);
    LcdConsole.WriteLine("Motor at position: " + motor.GetTachoCount());
    System.Threading.Thread.Sleep(2000);
    

    You can read more about the motor functions here. You might also want to check out the examples here.

    Anders

    #3656
    Author Image
    Devirex
    Participant

    Hello Anders,

    thanks for your quick reply. Yes i know that functions. I’m also very familiar with the code of the original lego firmware. I’m creating a robot which controls a kind of pendulum. So i have to write functions which accelerate the motors slow, linear and exponential. With exponential i mean the inverted way of ramp up and ramp down which is implemented in the lms_pwm device (or you can call it the driver of the motors from lego).
    And i want to trigger other functions when the pendulum is in a specific position.

    I also tried to realize it with the EV3-C/EVC Api but the “GetRotationCount” function is also slow there.
    Do you know why?

    #3667
    Author Image
    Anders Søborg
    Keymaster

    Hi again

    I deleted some part of the discussion since I just realised that I misunderstood what it is your are trying to do.

    Are excepting your program to do stop at 3600? this will never work as the sample frequency at which you are polling the tacho count is not fixed. You need to write a control loop with feedback – the easy way is to write a PID controller where your set-point is the position that you want to move to and the feedback is the tacho value. Once you have the PID controller working you need to create a profile generator to generate the profile/ramp that you want the motor “follow”. I will be back from vacation the day after tomorrow – so if you need any help to get started let me know and I can post some code to get the PID controller working.

    Anders

    #3668
    Author Image
    Devirex
    Participant

    Thanks for your quick answer. Am i right that the tacho count function is called to often and thats why the reply is not
    correct? It would be really great to get help from you how to realize that PID-Controller thing. Also it would be great to get a more detailed explanation what i’ve done wrong. Have a nice vacation.

    #3674
    Author Image
    Anders Søborg
    Keymaster

    Hi again.

    Let me start off by “reviewing” your program. For whatever reason you have the following code when waiting for the tacho count to exceed 3600.

    
    while(motor.GetTachoCount() <= 3600){
       System.Threading.Thread.Sleep(1);
    }
    

    On Windows the minimum sleep time is something like 20-30 ms – what it is on the EV3’s ARM processor I am not aware off but you could do some measurements to find out. Moreover Thread.Sleep is not guaranteed to sleep for exactly the specified amount of time – so that is why you get different results each time. So instead of suspending the current thread with a sleep do busy polling like shown below.

    
    using System;
    using MonoBrickFirmware;
    using MonoBrickFirmware.Movement;
    using MonoBrickFirmware.Display;
    using MonoBrickFirmware.UserInput;
    
    namespace MotorExample
    {
    	class MainClass
    	{
    		public static void Main (string[] args)
    		{
    			  
    			  Motor motor = new Motor(MotorPort.OutA);
    			  motor.ResetTacho();
    			  motor.On(100);
    			  while(motor.GetTachoCount() <= 3600){}
    			  motor.Brake();
    			  LcdConsole.WriteLine("Tacho count: " + motor.GetTachoCount());
    			  Buttons btns = new Buttons();
    			  btns.GetKeypress();
                              motor.Off();       
    		}
    	}
    }
    

    When I run this program the motor stops at the same position each time (+/- 1 degree). However the position it stops at is 3625 due to that fact that when you “ask” the motor to stop it will have a certain moment of inertia so physically you can not stop strait away. When you run the above program you will probably get a different result – your motor might stop at 3619 each time – why? You might be using rechargeable batteries which has a lower voltage so your motor will actually run slower when you apply full “speed” by calling motor.On(100). Another option could be that your motor might have a slightly larger internal resistance (more friction or even a different internal ohmic resistance). This is the reason why this is a bad solution for position control. Another reason is that the motor might have a large load (a large robot that it has to move) and when you try to brake instantly when it is running at full speed – it will take longer to brake compared to a motor without a load. The position it stops at even depends on the surface on which the robot is running… for some project the code above might be sufficient but if you want to be sure that you end up at the exact position you need to use some sort of control loop.

    With a control loop you calculate the difference between where you want to move and the current position. This difference is most often called “the error”. The error is then fed into the controller. Based on the current error (and the previous errors) a new output value is calculated and applied. This is repeated until the error is zero (or within some limits). A control loop is not only limited to controlling the position but can also be applied to control the speed.

    The function build into the MonoBrick firmware calls the “kernel” driver which uses a PID controller with a build in ramp generator. This is fine for most motor movements. What is wrong with the ramp of this function?

    #3684
    Author Image
    Devirex
    Participant

    Hi again,

    thanks for the explanation. Since your last post i have read some theory of PID Controllers and can now fully understand whats happening. Ok now the details of my Robot. I built a functional model of Top Spin (http://www.capte.org/img/enciclopedia/TopSpin.jpg).
    I want to to let it run like in real. The Arm is very heavy about 1kg and its driven by both EV3 Motors with a transmission of 2100 degrees per round. I connected the EV3 to a AC/DC Adapter. If i use the built in Ramp Up it will start too slow in the first third and then accelerate too fast in the last third of the ramp, it looks really unnatural.
    In the last third of the ramp i have also the problem that the load is too much for the motors and its speed of accelaration at this time.

    • This reply was modified 10 years, 3 months ago by Author ImageDevirex.
    #3694
    Author Image
    Anders Søborg
    Keymaster

    Hi again

    Did you create your own profile or are you just calling the move to function?

    Anders

    #3703
    Author Image
    Devirex
    Participant

    If you mean with profile the three steps than yes. Its the ramp itsself which does not fit, the lenght of it doesn`t change that. Also it starts always with a speed of 0. If i want to acceelerate from 40 to 70 there is no built in function to do that with a ramp i think.

    #3704
    Author Image
    Anders Søborg
    Keymaster

    Ok then you need to create your own PID controller. We will add one at some point since the profile as you mention makes it impossible to start at some speed. If you can not wait for this use the following PID code to get started (originally C++)

    
    using System;
    
    namespace MonoBrickFirmware.Movement
    {
    	class PID
    	{
    		private float k1;
    	    private float k2;
    	    private float k3;
    	   
    	    private float Kp;
    	    private float Ki;
    	    private float Kd;
    	   
    	    private float Ts;
    	   
    	    private float ek2;
    	    private float ek1;
    	   
    	    private float uk;
    	    private float uk1;
    	    private float max;
    	    private float min;
    	   
    	    private float maxChange;
    	    private float minChange;
    	   
    	    private bool maxMinChangeSet;
    	    
    		private void update(){
    	      k1 = Kp;
    	      k2 = Kp*(Ts/Ki);
    	      k3 = Kp*(Kd/Ts);
    	    }
     	 	public PID(float P, float I, float D, float newSampleTime, float maxOut, float minOut, float maxChangePerSec = 0.0f, float minChangePerSec = 0.0f){
    			Kp = P;
    			Ki = I;
    			Kd = D;
    			Ts = newSampleTime;
    			ek1 = 0;
    			ek2 = 0;
    			uk1 = 0;
    			max = maxOut;
    			min = minOut;
    			if(maxChangePerSec != 0.0){
    			  maxMinChangeSet = true;
    			  maxChange = Ts * maxChangePerSec;
    			  minChange = Ts * minChangePerSec;
    			}
    			else{
    			  maxMinChangeSet = false;
    			}
    			update();
    		}
    		public void setP(float P){
    		 	 Kp = P;
    		  	update();
    		}
    		public void setI(float I){
    		  	Ki = I;
    		  	update();
    		}
    		public void setD(float D){
    		  	Kd = D;
    		  	update();
    		}
    		public void setMaxMin(float newMax, float newMin){
    			max = newMax;
    			min = newMin;
    		}
    		
    		public void setUk1(float newUk1){
    		 	uk1 = newUk1; 
    		}
    		
    		public void setSampleTime(float time){
    			Ts = time;
    		  	update(); 
    		}
    		
    		public float output(float ek){
    			uk = uk1 + k1 *(ek -ek1) + k2*ek +k3*(ek-2*ek1+ek2);
    			if(uk > max){uk = max;}
    			if(uk < min){uk = min;}
    			
    			if(maxMinChangeSet){
    			  if( (uk-uk1) > maxChange){uk = uk1 + maxChange;}
    			  if( (uk-uk1) < minChange){uk = uk1 + minChange;}
    			}
    			uk1 = uk;
    			ek2 = ek1;
    			ek1 = ek;
    			
    			return uk;
    		}
    	};
    }
    
    

    Anders

    #3710
    Author Image
    Devirex
    Participant

    Hi again,

    thanks for the code. Now the question of the questions. How can i use this.
    Could you give me an example how to use it?

    #3720
    Author Image
    Anders Søborg
    Keymaster

    Call the output(float ek) with the error that you just calculated this will give you the output to apply. Call this code with a fixed interval/sample rate. The constructor takes four arguments P, I, D and the sample rate. You will need to do some tuning on the P, I and D to get the desired result. I will move the last part of this discussion to a new thread when I have added a working example to the firmware.

    Anders

    #3740
    Author Image
    Devirex
    Participant

    Hi Anders,

    thanks a lot!

    #3745
    Author Image
    Anders Søborg
    Keymaster

    No problem

Viewing 14 posts - 1 through 14 (of 14 total)

You must be logged in to reply to this topic.

Posted in

Make a donation