Permalink
Browse files

Allow G0/G1 Z-axis moves to be clamped to the machine cube.

This adds a new configuration option, "clamp-to-range", that will allow
G0/G1 moves to be parsed that are outside the machine cube space. These
moves will be clamped to the machine cube and still produce motion.

The clamp-to-range option will allow to clamp a movement before the
range-check is testing.

While clamp-to-range takes a string of desired axes to clamp to,
for now only the Z axis is allowed if it is the only axis moving.
This makes the most sense in 2.5D applications without accidentally
causing undesirable work results.

Based on a patch by H Hartley Sweeten <hsweeten@visionengravers.com>
  • Loading branch information...
hzeller committed Jul 27, 2018
1 parent ad6227d commit f9ce5e9b4742927cd5b69587fdd87513c4bfd822
@@ -11,6 +11,14 @@
home-order = XYZ
require-homing = yes # Require homing (G28) is executed before first move.
range-check = yes # Check that axes are within range. Dangerous if no.
# It is possible to clamp movements that would leave the machine cube to
# range; that way the range-check will not trigger for them, but movements don't
# will not end up where they were intended to be in that case, so configure
# with care. Typically it makes most sense in 2.5D applications where the
# Z-axis is less important. Thus this feature is limited to the Z-axis
#clamp-to-range = Z
auto-motor-disable-seconds = 120 # Switch off motors after 2min of inactivity.
# -- Logical axis configuration
@@ -98,6 +98,7 @@ class GCodeMachineControl::Impl : public GCodeParser::EventReceiver {
void wait_temperature() final; // M109, M116: Wait for temp. reached.
void dwell(float time_ms) final; // G4: dwell for milliseconds.
void motors_enable(bool enable) final; // M17,M84,M18: Switch on/off motors
void clamp_to_range(AxisBitmap_t affected, AxesRegister *axes) final;
bool coordinated_move(float feed_mm_p_sec, const AxesRegister &target) final;
bool rapid_move(float feed_mm_p_sec, const AxesRegister &target) final;
const char *unprocessed(char letter, float value, const char *) final;
@@ -134,15 +135,17 @@ class GCodeMachineControl::Impl : public GCodeParser::EventReceiver {
private:
const struct MachineControlConfig cfg_;
MotorOperations *const motor_ops_;
Planner *planner_;
HardwareMapping *const hardware_mapping_;
Spindle *const spindle_;
FILE *msg_stream_;
GCodeParser *parser_;
Planner *planner_ = nullptr;
FILE *msg_stream_ = nullptr;
GCodeParser *parser_ = nullptr;
// Derived configuration
float g0_feedrate_mm_per_sec_; // Highest of all axes; used for G0
// (will be trimmed if needed)
AxisBitmap_t axis_clamped_; // All axes that are clamped to range
// Current machine configuration
AxesRegister coordinate_display_origin_; // parser tells us
std::string coordinate_display_origin_name_;
@@ -232,6 +235,27 @@ bool GCodeMachineControl::Impl::Init() {
}
}
axis_clamped_ = 0;
for (char c : cfg_.clamp_to_range) {
const GCodeParserAxis clamped_axis = gcodep_letter2axis(c);
if (clamped_axis == GCODE_NUM_AXES) {
Log_error("clamp-to-range: Invalid clamping axis %c", c);
++error_count;
continue;
}
// Technically, we can do all axes, but in practice, it doesn't make sense
// without risking undesirable work-results.
// So for now, only do that on Z which can make sense in 2.5D applications.
if (clamped_axis != AXIS_Z) {
Log_error("clamp-to-range: Only Z-axis clamp allowed (not %c)", c);
++error_count;
continue;
}
axis_clamped_ |= (1 << clamped_axis);
}
// Now let's see what motors are mapped to any useful output.
Log_debug("-- Config --\n");
for (const GCodeParserAxis axis : AllAxes()) {
@@ -274,8 +298,11 @@ bool GCodeMachineControl::Impl::Init() {
line += StringPrintf("HOME@max->|; ");
}
if (axis_clamped_ & (1 << axis))
line += " [clamped to range]";
if (!cfg_.range_check)
line += "Limit checks disabled!";
line += " Limit checks disabled!";
Log_debug("%s", line.c_str());
@@ -733,6 +760,33 @@ bool GCodeMachineControl::Impl::test_within_machine_limits(const AxesRegister &a
return true;
}
void GCodeMachineControl::Impl::clamp_to_range(AxisBitmap_t affected,
AxesRegister *mutable_axes) {
// Due diligence: only clamp if all affected axes are actually configured.
// Otherwise we might end up in undesirable positions with partial clamps.
// (Right now, only Z-Axis is allowed in fact, see Init()).
if ((affected & axis_clamped_) != affected)
return;
AxesRegister &axes = *mutable_axes;
for (const GCodeParserAxis i : AllAxes()) {
if ((axis_clamped_ & (1 << i)) == 0)
continue;
if (axes[i] < 0) {
mprintf("// WARNING clamping Axis %c move %.1f to %.1fmm\n",
gcodep_axis2letter(i), axes[i], 0);
axes[i] = 0;
}
const float max_limit = cfg_.move_range_mm[i];
if (max_limit <= 0) continue;
if (axes[i] > max_limit) {
mprintf("// WARNING clamping Axis %c move %.1f to %.1fmm\n",
gcodep_axis2letter(i), axes[i], max_limit);
axes[i] = max_limit;
}
}
}
bool GCodeMachineControl::Impl::coordinated_move(float feed,
const AxesRegister &axis) {
if (!test_homing_status_ok())
@@ -61,6 +61,9 @@ struct MachineControlConfig {
bool acknowledge_lines; // Respond w/ 'ok' on each command on msg_stream.
bool require_homing; // Require homing before any moves.
bool range_check; // Do machine limit checks. Default 1.
std::string clamp_to_range; // Clamp these axes to machine range before
// range check. Dangerous.
// Support only "" or "Z" right now.
bool debug_print; // Print step-tuples to output_fd if 1.
bool synchronous; // Don't queue, wait for command to finish if 1.
bool enable_pause; // Enable pause switch detection. Default 0.
@@ -1453,6 +1453,7 @@ const char *GCodeParser::Impl::handle_move(const char *line, bool force_change)
float feedrate = -1;
const char *remaining_line;
AxesRegister new_pos = axes_pos_;
AxisBitmap_t affected_axes = 0;
while ((remaining_line = gparse_pair(line, &axis_l, &value))) {
const float unit_value = value * unit_to_mm_factor_;
@@ -1469,12 +1470,14 @@ const char *GCodeParser::Impl::handle_move(const char *line, bool force_change)
break; // Invalid axis: possibly start of new command.
new_pos[update_axis] = abs_axis_pos(update_axis, unit_value);
any_change = true;
affected_axes |= (1 << update_axis);
}
line = remaining_line;
}
bool did_move = false;
if (any_change) {
callbacks()->clamp_to_range(affected_axes, &new_pos);
if (modal_g0_g1_) {
did_move = callbacks()->coordinated_move(feedrate, new_pos);
} else {
@@ -236,6 +236,17 @@ class GCodeParser::EventReceiver {
virtual void dwell(float time_ms) = 0; // G4: dwell for milliseconds.
virtual void motors_enable(bool enable) = 0; // M17, M84, M18: Switch on/off motors
// Give receiver an opportunity to modify a target coordinate, e.g. clamp
// ranges before executing a G0/G1 move to prevent hitting a range-check
// later.
// Note, if you clamp values, following GCode will be relative to the new
// coordinate, so thoughtful consideration is needed; often it is better
// to go into soft-EStop in range conditions.
// (typically, only Z-clamping makes sense, so this is what it is called
// for now).
// [This API might change before things are fully settled]
virtual void clamp_to_range(AxisBitmap_t affected, AxesRegister *axes) {}
// G1 (coordinated move) and G0 (rapid move). Move to absolute coordinates.
// First parameter is feedrate in mm/sec if provided, or -1 otherwise.
// (typically, the user would need to remember the positive values).
@@ -38,6 +38,7 @@ MachineControlConfig::MachineControlConfig() {
debug_print = false;
synchronous = false;
range_check = true;
clamp_to_range = "";
require_homing = true;
enable_pause = false;
home_order = kHomeOrder;
@@ -81,6 +82,7 @@ class MachineControlConfigReader : public ConfigParser::Reader {
ACCEPT_VALUE("home-order", String, &config_->home_order);
ACCEPT_VALUE("require-homing", Bool, &config_->require_homing);
ACCEPT_VALUE("range-check", Bool, &config_->range_check);
ACCEPT_VALUE("clamp-to-range", String, &config_->clamp_to_range);
ACCEPT_VALUE("synchronous", Bool, &config_->synchronous);
ACCEPT_VALUE("enable-pause", Bool, &config_->enable_pause);
ACCEPT_VALUE("auto-motor-disable-seconds",

0 comments on commit f9ce5e9

Please sign in to comment.