LinuxCNC S-Curve Accelerations

More
31 Jul 2021 23:02 #216522 by cmorley
looking at simple_tp.c in control.c ; it would be a bit of work to pull it out as a component. You think you would need to break the motion controller into reading input and writing output components.
In this way you could stick the the simple_tp component between them in the thread order and not be one servo thread behind.

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 00:49 #216553 by Grotius
Hi Chris,

I don't understand why you need the simple_tp? I see it exists as a component.
But in control.c i see not quite what you mean. Maybe i miss something?

I found out that a higher level of components that are using c++ libraries. They are ideal for complex and fast tasks.
Trough the component design model, the code is quite encapsulated.

In this way you could stick the the simple_tp component between them
Indeed. Without compiling you can put something between it.

You think you would need to break the motion controller into reading input and writing output components.
I think many users will think different from my current work.
For me it is clear to leave the original linuxcnc source code as it is. This code works nice, don't change too much.
Complex improvements, like changing entire motionplanners can be done outside the source code.

Making a gcode reader in component style is quite easy. You can use the gpr c++ lib wich works fine.
When the component is compiled with a shared lib, it can show a gui gcode textbox as stand alone or as embedded in another gui. As i showed before. Using the opencascade lib, kdl kinematics, etc in component style is piece of cake.

I see much more potential and future succes in this type of modulair encapsulated higher level coding.
With this type, you can also load multiple motion planners or gcode readers together, the cascades.








 

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 01:24 #216560 by arvidb

looking at simple_tp.c in control.c ; it would be a bit of work to pull it out as a component.

Indeed. The below free_tp is just one of three instances of simple_tp:
$ find src/emc/motion/ -type f | xargs grep free_tp
src/emc/motion/mot_priv.h:    hal_bit_t *free_tp_enable;	/* RPI: free traj planner is running */
src/emc/motion/control.c:            if(joint->free_tp.enable == 1) {
src/emc/motion/control.c:                joint->free_tp.enable = 0;
src/emc/motion/control.c:                // since homing uses free_tp, this protection of aborted
src/emc/motion/control.c:	    joint->free_tp.enable = 0;
src/emc/motion/control.c:	    joint->free_tp.curr_vel = 0.0;
src/emc/motion/control.c:	    joint->free_tp.curr_pos = joint->pos_cmd;
src/emc/motion/control.c:			joint->free_tp.curr_pos = joint->pos_cmd;
src/emc/motion/control.c:		    joint->free_tp.curr_pos = joint->pos_cmd;
src/emc/motion/control.c:		    joint->free_tp.enable = 0;
src/emc/motion/control.c:            joint->free_tp.enable = 0;
src/emc/motion/control.c:	pos = joint->free_tp.pos_cmd + distance;
src/emc/motion/control.c:        joint->free_tp.pos_cmd = pos;
src/emc/motion/control.c:        joint->free_tp.max_vel = joint->vel_limit;
src/emc/motion/control.c:        joint->free_tp.max_acc = jaccel_limit;
src/emc/motion/control.c:        joint->free_tp.enable = 1;
src/emc/motion/control.c:               if (vel_lim < joint->free_tp.max_vel)
src/emc/motion/control.c:                   joint->free_tp.max_vel = vel_lim;
src/emc/motion/control.c:                /* except if homing, when we set free_tp max vel in do_homing */
src/emc/motion/control.c:                joint->free_tp.max_acc = jaccel_limit;
src/emc/motion/control.c:                joint->free_tp.max_acc = joint->acc_limit;
src/emc/motion/control.c:            simple_tp_update(&(joint->free_tp), servo_period );
src/emc/motion/control.c:            joint->pos_cmd = joint->free_tp.curr_pos;
src/emc/motion/control.c:            joint->vel_cmd = joint->free_tp.curr_vel;
src/emc/motion/control.c:            joint->coarse_pos = joint->free_tp.curr_pos;
src/emc/motion/control.c:            if ( joint->free_tp.active ) {
src/emc/motion/control.c:            if(GET_JOINT_ACTIVE_FLAG(&(joints[i])) && joints[i].free_tp.active)
src/emc/motion/control.c:    emcmot_hal_data->debug_bit_0 = joints[1].free_tp.active;
src/emc/motion/control.c:	*(joint_data->free_pos_cmd) = joint->free_tp.pos_cmd;
src/emc/motion/control.c:	*(joint_data->free_vel_lim) = joint->free_tp.max_vel;
src/emc/motion/control.c:	*(joint_data->free_tp_enable) = joint->free_tp.enable;
src/emc/motion/homing.c:	joint->free_tp.pos_cmd = joint->pos_cmd + 2.0 * joint_range;
src/emc/motion/homing.c:	joint->free_tp.pos_cmd = joint->pos_cmd - 2.0 * joint_range;
src/emc/motion/homing.c:	joint->free_tp.max_vel = fabs(vel);
src/emc/motion/homing.c:	joint->free_tp.max_vel = joint->vel_limit;
src/emc/motion/homing.c:    joint->free_tp.enable = 1;
src/emc/motion/homing.c:    if (!joint->free_tp.active) {
src/emc/motion/homing.c:	joint->free_tp.enable = 0;
src/emc/motion/homing.c:	        joint->free_tp.enable = 0;
src/emc/motion/homing.c:		joint->free_tp.enable = 0;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:		    joint->free_tp.enable = 0;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:		    joint->free_tp.enable = 0;
src/emc/motion/homing.c:		joint->free_tp.curr_pos += offset;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:		    joint->free_tp.enable = 0;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:			joint->free_tp.enable = 0;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:			joint->free_tp.enable = 0;
src/emc/motion/homing.c:		joint->free_tp.curr_pos += offset;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:		joint->free_tp.curr_pos += offset;
src/emc/motion/homing.c:		    joint->free_tp.enable = 0;
src/emc/motion/homing.c:		joint->free_tp.curr_pos = joint->pos_fb;
src/emc/motion/homing.c:		   joint->free_tp.curr_pos += offset;
src/emc/motion/homing.c:		if (joint->free_tp.active) {
src/emc/motion/homing.c:                                   (jtmp->free_tp.active)
src/emc/motion/homing.c:		joint->free_tp.pos_cmd = H[joint_num].home;
src/emc/motion/homing.c:		    joint->free_tp.max_vel = fabs(H[joint_num].home_final_vel);
src/emc/motion/homing.c:		    if (joint->free_tp.max_vel > joint->vel_limit)
src/emc/motion/homing.c:			joint->free_tp.max_vel = joint->vel_limit;
src/emc/motion/homing.c:		    joint->free_tp.max_vel = joint->vel_limit;
src/emc/motion/homing.c:		joint->free_tp.enable = 1;
src/emc/motion/homing.c:		if (!joint->free_tp.active) {
src/emc/motion/homing.c:		    joint->free_tp.enable = 0;
src/emc/motion/homing.c:		joint->free_tp.enable = 0;
src/emc/motion/motion.c:        if (joint != 0) { joint->free_tp.enable = 0; }
src/emc/motion/motion.c:    if ((retval = hal_pin_bit_newf(HAL_OUT, &(addr->free_tp_enable), mot_comp_id, "joint.%d.free-tp-enable", num)) != 0) return retval;
src/emc/motion/command.c:		    joint->free_tp.enable = 0;
src/emc/motion/command.c:		if (joint != 0) joint->free_tp.enable = 0;
src/emc/motion/command.c:		    joint->free_tp.pos_cmd = joint->max_jog_limit;
src/emc/motion/command.c:		    joint->free_tp.pos_cmd = joint->min_jog_limit;
src/emc/motion/command.c:	        joint->free_tp.max_vel = fabs(emcmotCommand->vel);
src/emc/motion/command.c:	        joint->free_tp.max_acc = joint->acc_limit;
src/emc/motion/command.c:	        joint->free_tp.enable = 1;
src/emc/motion/command.c:                    if (joint != 0) { joint->free_tp.enable = 0; }
src/emc/motion/command.c:		    tmp1 = joint->free_tp.pos_cmd + emcmotCommand->offset;
src/emc/motion/command.c:		    tmp1 = joint->free_tp.pos_cmd - emcmotCommand->offset;
src/emc/motion/command.c:	        joint->free_tp.pos_cmd = tmp1;
src/emc/motion/command.c:	        joint->free_tp.max_vel = fabs(emcmotCommand->vel);
src/emc/motion/command.c:	        joint->free_tp.max_acc = joint->acc_limit;
src/emc/motion/command.c:	        joint->free_tp.enable = 1;
src/emc/motion/command.c:                    if (joint != 0) { joint->free_tp.enable = 0; }
src/emc/motion/command.c:                joint->free_tp.pos_cmd = emcmotCommand->offset;
src/emc/motion/command.c:                if (joint->free_tp.pos_cmd > joint->max_jog_limit) {
src/emc/motion/command.c:                    joint->free_tp.pos_cmd = joint->max_jog_limit;
src/emc/motion/command.c:                if (joint->free_tp.pos_cmd < joint->min_jog_limit) {
src/emc/motion/command.c:                    joint->free_tp.pos_cmd = joint->min_jog_limit;
src/emc/motion/command.c:                joint->free_tp.max_vel = fabs(emcmotCommand->vel);
src/emc/motion/command.c:                joint->free_tp.max_acc = joint->acc_limit;
src/emc/motion/command.c:                joint->free_tp.enable = 1;
src/emc/motion/command.c:                   if (joint != 0) { joint->free_tp.enable = 0; }
src/emc/motion/command.c:            joint->free_tp.enable = 0; /* abort movement (jog, etc) in progress */
src/emc/motion/motion.h:	simple_tp_t free_tp;	/* planner for free mode motion */
Every single place where internal fields of simple_tp is touched needs to be changed to method calls to get a clean interface to the jog planner. This needs to be done regardless of what implementation we want to replace it with, and regardless of where we put it.

That's what I did in the code that TheRoslyak tested. Unfortunately I managed to break homing in the process...

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 01:40 #216564 by cmorley
control.c calls the simple_tp.c function.
simple_tp.c and simple_tp.comp are two different entities.
simple_tp.c is the jogging trajectory planner.

I was trying to say if you want to have HAL plug-able jogging TPs then you would need to change control.c to be two components that get called in a HAL thread.

Right now simple_tp.c is embedded in control.c and so can not be a HAL component because control.c needs to give information and get the answer from simple_tp.c in the same thread cycle.

since thread components run one after another you would need something like:

control calculates end waypoint component in servo thread
simple_tp calculates target component in servo thread
control forwards calculated target to PID component in thread

If you didn't do that but still broke simple_tp out as a HAL component the target passed to control would always be one thread cycle behind.


Gcode 'reader' is a completely different piece of code.
That is the interpreter.
The interpreter is already pluggable and the rs274 interpeter is written in c++

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 01:44 #216567 by cmorley
One thing I missed is the motion controller is written in c because it is real time.
The interpreter is not realtime so has many more programming options.

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 01:55 #216569 by cmorley
Whether you used function calls or used the struct the control state machine needs to be broken up, so the calculations are done in time.

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 03:05 #216578 by arvidb

Right now simple_tp.c is embedded in control.c *snip*

To be precise, simple_tp.c is embedded in motmod. It is used not just by control.c, but as shown above most of the files in src/emc/motion/ touch the state of the three jog planners - free_tp, teleop_tp, and ext_offset_tp - which are all instantiations of simple_tp. But yeah, motmod would have to be broken up, and simple_tp "untangled" from it, if one wants to have a free-standing HAL component that did jog planning.

So, Grotius, that "untangling" needs to be done whether you want to replace the simple_tp in motmod or split motmod up to have a modular jogging planner. A modular planner doesn't make things easier.

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 03:28 #216581 by cmorley
Ah yes - I didn't scroll your list :)
that's alot of entanglement :(

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 09:33 #216588 by automata
Hi,

The way to analyz3 and limit the jerk is to break it into two orthogonal parts.
1. Tangential jerk - along the path
2. Normal jerk - normal to the path.

Tangential jerk can be limited by modifying the trajectory while normal jerk would require modification of the path.
As you pointed out transition from a line to arc or vice versa would give rise to infinite jerk. But this would be normal to the instantaneous motion detection.
Method to limit normal jerk would be to use clothoids or splines or beziers to model the blending path.
Ruckig or similar techniques can be used to limit tangential jerk by modifying the position vs time i.e., Trajectory
- automata

Please Log in or Create an account to join the conversation.

More
01 Aug 2021 13:40 #216601 by automata

Hi,

It's in general a good idea to test the s-curve implementation from within a component. It will keep it simple. But then
you also need a component style gcode reader, including function's like "start from line" etc.

My current strategy after more coding today :
- Read the gcode file into a vector of paths. A path is a motion from M3 to M5. Or a rapid G0.
A path has a total motion lenght.

- The Ruckig lib will perform the motion of each path. Today i did this succesfully in a 1ms live mode.
Then during execution of the motion, parameters like acc_max, vel_max, jerk_max can be modified during the motion!
This is realy great! Also moving backwards, motion reverse is no problem.

- In the above stage's no real x,y,z interpolation is done so far. Just one DoFs is used for a lineair path planning.
Imagine arc lenght's and line lenght's combined as a total path lenght.

As a result of the above implemenation :

The tcp x,y,z and tcp euler x,y,z interpolation can be done from the position of the one DoFs.
With this result the kinematic model can be driven.
The kinematic model, for example a 6 axis robot can then use a 6 DoFs system that control's the kinematic model motion.


 

Hi Grotius,
Excellent strategy to make a separate motion hal module for testing and proveout.
I am also working on simillar lines to making a multiaxes motion planner and playout module.
From my point of view, LinuxCNC does the entire motion planning in the realtime context (same as ruckig as it is an online planner).
However, the main task entrusted to linuxcnc is playout of pre-known gcode. This implies that planning is inherently a non-realtime activity.
I am side stepping the feed override issue though and do have a good solution for that too with my strategy (one that has been commercially deployed in more than a few thousands of machines worldwide from my previous job).
The motion plan of a path can be represented as a queue of third order (for jerk limited planning) polynomials. The complete plan can be optimized offline (i.e. in the non-rt part of the program i.e. in milltask in LinuxCNC) and the result will be a series of polynomials.
P_1 = A_1t^3 + B_1t^2 + C_1t + D_1, valid from 0-tf_1 with startpoint of the axes as Xs_1, Ys_1,Zs_1,As_1,Bs_1,Cs_1,Us_1,Vs_1,Ws_1 and end point as Xf_1,Yf_1,Zf_1,Af_1,Bf_1,Cf_1,Uf_1,Vf_1,Wf_1. here P_1 represents the distance travelled along the path represented by the polynomial. Simillrly an arc representation can also be made with P_n representing either the distance moved on the arc OR even the angle advanced along the arc movement as it is monotonic increasing. Only constraint on P_n has to be is that it is monotonic and can be scaled between 0-1.

The motion or the realtime module does only a playout of the polynomials with a known time advance parameter which is the servo-thread time i.e. 1ms in most installations although it can be different. The polynomial is sampled at servo-thread time and new values of P_n are computed and using the Xs_n and Xf_n the value of the X coordinate can be derived.

The feed override can be handled by changing the time-advance parameter. So in order to slow down from the planned rate, the polynomial is sampled at a rate lower than 1ms (servo time) and vice versa for faster movement.

Now the trajectory planning i.e. computation of the polynomials can be done with a standard trapezoidal planner. Tangential Jerk limitation can be added by replacing the acceleration and deceleration polynomials with 2 or 3 3rd order polynomials that perserve the average acceleration and execution time (strategy outlined here forum.linuxcnc.org/38-general-linuxcnc-q...accelerations#102589).

Grotius let me know if you would like testers for your component.
-automata

 
The following user(s) said Thank You: Grotius

Please Log in or Create an account to join the conversation.

Time to create page: 0.283 seconds
Powered by Kunena Forum