Shop OBEX P1 Docs P2 Docs Learn Events
CNC - I May Not Be Crazy After All.... Multi-Axis Stage Synchronization - Page 3 — Parallax Forums

CNC - I May Not Be Crazy After All.... Multi-Axis Stage Synchronization

13»

Comments

  • Still on my phone so the easy one first.

    (Pseudo code)

    Increment the counter anyway

    IF any conditions are true, leave the counter as it is.

    ELSE
    Decrement the counter.
  • Mickster
    IF any conditions are true, leave the counter as it is.

    In order to determine that condition, you would need a flag in each conditional statement that your conditional statement is based upon. So instead of creating an additional flag variable, for which you must keep track, and another conditional statement of code to check the flag, why not just increase the counter if a condition is met?
  • The conditions are already there; gcode_struct_element being within 0 to 9.
  • For my next major step, I must try to eliminate the floating point bug within the main and follower cogs. Needless to say, this will require some delicate surgery.

    I will be attempting to resolve this issue according to a discussion within the Mothra thread, although with a smaller scaler, which was as follows:
    Okay Jason...

    Going back to what you said earlier and how I responded...

    I posted this snippet:
    current_move.follower_1_ratio = (double)current_move.master_total_steps / 
    	(double)current_gcode.y_total_steps;
    
    current_move.follower_1_int_part = (uint32_t)current_move.follower_1_ratio;
    
    current_move.follower_1_dec_part = current_move.follower_1_ratio - 
    	current_move.follower_1_int_part;
    
    All three of the structure members being initialized above, are all currently double data types. I suppose I could eliminate current_move.follower_1_ratio completely, and I could change current_move.follower_1_int_part and current_move.follower_1_dec_part to 32 bit unsigned integers. Then I am thinking that I could replace the code above, with something similar to this:
    current_move.follower_1_int_part = current_move.master_total_steps / 
    	current_gcode.y_total_steps;
    
    current_move.follower_1_dec_part = (current_move.master_total_steps % 
    	current_gcode.y_total_steps) * 10000;
    
    and then, going in line with what you said, in the problem area:
    follower_1_offset = follower_1_start_stop_speed * follower_1_dec_part  / 10000;
    
    Does that sound and look right?
  • Mickster wrote: »
    The conditions are already there; gcode_struct_element being within 0 to 9.

    Oh wait. Here I am assuming that, along with IF and ELSEIF, you have a default ELSE?

    If not, you could simply test for gcode_struct_element being within the 0-9 range to increment the counter.
  • idbruceidbruce Posts: 6,197
    edited 2017-06-30 14:25
    Mickster
    If not, you could simply test for gcode_struct_element being within the 0-9 range to increment the counter.

    Now that sounds more than reasonable and a very good point.

    However, keep in mind that this is just a test container, to prove whether or not 3 follower axes with different ratios can synchronize to a master axis. If they can, then we should optimize the application that this concept will go into, not this one.

    I noticed a problem the other day, but my mother just went into the hospital, so I have not had time to wrap my head around it yet. Perhaps you might be willing to help.

    As you know, this code is based upon master/follower relationships with ratios being used to establish timing. Within the test container, there is support for three followers, and each of the followers have three specific loops, which are for ramp up, maintain speed, and ramp down, so that is a total of nine of these specific loops. Anyhow, these loops all have this similar conditional statement in common, and it is definitely the wrong train of thought.
    if(follower_1_step_counter % (uint32_t)follower_1_int_part == 0)
    

    This condition determines if the follower should make a step, based upon a comparison ratio to the masters position in timing. I need a condition that will work correctly. I am sure that I could wrap my head around it, but at the moment, with mom in the sick bay, I will have less time than the short time that I had, to devote to this.

    Please take a look and let me know what you think.
  • Any particular reason to not stick with integers? Multiply up, perform ratio calcs, divide down?
  • That is what I will be doing next, changing that code over to integers, to remove a bug that currently exists in the follower code.

    My plan for this is four posts back within this thread.

    It really isn't the data types that I am concerned with, but more so the concept, to achieve proper follower step time. I do know that the integer part and the fractional part will both come into play, when determining the proper step time, but what I currently have is not the solution for this step timing because the number of steps made do not match the ratio.

    If I remember correctly, it makes too many steps, because the condition returns true too often.
  • Kinda my thinking. I had one that drove me nuts because any non-zero = true. My oversight resulted in the logic looking for a fraction of an encoder count....DOH!

    LOL!
  • idbruceidbruce Posts: 6,197
    edited 2017-06-30 22:48
    Okay, I took a moment to think about this conditional statement.

    I now believe that....
    if(follower_1_step_counter % (uint32_t)follower_1_int_part == 0)
    

    should be this instead, for the previously mentioned nine loops:
    if((follower_1_step_counter + 1) % follower_1_int_part == 0)
    

    But.... I could be wrong :)


    EDIT: This isn't necessarily wrong, but I believe it would only apply to ratios that have a fractional part. If the ratio doesn't have a fractional part, then the top conditional statement would apply, with timing equivalent to the masters timing.

    EDIT: Nope... It's wrong. Be back in a sec.

    Okay, how about this?
    if(follower_1_step_counter % (follower_1_int_part + 1) == 0)
    

    I think that is it. I think.

    EDIT: Nope... the upper alteration is the correct one.

    EDIT: LOL... Wrong again... I believe this is it...
    if((follower_1_step_counter - 1) % follower_1_int_part == 0)
    
  • Since I am here, I suppose it is worth mentioning, that I currently have no implementation available for axes which have equal movement plans, such as drawing or machining a 45 degree angle, but that can be achieved later.
  • idbruceidbruce Posts: 6,197
    edited 2017-07-01 11:58
    Referring back several posts, you will notice the inclusion of a fairly large quote from another thread, in which I was discussing the removal of a floating point bug. In addition to other things, I am currently in the process of attempting to remove this bug, by converting several variables from double data types to 32 bit unsigned integers.

    In an attempt to make a long story short, I have no idea how many digits will be returned by this function....
    current_move.follower_1_dec_part = current_move.master_total_steps % 
    	current_gcode.y_total_steps;
    

    but since I am converting the fractional part of a double into an unsigned integer, for computational purposes, I need to ensure that the newly formed integer, will always have the same length. I also need to ensure that the value will be reasonably accurate for calculating my timing computations, and I need to accomplish this task, without the support of floating point functions. In addition to that, I must keep in mind a warning that Jason provided in the Mothra thread:
    scaling it up like that is going to blow over the 32 bit number limit relatively easily

    So my thoughts are to format the decimal part of the ratio, with this function, within Gearing.c, before placing it into the MOVE_STRUCT, and then within the follower functions, I will divide by 10,000:
    uint32_t format_dec_part(uint32_t decimal_part)
    {
    	if(decimal_part < 10)
    	{
    		return decimal_part * 1000; // 4 digits
    	}
    	else if(decimal_part < 100)
    	{
    		return decimal_part * 100; // 4 digits
    	}
    	else if(decimal_part < 1000)
    	{
    		return decimal_part * 10; // 4 digits
    	}
    	else if(decimal_part => 1000 && decimal_part =< 9999)
    	{
    		return decimal_part; // 4 digits
    	}
    	else
    	{
    		while(decimal_part > 9999)
    		{
    			decimal_part = decimal_part / 10;
    		}
    	}
    
    	return decimal_part; // 4 digits
    }
    

    EDIT: With all the necessary preparations now made within move_struct.h, Gearing.h, and Gearing, the next natural step would be to replace the following code:
    current_move.follower_1_ratio = (double)current_move.master_total_steps / 
    	(double)current_gcode.y_total_steps;
    
    current_move.follower_1_int_part = (uint32_t)current_move.follower_1_ratio;
    
    current_move.follower_1_dec_part = current_move.follower_1_ratio - 
    	current_move.follower_1_int_part;
    

    with this code:
    current_move.follower_1_int_part = 
    	current_move.master_total_steps / current_gcode.y_total_steps;
    
    current_move.follower_1_dec_part = 
    	format_dec_part(current_move.master_total_steps % current_gcode.y_total_steps);
    

    EDIT: However going back to something Jason said in the Mothra thread:
    If you do your scaling in powers of two, the resulting code will run an order of magnitude faster - Prop doesn't do multiply or divide natively, so if you have something that needs to be scaled in an inner loop, use a scale of 64 or 128 instead of 100. It's a marginal change in precision, but a massive change in performance, because now it can be done with a shift instead of calling a divide function.

    If the proceeding changes prove to be too slow for the timing loops within the follower cogs, then the proceeding changes will have to be modified slightly, to incorporate Janson's suggestion above.

    EDIT: At this point, the resulting ratios are incorrect, so I will have to run an isolated test to determine what's going wrong.
  • Mickster

    I never really used the modulus operator, until I started programming for uCs. Now I find it indispensable

    I guess it is fair to say that I have learned a lot since knocking on Parallax's front door.

    Well now, I have changed my mind about the modulus operator and I will have to check all of my important code that uses this operator, because it certainly doesn't work the way I thought it did.
  • Ah, I missed the Mothra thread but what Jason and Heater have stated is not just some workaround for lack of FP support, it's also what the high-end motion controllers do.

    SHL + SHR = Magic!
  • Mickster

    I am sure that you, Jason, and Heater are all correct. However, as it stands, my time is very limited, so I simply do not have the time to alter a bunch of code. About the very best that I can do, is work around the code I already have, just to test my experiment, and it is already proving to take more time then I anticipated.

    As it stands now, because of my lack of understanding, of the modulus operator, I am having to create several work arounds, just to be able to test. Just another problem that I did not foresee.
  • As I see it... Looking back.... The use of doubles certainly made some portions of it much easier.... BUT... It also made some portions much tougher.
  • idbruce wrote: »

    As it stands now, because of my lack of understanding, of the modulus operator, I am having to create several work arounds, just to be able to test. Just another problem that I did not foresee.

    Just going off memory of gearing.c, I got the impression that you're calculating a number of steps but then using the ℅ would give you the remainder rather than the required quotient(??)

    I probably misunderstood but something didn't feel right.

  • I was expecting a remainder, but received something else :)

    I was using it for calculating running steps (max speed) , as well as the fractional portion of the gear ratio.

    There may be more uses of it, but that is what I know so far. I already repaired the running steps, and I just finished creating the work around for the fractional portion. However, the work around for the fractional portion may be unnecessary and I may just need to cast it, which is what it looks like.
  • 13 / 5 = 2.6

    13 % 5 = 3 (two fives in thirteen and a remainder of three)
  • Ahhhh..... but those are whole numbers, Try it with a double result

    16933 / 579 = 29.24525043177893
    16933 % 579 will not result in 0.24525043177893

    That was part of my misconception
  • Moduleo is an integer only operation. If you want a float remainder you need to use frac or fmod, neither of which are cheap. :) It's also worth mentioning that the sign of the result depends on the signs of the inputs, so it's usually easier to do a sign test of your input, ABS() it, then use modulo.
  • idbruceidbruce Posts: 6,197
    edited 2017-07-02 11:45
    WOW.... Great tip Jason. I am not sure, but perhaps that will make this test easier and my life a little easier, and I definitely need easier at this point. I have been back and forth to the hospital, about twenty times in the last three days, and averaging about four to five hours of sleep per day. They finally determined that my mother had a stroke, and she kept getting worse, but has now stabilized. So life is really hectic and sorrowful at the moment.
  • idbruceidbruce Posts: 6,197
    edited 2017-07-02 12:17
    Moduleo is an integer only operation. If you want a float remainder you need to use frac or fmod, neither of which are cheap. It's also worth mentioning that the sign of the result depends on the signs of the inputs, so it's usually easier to do a sign test of your input, ABS() it, then use modulo.

    Your post got me to thinking... And this is what those thoughts are...

    1. The primary cog, starts the master cog. From that point, the primary cog is simply responsible for parsing G-Code, monitoring and filling a MOVE_STRUCT queue, and shutting down the master cog when all the movements have been made.

    2. The master cog is responsible for starting and shutting down all the necessary follower cogs, initializing movement variables, which is not a lot to initialize.... then deletes the queue entry before each movement begins.

    Providing that I can achieve synchronization, I think this plan would work very well, depending upon the time that it takes to initialize the movement variables. If the initialization time is within reason and if the SD card support code could be shrunk, then I think it might be worth pursuing a one chip design again. Additionally, it would be necessary for the parsing and queue code to stay ahead of the movement code.
Sign In or Register to comment.