-
Notifications
You must be signed in to change notification settings - Fork 54
a performance improvement for scanning #262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
53e3bbe
7f696ed
28f0684
860725f
9eb55fa
d3d0b32
c901147
c6fbcca
b7a9ce2
1c1f885
5cb40c7
c427919
3a1aa64
8ebcf2c
2f93b68
6a3c3ac
b5ee8a5
b1e393e
10e8ebe
a9acc9c
7d53cc3
68869a5
dd613bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ | |
| // byte[2]: how many micro steps - upper 8 bits | ||
| // byte[3]: how many micro steps - lower 8 bits | ||
|
|
||
| static const int CMD_LENGTH = 8; | ||
| static const int CMD_LENGTH = 24; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't figure out why this needs to go up to 24 instead of 10 (we need 2 extra bytes for the MOVE*_XY, but I don't see why we need 11-24). It's probably okay, but it does mean we send 14 more bytes per command than we absolutely need to. I think that's only ~8 us more per command at 2 MHz, but if we don't need it we might as well not waste the time.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't thought much about it; I'm just leaving room for future situations. OK, I would change to 10.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The command length must be adjusted in multiples of 4 bytes. @ianohara |
||
| static const int MSG_LENGTH = 24; | ||
| byte buffer_rx[512]; | ||
| byte buffer_tx[MSG_LENGTH]; | ||
|
|
@@ -67,6 +67,9 @@ static const int SET_PID_ARGUMENTS = 29; | |
| static const int SEND_HARDWARE_TRIGGER = 30; | ||
| static const int SET_STROBE_DELAY = 31; | ||
| static const int SET_AXIS_DISABLE_ENABLE = 32; | ||
| static const int MOVE_XY = 33; | ||
| static const int MOVETO_XY = 34; | ||
| static const int SET_SRAMP_MOD = 36; | ||
| static const int SET_PIN_LEVEL = 41; | ||
| static const int INITFILTERWHEEL = 253; | ||
| static const int INITIALIZE = 254; | ||
|
|
@@ -301,12 +304,11 @@ uint16_t home_safety_margin[4] = {4, 4, 4, 4}; | |
| // IntervalTimer does not work on teensy with SPI, the below lines are to be removed | ||
| static const int TIMER_PERIOD = 500; // in us | ||
| volatile int counter_send_pos_update = 0; | ||
| volatile bool flag_send_pos_update = false; | ||
|
|
||
| static const int interval_send_pos_update = 10000; // in us | ||
| elapsedMicros us_since_last_pos_update; | ||
|
|
||
| static const int interval_check_position = 10000; // in us | ||
| static const int interval_check_position = 5000; // in us | ||
| elapsedMicros us_since_last_check_position; | ||
|
|
||
| static const int interval_send_joystick_update = 30000; // in us | ||
|
|
@@ -356,7 +358,7 @@ void onJoystickPacketReceived(const uint8_t* buffer, size_t size) | |
| } | ||
| else | ||
| { | ||
| focusPosition = focusPosition + (int32_t(uint32_t(buffer[0]) << 24 | uint32_t(buffer[1]) << 16 | uint32_t(buffer[2]) << 8 | uint32_t(buffer[3])) - focuswheel_pos); | ||
| focusPosition = focusPosition - (int32_t(uint32_t(buffer[0]) << 24 | uint32_t(buffer[1]) << 16 | uint32_t(buffer[2]) << 8 | uint32_t(buffer[3])) - focuswheel_pos); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't figure out why we switched the sign here. Can you help me understand how this is related to scanning performance, and what the sign switch does?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The knob is designed to increase with clockwise rotation and decrease with counterclockwise rotation. This modification aims to align with conventional intuition. |
||
| focuswheel_pos = int32_t(uint32_t(buffer[0]) << 24 | uint32_t(buffer[1]) << 16 | uint32_t(buffer[2]) << 8 | uint32_t(buffer[3])); | ||
| } | ||
|
|
||
|
|
@@ -911,6 +913,21 @@ void loop() { | |
| } | ||
| break; | ||
| } | ||
| case MOVETO_XY: | ||
| { | ||
| long x_absolute_position = int32_t(uint32_t(buffer_rx[2]) << 24 | uint32_t(buffer_rx[3]) << 16 | uint32_t(buffer_rx[4]) << 8 | uint32_t(buffer_rx[5])); | ||
| X_direction = sgn(x_absolute_position - tmc4361A_currentPosition(&tmc4361[x])); | ||
| X_commanded_target_position = x_absolute_position; | ||
| if (tmc4361A_moveTo(&tmc4361[x], X_commanded_target_position) == 0) | ||
| X_commanded_movement_in_progress = true; | ||
| long y_absolute_position = int32_t(uint32_t(buffer_rx[6]) << 24 | uint32_t(buffer_rx[7]) << 16 | uint32_t(buffer_rx[8]) << 8 | uint32_t(buffer_rx[9])); | ||
| Y_direction = sgn(y_absolute_position - tmc4361A_currentPosition(&tmc4361[y])); | ||
| Y_commanded_target_position = y_absolute_position; | ||
| if (tmc4361A_moveTo(&tmc4361[y], Y_commanded_target_position) == 0) | ||
| Y_commanded_movement_in_progress = true; | ||
| mcu_cmd_execution_in_progress = true; | ||
| break; | ||
| } | ||
| case MOVETO_Z: | ||
| { | ||
| long absolute_position = int32_t(uint32_t(buffer_rx[2]) << 24 | uint32_t(buffer_rx[3]) << 16 | uint32_t(buffer_rx[4]) << 8 | uint32_t(buffer_rx[5])); | ||
|
|
@@ -938,6 +955,21 @@ void loop() { | |
| } | ||
| break; | ||
| } | ||
| case SET_SRAMP_MOD: | ||
| { | ||
| uint8_t axis = buffer_rx[2]; | ||
| uint8_t mode = buffer_rx[3]; | ||
| // map to w axis, in the host w axis index is 5 | ||
| if (axis == 5) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate on the comment above? What code is the host code in this case? Also, we have And I don't understand why we map In general - try to avoid using bare numbers like this. Instead, use constants with clear names. This helps make the code more clear, and helps in refactoring later (For instance, if we need to change the axis number for AXIS_W in the future, it's easy to change the AXIS_W define but hard to go and figure out which inline
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the python axes match the defines here, so I'm really not sure what the "map to w axis, in host w axis is 5" comment means. Here are the firmware defines: Here are the python defines:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I would change the code to easy to understand. |
||
| axis = 3; | ||
|
|
||
| // guard code | ||
| if (axis > STAGE_AXES) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it only okay to change the ramp mode for X, Y, and Z? It isn't okay to change them for THETA and W?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For safety reasons, I’ve only tested along the X, Y, and Z axes so far, so I’m keeping it in safe mode for now.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This just fails silently, though. If someone tries to use this command with
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we let all other commands fail silently, so I guess we can keep this. We really need to introduce a mechanism for communicating command failures, though. Failing silently shouldn't be an option! |
||
| return; | ||
|
|
||
| tmc4361[axis].ramp_mode = mode; | ||
| break; | ||
| } | ||
| case SET_LIM: | ||
| { | ||
| switch (buffer_rx[2]) | ||
|
|
@@ -2181,9 +2213,9 @@ void loop() { | |
| buffer_tx[18] &= ~ (1 << BIT_POS_JOYSTICK_BUTTON); // clear the joystick button bit | ||
| buffer_tx[18] = buffer_tx[18] | joystick_button_pressed << BIT_POS_JOYSTICK_BUTTON; | ||
|
|
||
| // Calculate and fill out the checksum. NOTE: This must be after all other buffer_tx modifications are done! | ||
| uint8_t checksum = crc8ccitt(buffer_tx, MSG_LENGTH - 1); | ||
| buffer_tx[MSG_LENGTH - 1] = checksum; | ||
| // Calculate and fill out the checksum. NOTE: This must be after all other buffer_tx modifications are done! | ||
| uint8_t checksum = crc8ccitt(buffer_tx, MSG_LENGTH - 1); | ||
| buffer_tx[MSG_LENGTH - 1] = checksum; | ||
|
|
||
| if(!DEBUG_MODE) | ||
| SerialUSB.write(buffer_tx,MSG_LENGTH); | ||
|
|
@@ -2201,8 +2233,6 @@ void loop() { | |
| SerialUSB.print(", PG:"); | ||
| SerialUSB.println(digitalRead(pin_PG)); | ||
| } | ||
| flag_send_pos_update = false; | ||
|
|
||
| } | ||
|
|
||
| // keep checking position process at suitable frequence | ||
|
|
@@ -2213,22 +2243,26 @@ void loop() { | |
| { | ||
| X_commanded_movement_in_progress = false; | ||
| mcu_cmd_execution_in_progress = false || Y_commanded_movement_in_progress || Z_commanded_movement_in_progress || W_commanded_movement_in_progress; | ||
| us_since_last_check_position = interval_check_position + 1; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A comment here to explain this might be helpful. Something like "// It's important that we check positions next time around because <explain why ...>, so force the elapsed time to be larger than our interval."
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK |
||
| } | ||
| if (Y_commanded_movement_in_progress && tmc4361A_currentPosition(&tmc4361[y]) == Y_commanded_target_position && !is_homing_Y && !tmc4361A_isRunning(&tmc4361[y], stage_PID_enabled[y])) | ||
| { | ||
| Y_commanded_movement_in_progress = false; | ||
| mcu_cmd_execution_in_progress = false || X_commanded_movement_in_progress || Z_commanded_movement_in_progress || W_commanded_movement_in_progress; | ||
| us_since_last_check_position = interval_check_position + 1; | ||
| } | ||
| if (Z_commanded_movement_in_progress && tmc4361A_currentPosition(&tmc4361[z]) == Z_commanded_target_position && !is_homing_Z && !tmc4361A_isRunning(&tmc4361[z], stage_PID_enabled[z])) | ||
| { | ||
| Z_commanded_movement_in_progress = false; | ||
| mcu_cmd_execution_in_progress = false || X_commanded_movement_in_progress || Y_commanded_movement_in_progress || W_commanded_movement_in_progress; | ||
| us_since_last_check_position = interval_check_position + 1; | ||
| } | ||
| if (enable_filterwheel == true) { | ||
| if (W_commanded_movement_in_progress && tmc4361A_currentPosition(&tmc4361[w]) == W_commanded_target_position && !is_homing_W && !tmc4361A_isRunning(&tmc4361[w], stage_PID_enabled[w])) | ||
| { | ||
| W_commanded_movement_in_progress = false; | ||
| mcu_cmd_execution_in_progress = false || X_commanded_movement_in_progress || Y_commanded_movement_in_progress || Z_commanded_movement_in_progress; | ||
| us_since_last_check_position = interval_check_position + 1; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -2270,28 +2304,6 @@ void loop() { | |
| } | ||
| } | ||
|
|
||
| /*************************************************** | ||
|
|
||
| timer interrupt | ||
|
|
||
| ***************************************************/ | ||
|
|
||
| // timer interrupt | ||
| /* | ||
| // IntervalTimer stops working after SPI.begin() | ||
| void timer_interruptHandler() | ||
| { | ||
| SerialUSB.println("timer event"); | ||
| counter_send_pos_update = counter_send_pos_update + 1; | ||
| if(counter_send_pos_update==interval_send_pos_update/TIMER_PERIOD) | ||
| { | ||
| flag_send_pos_update = true; | ||
| counter_send_pos_update = 0; | ||
| SerialUSB.println("send pos update"); | ||
| } | ||
| } | ||
| */ | ||
|
|
||
| /***************************************************************************************************/ | ||
| /********************************************* utils *********************************************/ | ||
| /***************************************************************************************************/ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -329,19 +329,24 @@ def update_coordinates_dataframe(self, region_id, z_level, pos: squid.abc.Pos, f | |
|
|
||
| def move_to_coordinate(self, coordinate_mm): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe there's a reason I'm missing here, but this refactor duplicates code. Instead, I probably would do: This:
Also, since
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I would copy the code. |
||
| print("moving to coordinate", coordinate_mm) | ||
| x_mm = coordinate_mm[0] | ||
| self.stage.move_x_to(x_mm) | ||
| self._sleep(SCAN_STABILIZATION_TIME_MS_X / 1000) | ||
|
|
||
| y_mm = coordinate_mm[1] | ||
| self.stage.move_y_to(y_mm) | ||
| self._sleep(SCAN_STABILIZATION_TIME_MS_Y / 1000) | ||
|
|
||
| # check if z is included in the coordinate | ||
| if len(coordinate_mm) == 3: | ||
| x_mm = coordinate_mm[0] | ||
| y_mm = coordinate_mm[1] | ||
| z_mm = coordinate_mm[2] | ||
|
|
||
| self.stage.move_xy_to(x_mm, y_mm, blocking=False) | ||
| self.move_to_z_level(z_mm) | ||
|
|
||
| time.sleep(SCAN_STABILIZATION_TIME_MS_Y / 1000) | ||
| else: | ||
| x_mm = coordinate_mm[0] | ||
| y_mm = coordinate_mm[1] | ||
|
|
||
| self.stage.move_xy_to(x_mm, y_mm) | ||
| time.sleep(SCAN_STABILIZATION_TIME_MS_Y / 1000) | ||
|
|
||
| def move_to_z_level(self, z_mm): | ||
| print("moving z") | ||
| self.stage.move_z_to(z_mm) | ||
|
|
@@ -546,7 +551,6 @@ def acquire_camera_image( | |
| camera_illumination_time = self.camera.get_exposure_time() | ||
| if self.liveController.trigger_mode == TriggerMode.SOFTWARE: | ||
| self.liveController.turn_on_illumination() | ||
| self.wait_till_operation_is_completed() | ||
| camera_illumination_time = None | ||
| elif self.liveController.trigger_mode == TriggerMode.HARDWARE: | ||
| if "Fluorescence" in config.name and ENABLE_NL5 and NL5_USE_DOUT: | ||
|
|
@@ -614,7 +618,6 @@ def acquire_rgb_image(self, config, file_ID, current_path, current_round_images, | |
| if self.liveController.trigger_mode == TriggerMode.SOFTWARE: | ||
| # TODO(imo): use illum controller | ||
| self.liveController.turn_on_illumination() | ||
| self.wait_till_operation_is_completed() | ||
|
|
||
| # read camera frame | ||
| self.camera.send_trigger(illumination_time=self.camera.get_exposure_time()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -505,6 +505,10 @@ def __init__(self, serial_device: AbstractCephlaMicroSerial, reset_and_initializ | |
| self.log.debug("Resetting and initializing microcontroller.") | ||
| self.reset() | ||
| time.sleep(0.5) | ||
|
|
||
| # Need set the parameter before initialization | ||
| self.initizlize_sramp_of_axes() | ||
|
|
||
| if USE_SQUID_FILTERWHEEL: | ||
| self.init_filter_wheel() | ||
| time.sleep(0.5) | ||
|
|
@@ -561,6 +565,17 @@ def reset(self): | |
| # here. | ||
| self._cmd_id = 0 | ||
|
|
||
| def initizlize_sramp_of_axes(self): | ||
| # initialize axes sramp mode | ||
| self.set_sramp_mode(AXIS.X, X_AXIS_SRAMP_MODE) | ||
| self.wait_till_operation_is_completed() | ||
| self.set_sramp_mode(AXIS.Y, Y_AXIS_SRAMP_MODE) | ||
| self.wait_till_operation_is_completed() | ||
| self.set_sramp_mode(AXIS.Z, Z_AXIS_SRAMP_MODE) | ||
| self.wait_till_operation_is_completed() | ||
| self.set_sramp_mode(AXIS.W, W_AXIS_SRAMP_MODE) | ||
| self.wait_till_operation_is_completed() | ||
|
|
||
| def initialize_drivers(self): | ||
| self._cmd_id = 0 | ||
| cmd = bytearray(self.tx_buffer_length) | ||
|
|
@@ -684,6 +699,24 @@ def move_y_to_usteps(self, usteps): | |
| cmd[5] = payload & 0xFF | ||
| self.send_command(cmd) | ||
|
|
||
| def move_xy_to_usteps(self, xusteps, yusteps): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like we also added a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I will temporarily remove MOVE_XY from the firmware. |
||
| xpayload = self._int_to_payload(xusteps, 4) | ||
| ypayload = self._int_to_payload(yusteps, 4) | ||
|
|
||
| cmd = bytearray(self.tx_buffer_length) | ||
| cmd[1] = CMD_SET.MOVETO_XY | ||
| cmd[2] = xpayload >> 24 | ||
| cmd[3] = (xpayload >> 16) & 0xFF | ||
| cmd[4] = (xpayload >> 8) & 0xFF | ||
| cmd[5] = xpayload & 0xFF | ||
|
|
||
| cmd[6] = ypayload >> 24 | ||
| cmd[7] = (ypayload >> 16) & 0xFF | ||
| cmd[8] = (ypayload >> 8) & 0xFF | ||
| cmd[9] = ypayload & 0xFF | ||
|
|
||
| self.send_command(cmd) | ||
|
|
||
| def move_z_usteps(self, usteps): | ||
| self._move_axis_usteps(usteps, CMD_SET.MOVE_Z) | ||
|
|
||
|
|
@@ -961,6 +994,9 @@ def configure_squidfilter(self): | |
| self.wait_till_operation_is_completed() | ||
| self.set_max_velocity_acceleration(AXIS.W, MAX_VELOCITY_W_mm, MAX_ACCELERATION_W_mm) | ||
| self.wait_till_operation_is_completed() | ||
| # initialize axes sramp mode | ||
| self.set_sramp_mode(AXIS.W, W_AXIS_SRAMP_MODE) | ||
| self.wait_till_operation_is_completed() | ||
|
|
||
| def ack_joystick_button_pressed(self): | ||
| cmd = bytearray(self.tx_buffer_length) | ||
|
|
@@ -994,6 +1030,13 @@ def set_pin_level(self, pin, level): | |
| cmd[3] = level | ||
| self.send_command(cmd) | ||
|
|
||
| def set_sramp_mode(self, axis, mode): | ||
| cmd = bytearray(self.tx_buffer_length) | ||
| cmd[1] = CMD_SET.SET_SRAMP_MOD | ||
| cmd[2] = axis | ||
| cmd[3] = mode | ||
| self.send_command(cmd) | ||
|
|
||
| def turn_on_AF_laser(self): | ||
| self.set_pin_level(MCU_PINS.AF_LASER, 1) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,6 +107,18 @@ def move_y_to(self, abs_mm: float, blocking: bool = True): | |
| self._calc_move_timeout(abs_mm - self.get_pos().y_mm, self.get_config().Y_AXIS.MAX_SPEED) | ||
| ) | ||
|
|
||
| def move_xy_to(self, x_abs_mm: float, y_abs_mm: float, blocking: bool = True): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have an To do this change properly, we should:
Test, or find someone that can help you test, the new code on all the stages we support. We should not merge this before doing all of the above since it will break some customers!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I would add an @AbstractMethod called move_xy_to to AbstractStage. Would you please ask somebody do the implementation of move_xy_to in to prior_stages? @hongquanli
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or I just add an empty function in prior_stages? |
||
| self._microcontroller.move_xy_to_usteps( | ||
| self._config.X_AXIS.convert_real_units_to_ustep(x_abs_mm), | ||
| self._config.Y_AXIS.convert_real_units_to_ustep(y_abs_mm), | ||
| ) | ||
|
|
||
| if blocking: | ||
| x_timeout = self._calc_move_timeout(x_abs_mm - self.get_pos().x_mm, self.get_config().X_AXIS.MAX_SPEED) | ||
| y_timeout = self._calc_move_timeout(y_abs_mm - self.get_pos().y_mm, self.get_config().Y_AXIS.MAX_SPEED) | ||
|
|
||
| self._microcontroller.wait_till_operation_is_completed(max(x_timeout, y_timeout)) | ||
|
|
||
| def move_z_to(self, abs_mm: float, blocking: bool = True): | ||
| # From Hongquan, we want the z axis to rest on the "up" (wrt gravity) direction of gravity. So if we | ||
| # are moving in the negative (down) z direction, we need to move past our mark a bit then | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we don't initialize this in
tmc4361A_init, which means I think we default it to 0. To preserve the original behavior it'd be good to settmc4361a->ramp_mode = TMC4361_RAMP_SSHAPin the init.I know we set the ramp mode in our
.pyimplementation, but you generally don't want to assume other pieces of code will do the right thing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I agree with you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@veerwang can you implement the suggested change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I would do it.