diff --git a/.travis.yml b/.travis.yml index 37f24faf..6efeeeb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,9 +31,10 @@ install: - if [ ! -e $GCC_DIR/bin/arm-none-eabi-gcc ]; then wget $GCC_URL -O $GCC_ARCHIVE; tar xfj $GCC_ARCHIVE -C $HOME/dl; fi - export PATH=$PATH:$GCC_DIR/bin -- export TUP_DIR=$HOME/dl/tup_0.7.5-0~16.04.york0_amd64 -- export TUP_ARCHIVE=$HOME/dl/tup_0.7.5-0~16.04.york0_amd64.deb -- export TUP_URL=http://ppa.launchpad.net/jonathonf/tup/ubuntu/pool/main/t/tup/tup_0.7.8-2~16.04.york0_amd64.deb +- export TUP_VER=tup_0.7.8-3~16.04.york0_amd64 +- export TUP_DIR=$HOME/dl/$TUP_VER +- export TUP_ARCHIVE=$HOME/dl/$TUP_VER.deb +- export TUP_URL=http://ppa.launchpad.net/jonathonf/tup/ubuntu/pool/main/t/tup/$TUP_VER.deb - if [ ! -e $TUP_DIR/bin/tup ]; then wget $TUP_URL -O $TUP_ARCHIVE; dpkg-deb -R $TUP_ARCHIVE $TUP_DIR; fi - export PATH=$PATH:$TUP_DIR/usr/bin diff --git a/CHANGELOG.md b/CHANGELOG.md index 8853810f..c9e8c24e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Unreleased Features Please add a note of your changes below this heading if you make a Pull Request. +### Added +* Check current limit violation: added `ERROR_CURRENT_UNSTABLE`, `motor.config.current_lim_tolerance`. + +# Releases +## [0.4.10] - 2019-04-24 +### Fixed +* Index search would trigger in the wrong place. + +## [0.4.9] - 2019-04-23 ### Added * A release target for ODrive v3.6 * Communication watchdog feature. @@ -18,7 +27,6 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Fixed * Encoder index interrupts now disabled when not searching -# Releases ## [0.4.8] - 2019-02-25 ### Added * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. diff --git a/Firmware/.clang-format b/Firmware/.clang-format new file mode 100644 index 00000000..eac50873 --- /dev/null +++ b/Firmware/.clang-format @@ -0,0 +1,7 @@ +--- +BasedOnStyle: Google +AlignConsecutiveAssignments: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +IndentWidth: '4' + +... diff --git a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h b/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h index bb89fbba..3e522777 100644 --- a/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h +++ b/Firmware/Board/v3/Drivers/CMSIS/Include/cmsis_gcc.h @@ -161,7 +161,7 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) */ __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) { - __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); + __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : ); } @@ -187,7 +187,7 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) */ __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) { - __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); + __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : ); } diff --git a/Firmware/Board/v3/Src/gpio.c b/Firmware/Board/v3/Src/gpio.c index 571dce7a..08aa1450 100644 --- a/Firmware/Board/v3/Src/gpio.c +++ b/Firmware/Board/v3/Src/gpio.c @@ -235,6 +235,8 @@ bool GPIO_subscribe(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin, GPIO_InitStruct.Pull = pull_up_down; HAL_GPIO_Init(GPIO_port, &GPIO_InitStruct); + // Clear any previous triggers + __HAL_GPIO_EXTI_CLEAR_IT(GPIO_pin); // Enable interrupt HAL_NVIC_SetPriority(get_irq_number(GPIO_pin), 0, 0); HAL_NVIC_EnableIRQ(get_irq_number(GPIO_pin)); diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index b8995b3c..97d97170 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -7,7 +7,8 @@ #include "utils.h" #include "communication/interface_can.hpp" -Axis::Axis(const AxisHardwareConfig_t& hw_config, +Axis::Axis(int axis_num, + const AxisHardwareConfig_t& hw_config, Config_t& config, Encoder& encoder, SensorlessEstimator& sensorless_estimator, @@ -16,7 +17,8 @@ Axis::Axis(const AxisHardwareConfig_t& hw_config, TrapezoidalTrajectory& trap, Endstop& min_endstop, Endstop& max_endstop) - : hw_config_(hw_config), + : axis_num_(axis_num), + hw_config_(hw_config), config_(config), encoder_(encoder), sensorless_estimator_(sensorless_estimator), diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index a5fcb049..7ce82ace 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -95,7 +95,8 @@ public: LOCKIN_STATE_CONST_VEL, }; - Axis(const AxisHardwareConfig_t& hw_config, + Axis(int axis_num, + const AxisHardwareConfig_t& hw_config, Config_t& config, Encoder& encoder, SensorlessEstimator& sensorless_estimator, @@ -205,6 +206,7 @@ public: void run_state_machine_loop(); + int axis_num_; const AxisHardwareConfig_t& hw_config_; Config_t& config_; diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index d79d2b2e..c67dd228 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -128,6 +128,7 @@ bool Encoder::run_index_search() { if (!config_.idx_search_unidirectional && axis_->motor_.config_.direction == 0) { axis_->motor_.config_.direction = 1; } + set_idx_subscribe(); bool orig_finish_on_enc_idx = axis_->config_.lockin.finish_on_enc_idx; axis_->config_.lockin.finish_on_enc_idx = true; diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 3f39b1b6..17707eaa 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -192,7 +192,7 @@ int odrive_main(void) { TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]); Endstop *min_endstop = new Endstop(min_endstop_configs[i]); Endstop *max_endstop = new Endstop(max_endstop_configs[i]); - axes[i] = new Axis(hw_configs[i].axis_config, axis_configs[i], + axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i], *encoder, *sensorless_estimator, *controller, *motor, *trap, *min_endstop, *max_endstop); } diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 6fa01cc8..2a9cd703 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -342,6 +342,7 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_pha if (fabsf(current_meas_.phB) > ictrl.overcurrent_trip_level || fabsf(current_meas_.phC) > ictrl.overcurrent_trip_level) { set_error(ERROR_CURRENT_SENSE_SATURATION); + return false; } // Clarke transform @@ -356,6 +357,13 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_pha ictrl.Iq_measured += ictrl.I_measured_report_filter_k * (Iq - ictrl.Iq_measured); ictrl.Id_measured += ictrl.I_measured_report_filter_k * (Id - ictrl.Id_measured); + // Check for violation of current limit + float I_trip = config_.current_lim_tolerance * effective_current_lim(); + if (SQ(Id) + SQ(Iq) > SQ(I_trip)) { + set_error(ERROR_CURRENT_UNSTABLE); + return false; + } + // Current error float Ierr_d = Id_des - Id; float Ierr_q = Iq_des - Iq; diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 279e6848..d31985ee 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -22,7 +22,8 @@ public: ERROR_BRAKE_DEADTIME_VIOLATION = 0x0100, ERROR_UNEXPECTED_TIMER_CALLBACK = 0x0200, ERROR_CURRENT_SENSE_SATURATION = 0x0400, - ERROR_INVERTER_OVER_TEMP = 0x0800 + ERROR_INVERTER_OVER_TEMP = 0x0800, + ERROR_CURRENT_UNSTABLE = 0x1000 }; enum MotorType_t { @@ -68,6 +69,7 @@ public: // Read out max_allowed_current to see max supported value for current_lim. // float current_lim = 70.0f; //[A] float current_lim = 10.0f; //[A] + float current_lim_tolerance = 1.25f; // multiple of current_lim // Value used to compute shunt amplifier gains float requested_current_range = 60.0f; // [A] float current_control_bandwidth = 1000.0f; // [rad/s] @@ -227,6 +229,7 @@ public: make_protocol_property("direction", &config_.direction), make_protocol_property("motor_type", &config_.motor_type), make_protocol_property("current_lim", &config_.current_lim), + make_protocol_property("current_lim_tolerance", &config_.current_lim_tolerance), make_protocol_property("inverter_temp_limit_lower", &config_.inverter_temp_limit_lower), make_protocol_property("inverter_temp_limit_upper", &config_.inverter_temp_limit_upper), make_protocol_property("requested_current_range", &config_.requested_current_range), diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 498d5172..c1f3cc68 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -976,17 +976,17 @@ public: output_properties_.register_endpoints(list, id + 1 + decltype(input_properties_)::endpoint_count, length); } - template std::enable_if_t + template std::enable_if_t handle_ex() { invoke_function_with_tuple(*obj_, func_ptr_, in_args_); } - template std::enable_if_t + template std::enable_if_t handle_ex() { std::get<0>(out_args_) = invoke_function_with_tuple(*obj_, func_ptr_, in_args_); } - template std::enable_if_t= 2> + template std::enable_if_t= 2> handle_ex() { out_args_ = invoke_function_with_tuple(*obj_, func_ptr_, in_args_); } @@ -997,7 +997,7 @@ public: (void) output; LOG_FIBRE("tuple still at %x and of size %u\r\n", (uintptr_t)&in_args_, sizeof(in_args_)); LOG_FIBRE("invoke function using %d and %.3f\r\n", std::get<0>(in_args_), std::get<1>(in_args_)); - handle_ex(); + handle_ex(); } const char * name_; diff --git a/docs/_data/index.yaml b/docs/_data/index.yaml index fabd32ee..3ca1d28a 100644 --- a/docs/_data/index.yaml +++ b/docs/_data/index.yaml @@ -17,6 +17,8 @@ sections: url: encoders - title: Control url: control + - title: Hoverboard Guide + url: hoverboard - title: Troubleshooting url: troubleshooting - title: For ODrive Developers @@ -32,4 +34,4 @@ sections: - title: Motor Guide url: https://docs.google.com/spreadsheets/d/12vzz7XVEK6YNIOqH0jAz51F5VUpc-lJEs3mmkWP1H4Y - title: Encoder Guide - url: https://docs.google.com/spreadsheets/d/1OBDwYrBb5zUPZLrhL98ezZbg94tUsZcdTuwiVNgVqpU \ No newline at end of file + url: https://docs.google.com/spreadsheets/d/1OBDwYrBb5zUPZLrhL98ezZbg94tUsZcdTuwiVNgVqpU diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index a0757e2f..db6869a8 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -136,4 +136,4 @@ Not all parameters can be accessed via the ASCII protocol but at least all param #### System commands: * `ss` - Save config * `se` - Erase config -* `sr` - Reboot +* `sb` - Reboot diff --git a/docs/commands.md b/docs/commands.md index e164b884..e6ddaa09 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -68,9 +68,9 @@ Possible values are: ### Tuning parameters The motion control gains are currently manually tuned: -* `.controller.config.pos_gain = 20.0f` [(counts/s) / counts] -* `.controller.config.vel_gain = 5.0f / 10000.0f` [A/(counts/s)] -* `.controller.config.vel_integrator_gain = 10.0f / 10000.0f` [A/((counts/s) * s)] +* `.controller.config.pos_gain = 20.0` [(counts/s) / counts] +* `.controller.config.vel_gain = 5.0 / 10000.0` [A/(counts/s)] +* `.controller.config.vel_integrator_gain = 10.0 / 10000.0` [A/((counts/s) * s)] An upcoming feature will enable automatic tuning. Until then, here is a rough tuning procedure: * Set the integrator gain to 0 @@ -124,3 +124,7 @@ odrv0.axis0.controller.vel_setpoint = 400 odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / ( * ) ``` +To start the motor: +``` +.requested_state = AXIS_STATE_SENSORLESS_CONTROL +``` diff --git a/docs/configuring-vscode.md b/docs/configuring-vscode.md index bfb3261b..572f9933 100644 --- a/docs/configuring-vscode.md +++ b/docs/configuring-vscode.md @@ -2,7 +2,7 @@ VSCode is the recommended IDE for working with the ODrive codebase. It is a light-weight text editor with Git integration and GDB debugging functionality. -Before doing the VSCode setup, make sure you've installed all of your [prerequisites](README.md#installing-prerequisites) +Before doing the VSCode setup, make sure you've installed all of your [prerequisites](developer-guide#installing-prerequisites) ## Setup Procedure 1. Clone the ODrive repository @@ -32,7 +32,7 @@ A terminal window will open with your native shell. VSCode is configured to run A terminal window will open with your native shell. VSCode is configured to run the command `make flash` in this terminal. -If the flashing worked, you can connect to the board using the [odrivetool](../docs/getting-started#start-odrivetool). +If the flashing worked, you can connect to the board using the [odrivetool](getting-started#start-odrivetool). ## Debugging An extension called Cortex-Debug has recently been released which is designed specifically for debugging ARM Cortex projects. You can read more on Cortex-Debug here: https://github.com/Marus/cortex-debug diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 04671b48..05f7af67 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -85,8 +85,9 @@ Some instructions in this document may assume that you're using a bash command p * __Note 1__: After installing, create an environment variable named `ARM_GCC_ROOT` whose value is the path you installed to. e.g. `C:\Program Files (x86)\GNU Tools Arm Embedded\7 2018-q2-update`. This variable is used to locate include files for the c/c++ Visual Studio Code extension. * __Note 2__: 8-2018-q4-major seems to have a bug on Windows. Please use 7-2018-q2-update. * [Tup](http://gittup.org/tup/index.html) -* [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm) -* [OpenOCD](http://gnuarmeclipse.github.io/openocd/install/). Also follow the instructions on the ST-LINK/V2 drivers. +* [GNU MCU Eclipse's Windows Build Tools](https://github.com/gnu-mcu-eclipse/windows-build-tools/releases) +* [OpenOCD](http://gnuarmeclipse.github.io/openocd/install/). +* [ST-Link/V2 Drivers](http://www.st.com/web/en/catalog/tools/FM147/SC1887/PF260219)
@@ -136,6 +137,8 @@ Example usage: `./run_tests.py --test-rig-yaml ../tools/test-rig-parallel.yaml`

## Debugging +If you're using VSCode, make sure you have the Cortex Debug extension, OpenOCD, and the STLink. You can verify that OpenOCD and STLink are working by ensuring you can flash code. Open the ODrive_Workspace.code-workspace file, and start a debugging session (F5). VSCode will pick up the correct settings from the workspace and automatically connect. Breakpoints can be added graphically in VSCode. + * Run `make gdb`. This will reset and halt at program start. Now you can set breakpoints and run the program. If you know how to use gdb, you are good to go.

diff --git a/docs/encoders.md b/docs/encoders.md index 04cf9663..c3d42fb0 100644 --- a/docs/encoders.md +++ b/docs/encoders.md @@ -1,15 +1,18 @@ # Encoders ## Known and Supported Encoders -Check out the [ODrive Encoder Guide](https://docs.google.com/spreadsheets/d/1OBDwYrBb5zUPZLrhL98ezZbg94tUsZcdTuwiVNgVqpU). +Be sure to read the [ODrive Encoder Guide](https://docs.google.com/spreadsheets/d/1OBDwYrBb5zUPZLrhL98ezZbg94tUsZcdTuwiVNgVqpU). ## Encoder Calibration - -All encoder types that are currently supported require the ODrive to do some sort of encoder calibration at every startup before you can run the motor control. Take this into account when designing your application. - +Please take into account that all encoder types supported by ODrive require that you do some sort of encoder calibration. This requires the following: +* Selecting an encoder and mounting it to your motor +* Choosing an interface (e.g., AB, ABI or SPI) +* Connecting the pins to the odrive +* Loading the correct odrive firmware (the default will work in many cases) +* Motor calibration +* Saving the settings in the odrive for correct bootup ### Encoder without index signal - During encoder offset calibration the rotor must be allowed to rotate without any biased load during startup. That means mass and weak friction loads are fine, but gravity or spring loads are not okay. In the `odrivetool`, type `.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION` Enter. @@ -37,5 +40,115 @@ Below are the steps to do the one-time calibration and configuration. Note that That's it, now on every reboot the motor will turn in one direction until it finds the encoder index. -* If you wish to scan for the index pulse in the other direction (if for example your axis usually starts close to a hard-stop), you can set a negative value in `.encoder.config.idx_search_speed`. +* If you wish to scan for the index pulse in the other direction, that feature is currently undocumented. * If your motor has problems reaching the index location due to the mechanical load, you can increase `.motor.config.calibration_current`. + +*IMPORTANT:* Your motor should find the same rotational position when the ODrive performs an index search if the index signal is working properly. This means that the motor should spin, and stop at the same position if you have set .config.startup_encoder_index_search so the search starts on reboot, or you if call the command:.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH after reboot. You can test this. Send the reboot() command, and while it's rebooting turn your motor, then make sure the motor returns back to the correct position each time when it comes out of reboot. Try this procedure a couple of times to be sure. + +### Startup sequence notes +The following are variables that MUST be set up for your encoder configuration. Your values will vary depending on your encoder: + +* `.encoder.config.cpr = 8192` +* `.encoder.config.mode = ENCODER_MODE_INCREMENTAL` + +The following are examples of values that MAY impact the success of calibration. These are not all the varibles you have to set for startup. Only change these when you understand why they are needed; your values will vary depending on your setup: +* `.motor.config.motor_type = MOTOR_TYPE_HIGH_CURRENT` select if you have a gimbal or high amp motor +* `.encoder.config.calib_range = 0.05` helps to relax the accuracy of encoder counts during calibration +* `.motor.config.calibration_current = 10.0` _sometimes_ needed if this is a large motor +* `.motor.config.resistance_calib_max_voltage = 12.0` _sometimes_ needed depending on motor +* `.controller.config.vel_limit = 50000` low values result in the spinning motor stopping abruptly during calibration + +Lots of other values can get you. It's a process. Thankfully there is a lot of good people that will help you debug calibration problems. + +If calibration works, congratulations. + +Now try: +* `.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` +* `.controller.set_vel_setpoint(3000,0) ` +let it loop a few times and then set: +* `.requested_state = AXIS_STATE_IDLE` + +Do you still have no errors? Awesome. Now save the calibration, you can set the below. Note that this only works if you are using an absolute encoder or the encoder index input (see "Encoder with index signal" above). +* `.encoder.config.pre_calibrated = True` +* `.motor.config.pre_calibrated = True ` + +And see if ODrive agrees that calibration worked by just running +* `.encoder.config.pre_calibrated` + +(using no "= True" ). Make sure that 'pre_calibrated' is in fact True. + +Also, if you have calibrated and encoder.pre_calibrated is equal to true, and you had no errors so far. Run this: +* `odrv0.save_configuration()` +* `odrv0.reboot()` + +and now see if after a reboot you can run: +* `.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH` + +without getting errors. + +## What happens if calibration fails +There are subtle ways that encoder problems will impact your ODrive. For example, ODrive may not complete the calibrate sequence when you go to: +* `.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE` + +Or, ODrive may complete the calibrate sequence after: +* `.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE` + +but then it fails after you go to: +* `.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` + +Or ODrive may just vibrate in an entertaining way. See: +https://www.youtube.com/watch?v=gaRUmwvSyAs + +## Encoder Testing +There are things you can test to make sure your encoder is properly connected. +This run the command: +* `.encoder.shadow_count ` + +and look at your value. Then turn your motor by hand and see if that value changes. Also, notice that the command: +* `.encoder.config.cpr = 4000` + +must reflect the number of counts odrive receives after one complete turn of the motor. So use shadow_count to test if that is working properly. + +You will probably never be able to properly debug if you have problems unless you use an oscilloscope. If you have one, try the following: +Connect to the AB pins, see if you get square waves as you turn the motor. +Connect to the I pin, see if you get a pulse on a complete rotation. Sometimes this is hard to see. +If you are using SPI, have a lot at the signal on the CLK, and CS pins. There are many examples on the net for how these should behave. + +## Encoder Noise +Noise is found in all circuits, life is just about figuring out if it is preventing your system from working. Lots of users have no problems with noise interferring with their odrive operation, others will tell you "_I've been using the same encoder as you with no problems_". Power to 'em, that may be true, but it doesn't mean it will work for you. If you are concerned about noise, there are several possible sources: + +* Importantly, encoder wires may be too close to motor wires, avoid overlap as much as possible +* Long wires between encoder and ODrive +* Use of ribbon cable + +The following _might_ mitigate noise problems. Use shielded cable, or use twisted pairs, where one side of each twisted pair is tied to ground, the other side is tied to your signal. If you are using SPI, use a 20-50 ohm resistor in series on CLK, which is more susceptable noise. + +If you are using an encoder with an index signal, another problem that has been encountered is with noise on the Z input of ODrive. Symptoms for this problem include: +* difficulty with requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE, where your calibration sequence may not complete +* strange behavior after performing odrv0.save_configuration() and odrv0.reboot() +* when performing an index_search, the motor does not return to the same position each time. +One easy step that _might_ fix the noise on the Z input has been to solder a 22nF-47nF capacitor to the Z pin and the GND pin on the underside of the ODrive board. + +## AS5047/AS5048 Encoders +The AS5047/AS5048 encoders are Hall Effect/Magenetic sensors that can serve as rotary encoders for the ODrive. + +The AS5047 has 3 independent output interfaces: SPI, ABI, and PWM. +The AS5048 has 4 independent output interfaces: SPI, ABI, I2C, and PWM. + +Both chips come with evaluation boards that can simplify mounted the chips to your motor. For our purposes if you are using an evaluation board you should select the settings for 3.3v, and tie MOSI high to 3.3v. + +If you are having calibration problems - make sure your magnet is centered on the axis of rotation on the motor, some users report this has a significant impact on calibration. Also make sure your magnet height is within range of the spec sheet. + +#### Using ABI. +You can use ABI with the AS5047/AS5048 with the default ODrive firmware. For your wiring, connect A, B, 3.3v, GND to the labeled pins on the odrive +The acronym I and Z mean the same thing, connect those as well if you are using an index signal. + +#### Using SPI. +TobinHall has written a [branch](https://github.com/TobinHall/ODrive/tree/Non-Blocking_Absolute_SPI) that supports the SPI option on the AS5047/AS5048. Use his build to flash firmware on your ODrive and connect MISO, SCK, and CS to the labeled pins on the odrive + +Tie MOSI to 3.3v, connect to the SCK, CLK, MISO, GND and 3.2v pins on the ODrive. (note for SPI users, the acronym SCK and CLK mean the same thing, the acronym CSn and CS mean the same thing.) + +Add these commands to your calibration / startup script: +* `.encoder.config.abs_spi_cs_gpio_pin = 4` or which ever GPIO pin you choose +* `.encoder.config.mode = 257` +* `.axis0.encoder.config.cpr = 2**14` diff --git a/docs/interfaces.md b/docs/interfaces.md index 3f5a7dbd..627c54c2 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -49,6 +49,9 @@ For predictable results, try to have only one feature enabled for any one pin. W * `odrv0.save_configuration()` * `odrv0.reboot()` +### Analog input +Analog inputs can be used to measure voltages between 0 and 3.3V. Odrive uses a 12 bit ADC (4096 steps) and so has a maximum resolution of 0.8 mV. Some GPIO pins require the appropriate pin priority (see above) to be set before they can be used as an analog input. To read the voltage on GPIO1 in odrive tool the following would be entered: `odrv0.get_adc_voltage(1)` + ### Hall feedback pinout When the encoder mode is set to hall feedback, the pinout on the encoder port is as follows: diff --git a/docs/protocol.md b/docs/protocol.md index f916b1fe..6e8809d6 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -59,10 +59,10 @@ __Response__ The stream based format is just a wrapper for the packet format. - __Byte 0__ Sync byte `0xAA` - - __Bytes 1, 2__ Packet length + - __Byte 1__ Packet length - Currently both parties shall only emit and accept values of 0 through 127. - - __Bytes 3__ CRC8 of bytes 0 through 2 + - __Byte 2__ CRC8 of bytes 0 and 1 - See protocol.hpp for CRC details. - - __Bytes 4 to N-3__ Packet + - __Bytes 3 to N-3__ Packet - __Bytes N-2, N-1__ CRC16 - See protocol.hpp for CRC details. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 7795ed00..5b2ff466 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -7,6 +7,7 @@ Table of Contents: - [Common Axis Errors](#common-axis-errors) - [Common Motor Errors](#common-motor-errors) - [Common Encoder Errors](#common-encoder-errors) +- [Common Controller Errors](#common-controller-errors) - [USB Connectivity Issues](#usb-connectivity-issues) - [Firmware Issues](#firmware-issues) - [Other issues that may not produce an error code](#other-issues-that-may-not-produce-an-error-code) @@ -33,13 +34,13 @@ You tried to run a state before you are allowed to. Typically you tried to run e Confirm that your power leads are connected securely. For initial testing a 12V PSU which can supply a couple of amps should be sufficient while the use of low current 'wall wart' plug packs may lead to inconsistent behaviour and is not recommended. -You can monitor your PUS voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If you see your votlage drop below ~ 8V then you will trip this error. Even a relatively small motor can draw multiple kW momentary and so unless you have a very large PSU or are running of a battery you may encounter this error when executing high speed movements with a high current limit. To limit your PSU power draw you can limit your motor current and/or velocity limit `odrv0.axis0.controller.config.vel_limit` and `odrv0.axis0.motor.config.current_lim`. +You can monitor your PSU voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If you see your votlage drop below ~ 8V then you will trip this error. Even a relatively small motor can draw multiple kW momentary and so unless you have a very large PSU or are running of a battery you may encounter this error when executing high speed movements with a high current limit. To limit your PSU power draw you can limit your motor current and/or velocity limit `odrv0.axis0.controller.config.vel_limit` and `odrv0.axis0.motor.config.current_lim`. * `ERROR_DC_BUS_OVER_VOLTAGE = 0x04` Confirm that you have a brake resistor of the correct value connected securly and that `odrv0.config.brake_resistance` is set to the value of your brake resistor. -You can monitor your PUS voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If during a move you see the voltage rise above your PSU's nominal set voltage then you have your brake resistance set too low. This may happen if you are using long wires or small gauge wires to connect your brake resistor to your odrive which will added extra resistance. This extra resistance needs to be accounted for to prevent this voltage spike. If you have checked all your connections you can also try increasing your brake resistance by ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake resistor value. +You can monitor your PSU voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If during a move you see the voltage rise above your PSU's nominal set voltage then you have your brake resistance set too low. This may happen if you are using long wires or small gauge wires to connect your brake resistor to your odrive which will added extra resistance. This extra resistance needs to be accounted for to prevent this voltage spike. If you have checked all your connections you can also try increasing your brake resistance by ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake resistor value. ## Common Motor Errors @@ -76,9 +77,10 @@ To resolve this issue you can limit the M0 current to 40A. The lowest current at * `ERROR_MODULATION_MAGNITUDE = 0x0080` -The bus voltage was insufficent to push the requested current through the motor. Reduce `motor.config.calibration_current` and/or `motor.config.current_lim`, for errors at calibration-time and closed loop control respectively. +The bus voltage was insufficent to push the requested current through the motor. +If you are getting this during motor calibration, make sure that `motor.config.resistance_calib_max_voltage` is no more than half your bus voltage. -For gimbal motors, it is recommended to set the calibration_current and current_lim to half your bus voltage, or less. +For gimbal motors, it is recommended to set the `motor.config.calibration_current` and `motor.config.current_lim` to half your bus voltage, or less. ## Common Encoder Errors @@ -94,6 +96,13 @@ Confirm that your encoder is plugged into the right pins on the odrive board. Check that your encoder is a model that has an index pulse. If your encoder does not have a wire connected to pin Z on your odrive then it does not output an index pulse. +## Common Controller Errors + +* `ERROR_OVERSPEED = 0x01` + +Try increasing `.controller.config.vel_limit`. The default `vel_limit` of 20,000 encoder counts per second gives a motor speed of only ~146 RPM with the common CUI-AMT102 8192 count per rotation encoder. Note: Even if you do not commanded your motor to exceed `vel_limit` sudden changes in the load placed on a motor may cause this speed to be temporarily exceeded, resulting in this error. + +You can also try increasing `.controller.config.vel_limit_tolerance`. The default value of 1.2 means it will only allow a 20% violation of the speed limit. You can set the `vel_limit_tolerance` to 0 to disable the check altogether. ## USB Connectivity Issues @@ -140,6 +149,7 @@ Check that your encoder is a model that has an index pulse. If your encoder does ### Motor feels like it has less torque than it should and/or gets hot sitting still while under no load. - Encoder has likely slipped causing the motor controller to commutate the wrong windings slightly which reduces output torque and produces excess heat as the motor 'fights itself'. +- This can also be caused if the rotor bell slips on the motor shaft. On some motors the rotor bell is secured against the shaft with a grub screw. Confirm that this screw is tight enough. For further details on how to resolve this issue see [this forum post](https://discourse.odriverobotics.com/t/motor-gets-hot-has-less-torque-in-one-direction-than-the-other/2394). ### False steps or direction changes when using step/dir - Prior to Odrive board V3.5 no filtering is present on the GPIO pins used for step/dir interface and so inductively coupled noise may causes false steps to be detected. Odrive V3.5 and has onboard filtering to resolve this issue. diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 17d26f9f..955458b5 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -43,6 +43,7 @@ class errors: ERROR_BRAKE_DEADTIME_VIOLATION = 0x0100 ERROR_UNEXPECTED_TIMER_CALLBACK = 0x0200 ERROR_CURRENT_SENSE_SATURATION = 0x0400 + ERROR_CURRENT_UNSTABLE = 0x1000 class encoder: ERROR_NONE = 0