cancel
Showing results for 
Search instead for 
Did you mean: 

BUGREPORT MCSDK 5.4.3 (and temporary fix)

chaaalyy
Senior II

Hi,

since a couple of days i tried to get the STEVAL-ROBOT-1 up and running properly. But as i mentioned here:

https://community.st.com/s/question/0D53W000003yGcdSAE/to-choice-a-kit-for-pmsm

yesterday, i got more problems, than i would have expected from libraries, provided by a "mature" company like ST.

Ok, giving up is not an option... So meanwhile i figured out, what´s wrong (After around 30 hours of search...). But step by step:

After many problems, importing the (actual) STSW-ROBOT-1 into cubeIDE and getting it to be built, i faced problems with unreliable position control, unexpected behaviour with torque regulation, unexpected moving motor and so on. Until yesterday i thought, it´s maybe just an error in some overflow calculation, but THIS is even worse:

In the file trajectory_ctrl.c , located in the folder \MCSDK_v5.4.3\MotorControl\MCSDK\MCLib\Any\Src , Lines 341 - 354 , the function void TC_EncoderReset(PosCtrl_Handle_t *pHandle) is the problem:

This function is called within the EXTI0_1_IRQHandler (void) , located in stm32f0xx_mc_it.c , EVERY time, the index pin of the provided encoder is passed by. So far, so good...

But why the hell, also EVERY time within these functions, ENC_SetMecAngle(pHandle->pENC , pHandle->MecAngleOffset) is called ??? Of course, the irq handler needs some time (not much, but anyway...) to run through. And in THIS time, of course the motor runs a few steps further. Now you take the formerly measured counter values and reset the counter to a PREVIOUS captured value ??? (Sorry, i´m just a little bit upset about so much stupidity...)

Of course this leads to absolutely undefined behaviour of the motor, up to loosing synchronisation. If you run the motor repeatedly just full revolutions (360 degree), after a while you can see, it´s definitely not "on track" anymore.

The solution is simple: placing the ENC_SetMecAngle - function within the if / else brackets. So it´s reliably called, when needed to align the motor with the encoder and afterwards, it´s just not called anymore. I attached the correted file here.

This affects ALL projects, with EVERY hardware ! Not just the STEVAL-ROBOT-1-Kit !

Ok, the next problem: As i stated in the thread, i linked above, also the motor settings and board parameters are more than just lousy. Resistance and inductivity not set correctly, Amperes just crippled, and so on, and so on ...

I made a quick fix (not perfect, still some tuning needed, but these settings work FAR better, than the original ones from ST) I attached two files (drive_parameters.h and pmsm_motor_parameters.h) with the settings, just replace them in the folder \Inc (not visible in cubeIDE project explorer, so open the folder directly in your workspace).

Last, but not least: For everybody, who doesn´t believe in these errors, provided by ST and also has no time to set up a modbus connection, i made a quick and dirty "check" run in main.c. Two possibilities, comment out the one, you don´t need. Begin with the 360 degree steps to see the behaviour without the corrected trajectory, then make the corrections and let it run for an hour or so ... NO position errors and NO unexpected behaviour or even movements anymore. For the 400 revs - routine, just running forward and backward for 10 secs each: PID settings need some more fine tuning, so it "overruns" a little bit, before PID corrects the position 😉

Edit: For everybody, who is wondering about that "encCounterValue = TIM3->CNT;" in main.c ... Add it to your live expressions while debugging to check the "stop" point after each movement... Should be within +/- 3 encoder steps, even after 100000 x moving 360 degrees forward.....

20 REPLIES 20
chaaalyy
Senior II

Hello @claire OKU​ , hello @Community member​ 

I´ll try to do my best xD Ok, 1st the promised screenshot, while debugging and searching...
_legacyfs_online_stmicro_images_0693W000000XEOS.png

As you can see, it stopped at breakpoint in line 170 of trajectory_ctrl.c. In lines 160 and 167, the struct members Omega and OmegaPrev are equally set to the value of the previous calculated (and never altered afterwards) omega variable and therefore should be equal. Also Theta and ThetaPrev in lines 159 and 168 ... Theroretically set to the given function parameter "Angle", which also isn´t altered inside the function. Of course the calculation of "omega" and "acceleration" in lines 150 and 155 rely on ThetaPrev and OmegaPrev ...

Problem here: As far, as i understand the reading of the pHandle struct at the moment, the program stopped, something definitely has happened to the members, while execution.

Correct me, if i´m wrong, but my 1st thoughts went to some other functions, which are able to alter these values, when they are called within an interrupt somewhere between setting them originally and the stop at the breakpoint. It´s very suspicious, that this doesn´t happen all the time. It needs some luck to "catch" that.

At the moment, i took the screenshot, all the original files have been on their original state (exept a) main.c, where i set the target angle and call MC_ProgramPositionCommandMotor1 for the movement and HAL_Delay(1) afterwards , b) the three altered files from the original bugreport) The routines in main are:

void setStepDirPosition()
{
	uint16_t timCounterTemp = TIM2->CNT;
	STP_PULSES= timCounterTemp;
	if (STP_PULSES != STP_LAST_VALUE)
	{
		STP_PULSES &= 0xC000;
		STP_LAST_VALUE &= 0xC000;
		if (STP_PULSES == 0 && STP_LAST_VALUE == 0xC000){
			STP_CNT_OF++;
		}
		if (STP_PULSES == 0xC000 && STP_LAST_VALUE == 0){
			STP_CNT_OF--;
		}
	}
 
	if (STP_CNT_OF < 0)
		stepPosition = ((STP_CNT_OF + 1) * (0xFFFF +1)) - ((0xFFFF + 1) - timCounterTemp);
	else
		stepPosition = (STP_CNT_OF * (0xFFFF +1)) + timCounterTemp;
 
	STP_LAST_VALUE = timCounterTemp;
}
 
//End of mainloop:
    setStepDirPosition();
    targetDeg = ((float)stepPosition /1024 *360); 
    targetDeg *= 12; // ~around 3200 rpm @ given external step frequency
    fTargetPos = ((float)(targetDeg) * (float)(M_PI)) / 180.0f;
    MC_ProgramPositionCommandMotor1(fTargetPos, 0);
    HAL_Delay(1);

And here the problems "began" xD :

As i understand from documentation, this should work. But anyway the motor did not really move like expected. Of course it was following the given position... Just the movement was very "rough" and even worse:

When it ran for a while (like in the video from the post above, just with faster rpm), at the end (motorposition 44800 on that touchscreen, i use to produce the stepsignals) it should stop for a moment (there´s also a deceleration ramp in the generated step signals) and then move on. At low rpm, this works nice (exept the "roughness"), but at high rpm, at this moment, MC_GetOccurredFaultsMotor1 didn´t return 0 anymore and the motor was switched off...

After playing with PID settings, i quickly found, that these settings didn´t work, like expected ... And of course, i took a look into pid_regulator.c also... Well... Let´s say, i have been "a little bit shocked 😉 ", when i saw, how you handle the Kd-Parameter:

wDeltaError = wProcessVarError - pHandle->wPrevProcessVarError;
    wDifferential_Term = pHandle->hKdGain * wDeltaError;
 
    wDifferential_Term >>= pHandle->hKdDivisorPOW2;
 
    pHandle->wPrevProcessVarError = wProcessVarError;
 
    wTemp_output = PI_Controller( pHandle, wProcessVarError ) + wDifferential_Term;

Hmmmm..... Ok ....... Not really, what i would expect from a differential calculation 😉

Ok, digging around more and more (and very late at sunday night...) i gave up at some point...

My last try was:

Ok, TC_PositionRegulation is called indirectly from sysTick isr...

So without changing the target angle, it works.

If i try to move the motor by hand, it regulates back to the position, where it should be.

everything needed for that is:  hTorqueRef_Pos = PID_Controller(pHandle->PIDPosRegulator, wError);, where wError is derived from pHandle->Theta and applying that...

So why not just updating pHandle->Theta without any further calculations or "workarounds" and let´s do the PID (more or less) it´s job....

What shall i say: This works, as intended, but anyway: The PID results still lead to the same problem: A BEMF spike, when it should decelerate and therefore a shutdown of the motor 

I´m really sorry, that i´m not so experienced in that... And also english isn´t my 1st language (i´m german...). I have just seen: Something definitely goes wrong here xD For example: Just debug pid_controller.c, move around with the motor a bit and watch pHandle->wProcessVarError... Even with a given input error of near 0 it stays at very high values and just get´s balanced by wPrevProcessVarError 😉 So also try Kd at zero... You´ll experience some surprises 😉

Edit: Nearly forgotten: These "if (pHandle->ReceivedTh > ***)"-statements in lines 144, 154 and then 161... After two calls of the function, they are senseless... And also not necessary at all 😉 For "ReceivedTh", in the whole library, these are the only times, where it´s used. So i think the whole thing could be deleted...

​Due to restriction of Covid-19 I can't access in this moment to the lab, but I started to analyze your problem at least on the paper.

I see that you are using the follow mode since you set 0 as duration of the MC_ProgramPositionCommand and you are sendind command at frequency of 1ms that is more or less the same frequency of the Medium Frequency task (basically the freqeuncy of the execution of the position PID) so basically the follow mode of the trajectory is not useful since this trajectory positions are the interpolation of the previous set point to reconstruct the point in between.

Lets suppose that you supervisor (the one that calculate the trajectory) send you command each 10ms and the position PID is executed at 1m the trajectory controller in follow mode is used to estimate the ten points that should be in the middle to give it as reference to the position PID.

If the supervisor give the target position at the same frequency of the position PID there is no need to estimate the intermediate positions.

Another point is that I don't know which are the target points that your code gives to the MC_ProgramPositionCommand, basically because I don't know how the TIM2 is configured and which signals are given in input to this timer. Can you explain the logic behing the computation of the stepPosition and so the fTargetPos or can you caclulate this value by FW so I can verify which values are passed to the MC_ProgramPositionCommand?

Thank in advance 

Hi @Community member​ 

No problem 🙂 In one of my posts from above (the one with the picture of the robot-1 board and the soldered cables) i attached a video. In this vid you can see my basic setup. Basically it´s a F746 Disco, sending step/dir signals. On the screen, you can see the sent "position", where every digit represents one step pulse (just to give you an idea of the frequency). At side of the robot-board, i just set up Tim2, connected to PA15 and receive these pulses into TIM2->CNT. In the setStepDirPosition-function from above, i check the counter value (every loop), check for overflows (counter is set up with ARR of 0xFFFF) and pass that to the "stepPosition"-variable without further editing. So it represents the "Absolute" position, received by step signals. On PB6 i connected the dir signal, which just triggers an interrupt at rising and falling edge. In isr i just change the counter direction of TIM2.

I already checked the "Receiver" behaviour in debugger and this works like a charm 🙂 stepPosition variable always represents the same value like the "motorposition" from the f746 screen 🙂

Then further on in that code snippet, i calculate targetDeg unfortunately in two lines (this is a left over from debugging and testing...) 1st line i set 1 received step to represent 1 degree, in 2nd line i multiply it to manipulate the output speed (so i didn´t have to recalculate everything or even to change my "step generator" , when i just want to do a quick compare between different speeds) Just changing the 12 to 4, 2, 8, 5 or what else is enough 😉

Calculation of fTargetPos is the translation to the format, used by mc library then ...

In the video, 1 step represented 1:1 one degree. That was the 1st test.

I also tried different HAL_Delay values, because TIM2->CNT is always up to date and doesn´t need any loop or cpu time Exept a quick isr, when direction changes...) . It´s automatically triggered and independent. With a delay of 10ms, the motor showed the "smoothest" behaviour, but anyway at the end of the movement (when "44800 steps" where reached), pid values have been "strange" and the board stopped the motor (while code continued running...)

For covid-19: I hope, you and everybody else of the team is well !

I hope to find some time on friday to prepare a complete project with artificial generation of the targetDeg value (as i did in the beginning of testing...) This will make things a lot easier to explain problems / errors / behaviour and so on, if i can share it and we all have the same codebase and no special signal generation is needed 🙂

Hi again @Community member​ ,

Just took a quick look after your explanation about TC_FollowCommand (the disassembly of the given "way to move" into "steps per tick". I couldn´t debug that so far, but as i complained about that "senseless" pHandle->ReceivedTh, just a few "quick thoughts" about that. I´m not an engineer, but maybe (just maybe 😉 ) i found the "original plan behind it". For a moment just forget about the misleading name "ReceivedTh" and the fact, it´s a uint8_t, which can´t hold large values in the following snippet. As i said: Not tested, built or debugged. It´s just to show my idea, how this stuff at least could make sense 😉

void TC_FollowCommand(PosCtrl_Handle_t *pHandle, float Angle)
{
  float omega = 0, acceleration = 0, dt = 0;
  pHandle->ReceivedTh = pHandle->TcTick; // making something useful of ReceivedTh
  
  // Estimate speed
  if (pHandle->ReceivedTh > 0) 
  {
    // Calculate dt
    dt = pHandle->TcTick * pHandle->SysTickPeriod;
    pHandle->TcTick = 0;
  
    omega = (Angle - pHandle->ThetaPrev) / dt;
  }
  
  // Estimated acceleration
  if (pHandle->ReceivedTh > 1) {
    acceleration = (omega - pHandle->OmegaPrev) / dt;
  }
  
//deleted pHandle->ReceivedTh++ in if statement
 
  // Update state variable
  pHandle->ThetaPrev  = Angle;
  pHandle->OmegaPrev = omega;
 
  pHandle->Theta = Angle;
  pHandle->Omega = omega;
  
  pHandle->Acceleration = acceleration;
  
  pHandle->PositionCtrlStatus = TC_FOLLOWING_ON_GOING;   /* follow mode has been programmed */
  
  return;
}

Now i just pray, you can imagine, what i mean with that xD

Of course this has nothing to do with pid, but every journey starts with a first step xD

Now i continue thinking about that... For example: why TcTick isn´t used as divisor and therefore an abstracted value is used? TcTick*SysTickPeriod ?? As far, as i understand, every Systick that Tick is raised by one... So what we need is the amount of "Systick loops" to divide given movements.... Hmmmmmmmm.... Also the resulting speed (physically, of the motor...) will be a result of torque applied (and that should be the result of pid...)

So (as i think) the only really needed value here is the calculation of omega, to divide a "larger" movement into "parts", given to the pid every Tick (and of course it´s necessary to adjust TC_FollowExecution as well 😉 )

/Charly

chaaalyy
Senior II

I broke it down a bit further... Maybe this explains my thoughts better:

void TC_FollowCommand(PosCtrl_Handle_t *pHandle, float Angle)
{
  float omega = 0;
  
  if (pHandle->TcTick > 0) //to prevent division by zero
  {
    omega = (Angle - pHandle->ThetaPrev) / pHandle->TcTick;
    pHandle->TcTick = 0;
  }
else
{
   omega= (Angle - pHandle->ThetaPrev);
}
  
  pHandle->ThetaPrev  = Angle;
 
  // pHandle->Theta = Angle; //further handled in TC_FollowExecution
  pHandle->Omega = omega;
 
  pHandle->PositionCtrlStatus = TC_FOLLOWING_ON_GOING;   /* follow mode has been programmed */
  
  return;
}
 
 
void TC_FollowExecution(PosCtrl_Handle_t *pHandle)
{
 pHandle->Theta += pHandle->Omega;
}

 Edit: of course this is missing the final check, if target angle is reached.... Simplyfied that could look like Command: ReceivedTh = Angle and in Execution: if ((Theta-ReceivedTh) < Omega) ...

Gigi
ST Employee

​Dear @Karl Hönemann​ 

Let me explain the reason of the code starting from the original version

Let's take the function TC_FollowCommand. This function is intended to be used in this way: the user at fixed time send the target angle executing a strategy of acceleration/deceleration that is not know by the FW. This function (TC_FollowCommand) is responsible to understand that strategy and to propose to the position controller PID a set of target angles that tries to inerpolate the angle between the receved angles and that can be several intermediate steps.

Let suppose that the master is sending angles that came out from a constant speed. If this funcion is able to understand what is this speed, to calculate the intermediate angle is simple, you have to increment the angle with a constant value that is proportional with the speed and you have the correct interpolated values.

The problem is that tipically in application that requres positioning there is a lot of change in the speed. Basically to move from one point to another point starting from stand still and finish with stand still is necessary first a phase of acceleration, a middle step of constant speed and a final deceleration up to zero speed.

If this strategy is implemented by the master (who defines the positions) the function TC_FollowCommand must identify, not only a constant speed, but also if there is some acceleration, that means variation of speed.

How the function detects speed or acceleration only from the angle that is passed?

The strategy is this one:

  • Speed (omega) is estimated considering the derivative of the angle, so (Angle - PreviousAngle )/(delta time)
  • Acceletarion is estimated considering the derivative of the speed in the same way

But to do this the functon requires at least two values of angle to estimate the speed and three values of angle to estimate the acceleration.

An for this purpose the famous RecivedTh variable (member of the struc) is used.

Initially is zero that means no angle information has been received ->  the function can't estimate nothing

After the first received angle the variable is 0 because it will be incremented at the end of the function -> Angle is used as target but the speed can't be estimated

When recive the second point value is 1 -> we receive two angle from now on is possible to compute the speed but no the Acceleration

Finally when we receive the third point the value is 2 -> From now on is possible to estimate both the speed and the acceleration

For this reason the RecivedTh is no more incremented

Once estimated, speed and acceleration are used to integrate respectively the angle and the speed in TC_FollowExecution

If this is now more clear please review you modifications in this optic.

I agree with you that the case of dt = 0 is not managed and this can generate a "very bad" estimation of speed or acceleration and this can occur if the function TC_FollowCommand is executed before the execution of TC_IncTick. If you call to fast the function TC_FollowCommand  this could happen.

The most simple solution to this problem should be to skip the estimation in this case. For instance:

...

if (dt > 0) {

        omega = (Angle - pHandle->ThetaPrev) / dt;

}

...

if (dt > 0) {

       acceleration = (omega - pHandle->OmegaPrev) / dt;

}

Because the defualt value of omega and acceleration are both zero. Maybe is also the case to reset the ReceivedTh to zero because the expected sequence of estimation is not occurred.

Ciao

Gigi

...

chaaalyy
Senior II

Now i understand the intention behind that 🙂

Then i have to say, these estimations are a masterpiece 🙂 That´s, why i got some (just in my eyes) "strange" behaviour:

I tried the following scenario: (everything with moveDuration zero, so -> Follow Mode). I added 360 degree as target angle and delayed for 5 seconds (HAL_Delay(5000)). !st movement: one full rev. and stop 2nd time one full rev and continuing to turn slowly. 3rd movement: completing the already running revolution and continuing nearly seamless to turn smoothly 🙂

So it estimated correctly, the next "order" will be the same and moved in advance of my commands. It has been my mistake to assume the followMode is enabling the possibility to send different positions / movements at given intervals. This couldn´t work because a different command immediately will collide with the previous values. This also explains why PID has to work completely different than assumed and expected. I´m used to having the D-Part as … let´s say "lookahead". Of course this can´t work, when trajectory already had to do that. Thanks a lot for these informations 🙂

Gigi
ST Employee

​Thanks to you, I'm happy to see that my explanation has  been useful.

If you get other problems don't hesitate to contact us.

Ciao

Gigi

Claire O.
ST Employee

Dears,

MCSDK5.4.4 is now available in st.com with the bug fix.

Best regards,

Claire

Thank you 🙂

Will have a look, when there´s some time 🙂

Anything else changed from .3 to .4 ?