Make feed override control the WASM trajectory

This commit is contained in:
2026-08-31 06:19:36 +08:00
parent 2cda55e724
commit 51ba923cb5
13 changed files with 181 additions and 15 deletions

View File

@@ -253,7 +253,7 @@ if(EMSCRIPTEN)
string(REPLACE "_sim_io_init" "_sim_io_size','_sim_io_init" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_io_set_estop','_sim_io_recover" "_sim_io_set_estop','_sim_io_set_power','_sim_io_recover" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_device_init" "_sim_device_io_size','_sim_device_init" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_step','_sim_motion_read_state" "_sim_motion_step','_sim_motion_set_profile','_sim_motion_set_lookahead','_sim_motion_peek','_sim_motion_read_state" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_step','_sim_motion_read_state" "_sim_motion_step','_sim_motion_set_profile','_sim_motion_set_lookahead','_sim_motion_set_feed_override','_sim_motion_peek','_sim_motion_read_state" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_state_hash','_sim_motion_canonical" "_sim_motion_state_hash','_sim_motion_plan_hash','_sim_motion_canonical" _sim_module_link_flags "${_sim_module_link_flags}")
set_target_properties(sim_core_module PROPERTIES LINK_FLAGS "${_sim_module_link_flags}")
endif()

View File

@@ -30,8 +30,11 @@ typedef struct sim_motion_command {
sim_posemath_segment segment;
uint32_t id;
uint32_t tag;
uint32_t flags;
} sim_motion_command;
#define SIM_MOTION_COMMAND_FLAG_RAPID 1u
typedef struct sim_motion_state {
sim_posemath_pose position;
double velocity;
@@ -65,6 +68,7 @@ int sim_motion_step(sim_motion_queue *queue);
int sim_motion_set_profile(sim_motion_queue *queue, int profile);
int sim_motion_set_lookahead(sim_motion_queue *queue, uint32_t depth);
int sim_motion_set_dynamics(sim_motion_queue *queue, double acceleration_limit, double jerk_limit);
int sim_motion_set_feed_override(sim_motion_queue *queue, double feed_override);
int sim_motion_set_paused(sim_motion_queue *queue, uint32_t paused);
int sim_motion_peek(const sim_motion_queue *queue, uint32_t offset, sim_motion_command *command);
int sim_motion_read_state(const sim_motion_queue *queue, sim_motion_state *state);

View File

@@ -70,6 +70,7 @@ enum sim_task_gate_command {
SIM_TASK_GATE_SET_MODE = 13,
SIM_TASK_GATE_PROGRAM_LOAD = 14,
SIM_TASK_GATE_STEP = 15,
SIM_TASK_GATE_OVERRIDE = 16,
};
enum sim_task_gate_result {

View File

@@ -12,6 +12,7 @@ typedef struct motion_meta {
uint32_t lookahead;
double acceleration_limit;
double jerk_limit;
double feed_override;
double current_velocity;
double current_acceleration;
uint32_t paused;
@@ -34,6 +35,7 @@ static motion_meta *meta_for(const sim_motion_queue *queue, int create) {
free_slot->lookahead = 1;
free_slot->acceleration_limit = 1e9;
free_slot->jerk_limit = 1e12;
free_slot->feed_override = 1.0;
free_slot->paused = 0;
return free_slot;
}
@@ -106,6 +108,7 @@ int sim_motion_reset(sim_motion_queue *queue) {
meta->lookahead = 1;
meta->acceleration_limit = 1e9;
meta->jerk_limit = 1e12;
meta->feed_override = 1.0;
meta->current_velocity = 0.0;
meta->current_acceleration = 0.0;
meta->paused = 0;
@@ -147,6 +150,13 @@ int sim_motion_set_dynamics(sim_motion_queue *queue, double acceleration_limit,
return SIM_MOTION_OK;
}
int sim_motion_set_feed_override(sim_motion_queue *queue, double feed_override) {
motion_meta *meta = meta_for(queue, 1);
if (meta == NULL || !isfinite(feed_override) || feed_override < 0.0 || feed_override > 2.0) return SIM_MOTION_ERR_RANGE;
meta->feed_override = feed_override;
return SIM_MOTION_OK;
}
int sim_motion_set_paused(sim_motion_queue *queue, uint32_t paused) {
motion_meta *meta = meta_for(queue, 1);
if (meta == NULL || paused > 1u) return SIM_MOTION_ERR_ARGUMENT;
@@ -174,11 +184,12 @@ static double planned_velocity(const sim_motion_queue *queue, uint32_t offset) {
uint32_t index;
if (queue == NULL || offset >= queue->count) return 0.0;
index = (queue->head + offset) % SIM_MOTION_QUEUE_CAPACITY;
velocity = queue->commands[index].segment.velocity;
velocity = queue->commands[index].segment.velocity * (meta == NULL || (queue->commands[index].flags & SIM_MOTION_COMMAND_FLAG_RAPID) != 0u ? 1.0 : meta->feed_override);
if (meta == NULL || meta->profile == SIM_MOTION_PROFILE_EXACT_STOP) return velocity;
for (index = 1; index <= meta->lookahead && offset + index < queue->count; ++index) {
uint32_t next = (queue->head + offset + index) % SIM_MOTION_QUEUE_CAPACITY;
if (queue->commands[next].segment.velocity < velocity) velocity = queue->commands[next].segment.velocity;
const double next_velocity = queue->commands[next].segment.velocity * ((queue->commands[next].flags & SIM_MOTION_COMMAND_FLAG_RAPID) != 0u ? 1.0 : meta->feed_override);
if (next_velocity < velocity) velocity = next_velocity;
}
return velocity;
}

View File

@@ -224,6 +224,10 @@ int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
/* AXIS allows jog commands while idle in all three task modes; the
manual tab changes mode before sending the command when required. */
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_INTERP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_OVERRIDE:
/* Feed override is an online trajectory control. LinuxCNC accepts it
while AUTO is running or paused, provided the machine is powered. */
return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_MDI:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;

View File

@@ -44,6 +44,28 @@ int main(void) {
assert(sim_motion_set_profile(&queue, SIM_MOTION_PROFILE_EXACT_STOP) == SIM_MOTION_OK);
assert(sim_motion_set_lookahead(&queue, 0) == SIM_MOTION_ERR_RANGE);
assert(sim_motion_set_dynamics(&queue, 0.0, 1.0) == SIM_MOTION_ERR_RANGE);
{
sim_motion_queue override_queue = {0};
sim_motion_state override_state = {0};
sim_motion_command override_command = {0};
override_command.segment = line(0, 1);
assert(sim_motion_init(&override_queue, 0.1, 10.0) == SIM_MOTION_OK);
assert(sim_motion_set_feed_override(&override_queue, 0.5) == SIM_MOTION_OK);
assert(sim_motion_set_feed_override(&override_queue, 0.5) == SIM_MOTION_OK);
assert(sim_motion_set_feed_override(&override_queue, -0.1) == SIM_MOTION_ERR_RANGE);
assert(sim_motion_set_feed_override(&override_queue, 2.1) == SIM_MOTION_ERR_RANGE);
assert(sim_motion_enqueue(&override_queue, &override_command, 0) == SIM_MOTION_OK);
assert(sim_motion_step(&override_queue) == SIM_MOTION_OK);
assert(sim_motion_read_state(&override_queue, &override_state) == SIM_MOTION_OK);
assert(override_state.position.tran.x > 0.049 && override_state.position.tran.x < 0.051);
assert(override_state.velocity > 0.49 && override_state.velocity < 0.51);
sim_motion_reset(&override_queue);
override_command.flags = SIM_MOTION_COMMAND_FLAG_RAPID;
assert(sim_motion_enqueue(&override_queue, &override_command, 0) == SIM_MOTION_OK);
assert(sim_motion_step(&override_queue) == SIM_MOTION_OK);
assert(sim_motion_read_state(&override_queue, &override_state) == SIM_MOTION_OK);
assert(override_state.position.tran.x > 0.099 && override_state.position.tran.x < 0.101);
}
command.segment = line(9, 10);
assert(sim_motion_enqueue(&queue, &command, 0) == SIM_MOTION_ERR_STATE);
command.segment.motion_type = 99;

View File

@@ -28,6 +28,9 @@ int main(void) {
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_PAUSE_STATE);
assert(sim_task_set_mode(&task, SIM_TASK_MODE_AUTO) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 0, 0, 1, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 1, 1, 1, 0, 10) == SIM_TASK_GATE_ESTOP_ACTIVE);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_SET_MODE, SIM_TASK_MODE_MANUAL, 1, 0, 1, 0, 10) == SIM_TASK_GATE_MODE_AUTO_ACTIVE);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_ESTOP, 1, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);