Exemple #1
0
uint8_t cm_straight_probe(float target[], float flags[])
{
    // trap zero feed rate condition
    if ((cm.gm.feed_rate_mode != INVERSE_TIME_MODE) && (fp_ZERO(cm.gm.feed_rate))) {
        return (STAT_GCODE_FEEDRATE_NOT_SPECIFIED);
    }

    // error if no axes specified
    if (fp_NOT_ZERO(flags[AXIS_X]) && fp_NOT_ZERO(flags[AXIS_Y]) && fp_NOT_ZERO(flags[AXIS_Z])) {
        return (STAT_GCODE_AXIS_IS_MISSING);
    }

    // set probe move endpoint
    copy_vector(pb.target, target);     // set probe move endpoint
    copy_vector(pb.flags, flags);       // set axes involved on the move
    clear_vector(cm.probe_results);     // clear the old probe position.
    // NOTE: relying on probe_result will not detect a probe to 0,0,0.

    cm.probe_state = PROBE_WAITING;     // wait until planner queue empties before completing initialization
    pb.func = _probing_init;            // bind probing initialization function
    return (STAT_OK);
}
Exemple #2
0
/*
 * _compute_arc() - compute arc from I and J (arc center point)
 *
 *	The theta calculation sets up an clockwise or counterclockwise arc from the current
 *	position to the target position around the center designated by the offset vector.
 *	All theta-values measured in radians of deviance from the positive y-axis.
 *
 *                      | <- theta == 0
 *                    * * *
 *                  *       *
 *                *           *
 *                *     O ----T   <- theta_end (e.g. 90 degrees: theta_end == PI/2)
 *                *   /
 *                  C   <- theta_start (e.g. -145 degrees: theta_start == -PI*(3/4))
 *
 *  Parts of this routine were originally sourced from the grbl project.
 */
static stat_t _compute_arc()
{
	// A non-zero radius value indicates a radius arc
	// Compute IJK offset coordinates. These override any current IJK offsets
	if (fp_NOT_ZERO(arc.radius)) ritorno(_compute_arc_offsets_from_radius()); // returns if error

	// Calculate the theta (angle) of the current point (see header notes)
	// Arc.theta is starting point for theta (theta_start)
	arc.theta = _get_theta(-arc.offset[arc.plane_axis_0], -arc.offset[arc.plane_axis_1]);
	if(isnan(arc.theta) == true) return(STAT_ARC_SPECIFICATION_ERROR);

	// calculate the theta (angle) of the target point
	float theta_end = _get_theta(
		arc.gm.target[arc.plane_axis_0] - arc.offset[arc.plane_axis_0] - arc.position[arc.plane_axis_0],
 		arc.gm.target[arc.plane_axis_1] - arc.offset[arc.plane_axis_1] - arc.position[arc.plane_axis_1]);
	if(isnan(theta_end) == true) return (STAT_ARC_SPECIFICATION_ERROR);

	// ensure that the difference is positive so we have clockwise travel
	if (theta_end < arc.theta) { theta_end += 2*M_PI; }

	// compute angular travel and invert if gcode wants a counterclockwise arc
	// if angular travel is zero interpret it as a full circle
	arc.angular_travel = theta_end - arc.theta;
	if (fp_ZERO(arc.angular_travel)) {
		if (cm.gm.motion_mode == MOTION_MODE_CCW_ARC) {
			arc.angular_travel -= 2*M_PI;
		} else {
			arc.angular_travel = 2*M_PI;
		}
	} else {
		if (cm.gm.motion_mode == MOTION_MODE_CCW_ARC) {
			arc.angular_travel -= 2*M_PI;
		}
	}

	// Find the radius, calculate travel in the depth axis of the helix,
	// and compute the time it should take to perform the move
	arc.radius = hypot(arc.offset[arc.plane_axis_0], arc.offset[arc.plane_axis_1]);
	arc.linear_travel = arc.gm.target[arc.linear_axis] - arc.position[arc.linear_axis];

	// length is the total mm of travel of the helix (or just a planar arc)
	arc.length = hypot(arc.angular_travel * arc.radius, fabs(arc.linear_travel));
	if (arc.length < cm.arc_segment_len) return (STAT_MINIMUM_LENGTH_MOVE); // arc is too short to draw

	arc.time = _get_arc_time(arc.linear_travel, arc.angular_travel, arc.radius);

	// Find the minimum number of segments that meets these constraints...
	float segments_required_for_chordal_accuracy = arc.length / sqrt(4*cm.chordal_tolerance * (2 * arc.radius - cm.chordal_tolerance));
	float segments_required_for_minimum_distance = arc.length / cm.arc_segment_len;
	float segments_required_for_minimum_time = arc.time * MICROSECONDS_PER_MINUTE / MIN_ARC_SEGMENT_USEC;
	arc.segments = floor(min3(segments_required_for_chordal_accuracy,
							   segments_required_for_minimum_distance,
							   segments_required_for_minimum_time));

	arc.segments = max(arc.segments, 1);		//...but is at least 1 segment
	arc.gm.move_time = arc.time / arc.segments;	// gcode state struct gets segment_time, not arc time
	arc.segment_count = (int32_t)arc.segments;
	arc.segment_theta = arc.angular_travel / arc.segments;
	arc.segment_linear_travel = arc.linear_travel / arc.segments;
	arc.center_0 = arc.position[arc.plane_axis_0] - sin(arc.theta) * arc.radius;
	arc.center_1 = arc.position[arc.plane_axis_1] - cos(arc.theta) * arc.radius;
	arc.gm.target[arc.linear_axis] = arc.position[arc.linear_axis];	// initialize the linear target
	return (STAT_OK);
}
Exemple #3
0
void mp_calculate_trapezoid(mpBuf_t *bf)
{
	//********************************************
	//********************************************
	//**   RULE #1 of mp_calculate_trapezoid()  **
	//**        DON'T CHANGE bf->length         **
	//********************************************
	//********************************************

	// F case: Block is too short - run time < minimum segment time
	// Force block into a single segment body with limited velocities
	// Accept the entry velocity, limit the cruise, and go for the best exit velocity
	// you can get given the delta_vmax (maximum velocity slew) supportable.

	bf->naiive_move_time = 2 * bf->length / (bf->entry_velocity + bf->exit_velocity); // average

	if (bf->naiive_move_time < MIN_SEGMENT_TIME_PLUS_MARGIN) {
		bf->cruise_velocity = bf->length / MIN_SEGMENT_TIME_PLUS_MARGIN;
		bf->exit_velocity = max(0.0, min(bf->cruise_velocity, (bf->entry_velocity - bf->delta_vmax)));
		bf->body_length = bf->length;
		bf->head_length = 0;
		bf->tail_length = 0;
		// We are violating the jerk value but since it's a single segment move we don't use it.
		return;
	}

	// B" case: Block is short, but fits into a single body segment

	if (bf->naiive_move_time <= NOM_SEGMENT_TIME) {
		bf->entry_velocity = bf->pv->exit_velocity;
		if (fp_NOT_ZERO(bf->entry_velocity)) {
			bf->cruise_velocity = bf->entry_velocity;
			bf->exit_velocity = bf->entry_velocity;
		} else {
			bf->cruise_velocity = bf->delta_vmax / 2;
			bf->exit_velocity = bf->delta_vmax;
		}
		bf->body_length = bf->length;
		bf->head_length = 0;
		bf->tail_length = 0;
		// We are violating the jerk value but since it's a single segment move we don't use it.
		return;
	}

	// B case:  Velocities all match (or close enough)
	//			This occurs frequently in normal gcode files with lots of short lines
	//			This case is not really necessary, but saves lots of processing time

	if (((bf->cruise_velocity - bf->entry_velocity) < TRAPEZOID_VELOCITY_TOLERANCE) &&
	((bf->cruise_velocity - bf->exit_velocity) < TRAPEZOID_VELOCITY_TOLERANCE)) {
		bf->body_length = bf->length;
		bf->head_length = 0;
		bf->tail_length = 0;
		return;
	}

	// Head-only and tail-only short-line cases
	//	 H" and T" degraded-fit cases
	//	 H' and T' requested-fit cases where the body residual is less than MIN_BODY_LENGTH

	bf->body_length = 0;
	float minimum_length = mp_get_target_length(bf->entry_velocity, bf->exit_velocity, bf);
	if (bf->length <= (minimum_length + MIN_BODY_LENGTH)) {	// head-only & tail-only cases

		if (bf->entry_velocity > bf->exit_velocity)	{		// tail-only cases (short decelerations)
			if (bf->length < minimum_length) { 				// T" (degraded case)
				bf->entry_velocity = mp_get_target_velocity(bf->exit_velocity, bf->length, bf);
			}
			bf->cruise_velocity = bf->entry_velocity;
			bf->tail_length = bf->length;
			bf->head_length = 0;
			return;
		}

		if (bf->entry_velocity < bf->exit_velocity)	{		// head-only cases (short accelerations)
			if (bf->length < minimum_length) { 				// H" (degraded case)
				bf->exit_velocity = mp_get_target_velocity(bf->entry_velocity, bf->length, bf);
			}
			bf->cruise_velocity = bf->exit_velocity;
			bf->head_length = bf->length;
			bf->tail_length = 0;
			return;
		}
	}

	// Set head and tail lengths for evaluating the next cases
	bf->head_length = mp_get_target_length(bf->entry_velocity, bf->cruise_velocity, bf);
	bf->tail_length = mp_get_target_length(bf->exit_velocity, bf->cruise_velocity, bf);
	if (bf->head_length < MIN_HEAD_LENGTH) { bf->head_length = 0;}
	if (bf->tail_length < MIN_TAIL_LENGTH) { bf->tail_length = 0;}

	// Rate-limited HT and HT' cases
	if (bf->length < (bf->head_length + bf->tail_length)) { // it's rate limited

		// Symmetric rate-limited case (HT)
		if (fabs(bf->entry_velocity - bf->exit_velocity) < TRAPEZOID_VELOCITY_TOLERANCE) {
			bf->head_length = bf->length/2;
			bf->tail_length = bf->head_length;
			bf->cruise_velocity = min(bf->cruise_vmax, mp_get_target_velocity(bf->entry_velocity, bf->head_length, bf));

			if (bf->head_length < MIN_HEAD_LENGTH) {
				// Convert this to a body-only move
				bf->body_length = bf->length;
				bf->head_length = 0;
				bf->tail_length = 0;

				// Average the entry speed and computed best cruise-speed
				bf->cruise_velocity = (bf->entry_velocity + bf->cruise_velocity)/2;
				bf->entry_velocity = bf->cruise_velocity;
				bf->exit_velocity = bf->cruise_velocity;
			}
			return;
		}

		// Asymmetric HT' rate-limited case. This is relatively expensive but it's not called very often
		// iteration trap: uint8_t i=0;
		// iteration trap: if (++i > TRAPEZOID_ITERATION_MAX) { fprintf_P(stderr,PSTR("_calculate_trapezoid() failed to converge"));}

		float computed_velocity = bf->cruise_vmax;
		do {
			bf->cruise_velocity = computed_velocity;	// initialize from previous iteration
			bf->head_length = mp_get_target_length(bf->entry_velocity, bf->cruise_velocity, bf);
			bf->tail_length = mp_get_target_length(bf->exit_velocity, bf->cruise_velocity, bf);
			if (bf->head_length > bf->tail_length) {
				bf->head_length = (bf->head_length / (bf->head_length + bf->tail_length)) * bf->length;
				computed_velocity = mp_get_target_velocity(bf->entry_velocity, bf->head_length, bf);
			} else {
				bf->tail_length = (bf->tail_length / (bf->head_length + bf->tail_length)) * bf->length;
				computed_velocity = mp_get_target_velocity(bf->exit_velocity, bf->tail_length, bf);
			}
			// insert iteration trap here if needed
		} while ((fabs(bf->cruise_velocity - computed_velocity) / computed_velocity) > TRAPEZOID_ITERATION_ERROR_PERCENT);

		// set velocity and clean up any parts that are too short
		bf->cruise_velocity = computed_velocity;
		bf->head_length = mp_get_target_length(bf->entry_velocity, bf->cruise_velocity, bf);
		bf->tail_length = bf->length - bf->head_length;
		if (bf->head_length < MIN_HEAD_LENGTH) {
			bf->tail_length = bf->length;			// adjust the move to be all tail...
			bf->head_length = 0;
		}
		if (bf->tail_length < MIN_TAIL_LENGTH) {
			bf->head_length = bf->length;			//...or all head
			bf->tail_length = 0;
		}
		return;
	}

	// Requested-fit cases: remaining of: HBT, HB, BT, BT, H, T, B, cases
	bf->body_length = bf->length - bf->head_length - bf->tail_length;

	// If a non-zero body is < minimum length distribute it to the head and/or tail
	// This will generate small (acceptable) velocity errors in runtime execution
	// but preserve correct distance, which is more important.
	if ((bf->body_length < MIN_BODY_LENGTH) && (fp_NOT_ZERO(bf->body_length))) {
		if (fp_NOT_ZERO(bf->head_length)) {
			if (fp_NOT_ZERO(bf->tail_length)) {			// HBT reduces to HT
				bf->head_length += bf->body_length/2;
				bf->tail_length += bf->body_length/2;
			} else {									// HB reduces to H
				bf->head_length += bf->body_length;
			}
		} else {										// BT reduces to T
			bf->tail_length += bf->body_length;
		}
		bf->body_length = 0;

	// If the body is a standalone make the cruise velocity match the entry velocity
	// This removes a potential velocity discontinuity at the expense of top speed
	} else if ((fp_ZERO(bf->head_length)) && (fp_ZERO(bf->tail_length))) {
		bf->cruise_velocity = bf->entry_velocity;
	}
}
Exemple #4
0
/*
 * cm_arc_feed() - canonical machine entry point for arc
 *
 * Generates an arc by queuing line segments to the move buffer. The arc is
 * approximated by generating a large number of tiny, linear arc_segments.
 */
stat_t cm_arc_feed(float target[], float flags[],       // arc endpoints
				   float i, float j, float k,           // raw arc offsets
				   float radius,                        // non-zero radius implies radius mode
				   uint8_t motion_mode)                 // defined motion mode
{
	////////////////////////////////////////////////////
	// Set axis plane and trap arc specification errors

	// trap missing feed rate
	if ((cm.gm.feed_rate_mode != INVERSE_TIME_MODE) && (fp_ZERO(cm.gm.feed_rate))) {
    	return (STAT_GCODE_FEEDRATE_NOT_SPECIFIED);
	}

    // set radius mode flag and do simple test(s)
	bool radius_f = fp_NOT_ZERO(cm.gf.arc_radius);			    // set true if radius arc
    if ((radius_f) && (cm.gn.arc_radius < MIN_ARC_RADIUS)) {    // radius value must be + and > minimum radius
        return (STAT_ARC_RADIUS_OUT_OF_TOLERANCE);
    }

    // setup some flags
	bool target_x = fp_NOT_ZERO(flags[AXIS_X]);	                // set true if X axis has been specified
	bool target_y = fp_NOT_ZERO(flags[AXIS_Y]);
	bool target_z = fp_NOT_ZERO(flags[AXIS_Z]);

    bool offset_i = fp_NOT_ZERO(cm.gf.arc_offset[0]);	        // set true if offset I has been specified
    bool offset_j = fp_NOT_ZERO(cm.gf.arc_offset[1]);           // J
    bool offset_k = fp_NOT_ZERO(cm.gf.arc_offset[2]);           // K

	// Set the arc plane for the current G17/G18/G19 setting and test arc specification
	// Plane axis 0 and 1 are the arc plane, the linear axis is normal to the arc plane.
	if (cm.gm.select_plane == CANON_PLANE_XY) {	// G17 - the vast majority of arcs are in the G17 (XY) plane
    	arc.plane_axis_0 = AXIS_X;
    	arc.plane_axis_1 = AXIS_Y;
    	arc.linear_axis  = AXIS_Z;
        if (radius_f) {
            if (!(target_x || target_y)) {                      // must have at least one endpoint specified
        	    return (STAT_ARC_AXIS_MISSING_FOR_SELECTED_PLANE);
            }
        } else { // center format arc tests
            if (offset_k) { // it's OK to be missing either or both i and j, but error if k is present
        	    return (STAT_ARC_SPECIFICATION_ERROR);
            }
        }

    } else if (cm.gm.select_plane == CANON_PLANE_XZ) {	// G18
    	arc.plane_axis_0 = AXIS_X;
    	arc.plane_axis_1 = AXIS_Z;
    	arc.linear_axis  = AXIS_Y;
        if (radius_f) {
            if (!(target_x || target_z))
                return (STAT_ARC_AXIS_MISSING_FOR_SELECTED_PLANE);
        } else {
            if (offset_j)
                return (STAT_ARC_SPECIFICATION_ERROR);
        }

    } else if (cm.gm.select_plane == CANON_PLANE_YZ) {	// G19
    	arc.plane_axis_0 = AXIS_Y;
    	arc.plane_axis_1 = AXIS_Z;
    	arc.linear_axis  = AXIS_X;
        if (radius_f) {
            if (!(target_y || target_z))
                return (STAT_ARC_AXIS_MISSING_FOR_SELECTED_PLANE);
        } else {
            if (offset_i)
                return (STAT_ARC_SPECIFICATION_ERROR);
        }
	}

	// set values in the Gcode model state & copy it (linenum was already captured)
	cm_set_model_target(target, flags);

    // in radius mode it's an error for start == end
    if(radius_f) {
        if ((fp_EQ(cm.gmx.position[AXIS_X], cm.gm.target[AXIS_X])) &&
            (fp_EQ(cm.gmx.position[AXIS_Y], cm.gm.target[AXIS_Y])) &&
            (fp_EQ(cm.gmx.position[AXIS_Z], cm.gm.target[AXIS_Z]))) {
            return (STAT_ARC_ENDPOINT_IS_STARTING_POINT);
        }
    }

    // now get down to the rest of the work setting up the arc for execution
	cm.gm.motion_mode = motion_mode;
	cm_set_work_offsets(&cm.gm);					// capture the fully resolved offsets to gm
	memcpy(&arc.gm, &cm.gm, sizeof(GCodeState_t));	// copy GCode context to arc singleton - some will be overwritten to run segments
	copy_vector(arc.position, cm.gmx.position);		// set initial arc position from gcode model

	arc.radius = _to_millimeters(radius);			// set arc radius or zero

	arc.offset[0] = _to_millimeters(i);				// copy offsets with conversion to canonical form (mm)
	arc.offset[1] = _to_millimeters(j);
	arc.offset[2] = _to_millimeters(k);

	arc.rotations = floor(fabs(cm.gn.parameter));   // P must be a positive integer - force it if not

	// determine if this is a full circle arc. Evaluates true if no target is set
	arc.full_circle = (fp_ZERO(flags[arc.plane_axis_0]) & fp_ZERO(flags[arc.plane_axis_1]));

	// compute arc runtime values
	ritorno(_compute_arc());

	if (fp_ZERO(arc.length)) {
        return (STAT_MINIMUM_LENGTH_MOVE);          // trap zero length arcs that _compute_arc can throw
    }

/*	// test arc soft limits
	stat_t status = _test_arc_soft_limits();
	if (status != STAT_OK) {
    	cm.gm.motion_mode = MOTION_MODE_CANCEL_MOTION_MODE;
    	copy_vector(cm.gm.target, cm.gmx.position);		// reset model position
    	return (cm_soft_alarm(status));
	}
*/
	cm_cycle_start();						// if not already started
	arc.run_state = MOVE_RUN;				// enable arc to be run from the callback
	cm_finalize_move();
	return (STAT_OK);
}
Exemple #5
0
static stat_t _compute_arc()
{
	// Compute radius. A non-zero radius value indicates a radius arc
    if (fp_NOT_ZERO(arc.radius)) {                  // indicates a radius arc
        _compute_arc_offsets_from_radius();
    } else {                                        // compute start radius
        arc.radius = hypotf(-arc.offset[arc.plane_axis_0], -arc.offset[arc.plane_axis_1]);
    }

    // Test arc specification for correctness according to:
    // http://linuxcnc.org/docs/html/gcode/gcode.html#sec:G2-G3-Arc
    // "It is an error if: when the arc is projected on the selected plane, the distance from
    //  the current point to the center differs from the distance from the end point to the
    //  center by more than (.05 inch/.5 mm) OR ((.0005 inch/.005mm) AND .1% of radius)."

    // Compute end radius from the center of circle (offsets) to target endpoint
    float end_0 = arc.gm.target[arc.plane_axis_0] - arc.position[arc.plane_axis_0] - arc.offset[arc.plane_axis_0];
    float end_1 = arc.gm.target[arc.plane_axis_1] - arc.position[arc.plane_axis_1] - arc.offset[arc.plane_axis_1];
    float err = fabs(hypotf(end_0, end_1) - arc.radius);   // end radius - start radius
    if ( (err > ARC_RADIUS_ERROR_MAX) ||
        ((err > ARC_RADIUS_ERROR_MIN) && (err > arc.radius * ARC_RADIUS_TOLERANCE)) ) {
//        return (STAT_ARC_HAS_IMPOSSIBLE_CENTER_POINT);
        return (STAT_ARC_SPECIFICATION_ERROR);
    }

	// Calculate the theta (angle) of the current point (position)
	// arc.theta is angular starting point for the arc (also needed later for calculating center point)
    arc.theta = atan2(-arc.offset[arc.plane_axis_0], -arc.offset[arc.plane_axis_1]);

    // g18_correction is used to invert G18 XZ plane arcs for proper CW orientation
    float g18_correction = (cm.gm.select_plane == CANON_PLANE_XZ) ? -1 : 1;

	if (arc.full_circle) {                                  // if full circle you can skip the stuff in the else clause
    	arc.angular_travel = 0;                             // angular travel always starts as zero for full circles
    	if (fp_ZERO(arc.rotations)) {                       // handle the valid case of a full circle arc w/P=0
            arc.rotations = 1.0;
        }
    } else {                                                // ... it's not a full circle
        arc.theta_end = atan2(end_0, end_1);

        // Compute the angular travel
        if (fp_EQ(arc.theta_end, arc.theta)) {
	        arc.angular_travel = 0;                         // very large radii arcs can have zero angular travel (thanks PartKam)
        } else {
	        if (arc.theta_end < arc.theta) {                // make the difference positive so we have clockwise travel
                arc.theta_end += (2*M_PI * g18_correction);
            }
	        arc.angular_travel = arc.theta_end - arc.theta; // compute positive angular travel
    	    if (cm.gm.motion_mode == MOTION_MODE_CCW_ARC) { // reverse travel direction if it's CCW arc
                arc.angular_travel -= (2*M_PI * g18_correction);
            }
        }
	}

    // Add in travel for rotations
    if (cm.gm.motion_mode == MOTION_MODE_CW_ARC) {
        arc.angular_travel += (2*M_PI * arc.rotations * g18_correction);
    } else {
        arc.angular_travel -= (2*M_PI * arc.rotations * g18_correction);
    }

	// Calculate travel in the depth axis of the helix and compute the time it should take to perform the move
	// arc.length is the total mm of travel of the helix (or just a planar arc)
	arc.linear_travel = arc.gm.target[arc.linear_axis] - arc.position[arc.linear_axis];
	arc.planar_travel = arc.angular_travel * arc.radius;
	arc.length = hypotf(arc.planar_travel, arc.linear_travel);  // NB: hypot is insensitive to +/- signs
	_estimate_arc_time();	// get an estimate of execution time to inform arc_segment calculation

	// Find the minimum number of arc_segments that meets these constraints...
	float arc_segments_for_chordal_accuracy = arc.length / sqrt(4*cm.chordal_tolerance * (2 * arc.radius - cm.chordal_tolerance));
	float arc_segments_for_minimum_distance = arc.length / cm.arc_segment_len;
	float arc_segments_for_minimum_time = arc.arc_time * MICROSECONDS_PER_MINUTE / MIN_ARC_SEGMENT_USEC;

	arc.arc_segments = floor(min3(arc_segments_for_chordal_accuracy,
							      arc_segments_for_minimum_distance,
							      arc_segments_for_minimum_time));

	arc.arc_segments = max(arc.arc_segments, 1);            //...but is at least 1 arc_segment
 	arc.gm.move_time = arc.arc_time / arc.arc_segments;     // gcode state struct gets arc_segment_time, not arc time
	arc.arc_segment_count = (int32_t)arc.arc_segments;
	arc.arc_segment_theta = arc.angular_travel / arc.arc_segments;
	arc.arc_segment_linear_travel = arc.linear_travel / arc.arc_segments;
    arc.center_0 = arc.position[arc.plane_axis_0] - sin(arc.theta) * arc.radius;
    arc.center_1 = arc.position[arc.plane_axis_1] - cos(arc.theta) * arc.radius;
	arc.gm.target[arc.linear_axis] = arc.position[arc.linear_axis];	// initialize the linear target
	return (STAT_OK);
}