mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-11 05:47:38 +08:00
Merge branch 'devel' into input-filter
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
Please add a note of your changes below this heading if you make a Pull Request.
|
||||
|
||||
### Added
|
||||
* `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors.
|
||||
* Second order setpoint input filter.
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -271,8 +271,10 @@ bool Encoder::update() {
|
||||
if (delta_enc > 3)
|
||||
delta_enc -= 6;
|
||||
} else {
|
||||
set_error(ERROR_ILLEGAL_HALL_STATE);
|
||||
return false;
|
||||
if (!config_.ignore_illegal_hall_state) {
|
||||
set_error(ERROR_ILLEGAL_HALL_STATE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
float offset_float = 0.0f; // Sub-count phase alignment offset
|
||||
float calib_range = 0.02f;
|
||||
float bandwidth = 1000.0f;
|
||||
bool ignore_illegal_hall_state = false;
|
||||
};
|
||||
|
||||
Encoder(const EncoderHardwareConfig_t& hw_config,
|
||||
@@ -106,7 +107,8 @@ public:
|
||||
make_protocol_property("offset_float", &config_.offset_float),
|
||||
make_protocol_property("bandwidth", &config_.bandwidth,
|
||||
[](void* ctx) { static_cast<Encoder*>(ctx)->update_pll_gains(); }, this),
|
||||
make_protocol_property("calib_range", &config_.calib_range)
|
||||
make_protocol_property("calib_range", &config_.calib_range),
|
||||
make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -713,4 +713,33 @@ void pwm_in_cb(int channel, uint32_t timestamp) {
|
||||
last_timestamp[gpio_num - 1] = timestamp;
|
||||
last_pin_state[gpio_num - 1] = current_pin_state;
|
||||
last_sample_valid[gpio_num - 1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Analog speed control input */
|
||||
|
||||
static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio)
|
||||
{
|
||||
float fraction = get_adc_voltage(get_gpio_port_by_pin(gpio), get_gpio_pin_by_pin(gpio)) / 3.3f;
|
||||
float value = map->min + (fraction * (map->max - map->min));
|
||||
get_endpoint(map->endpoint)->set_from_float(value);
|
||||
}
|
||||
|
||||
static void analog_polling_thread(void *)
|
||||
{
|
||||
while (true) {
|
||||
for (int i = 0; i < GPIO_COUNT; i++) {
|
||||
struct PWMMapping_t *map = &board_config.analog_mappings[i];
|
||||
|
||||
if (is_endpoint_ref_valid(map->endpoint))
|
||||
update_analog_endpoint(map, i + 1);
|
||||
}
|
||||
osDelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void start_analog_thread()
|
||||
{
|
||||
osThreadDef(thread_def, analog_polling_thread, osPriorityLow, 0, 4*512);
|
||||
osThreadCreate(osThread(thread_def), NULL);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ void sync_timers(TIM_HandleTypeDef* htim_a, TIM_HandleTypeDef* htim_b,
|
||||
void start_general_purpose_adc();
|
||||
float get_adc_voltage(GPIO_TypeDef* GPIO_port, uint16_t GPIO_pin);
|
||||
void pwm_in_init();
|
||||
void start_analog_thread();
|
||||
|
||||
void update_brake_current();
|
||||
|
||||
|
||||
@@ -214,6 +214,8 @@ int odrive_main(void) {
|
||||
axes[i]->start_thread();
|
||||
}
|
||||
|
||||
start_analog_thread();
|
||||
|
||||
system_stats_.fully_booted = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ struct BoardConfig_t {
|
||||
//<! the brake power if the brake resistor is disabled.
|
||||
//<! The default is 26V for the 24V board version and 52V for the 48V board version.
|
||||
PWMMapping_t pwm_mappings[GPIO_COUNT];
|
||||
PWMMapping_t analog_mappings[GPIO_COUNT];
|
||||
};
|
||||
extern BoardConfig_t board_config;
|
||||
extern bool user_config_loaded_;
|
||||
|
||||
@@ -163,8 +163,11 @@ static inline auto make_obj_tree() {
|
||||
make_protocol_object("gpio2_pwm_mapping", make_protocol_definitions(board_config.pwm_mappings[1])),
|
||||
make_protocol_object("gpio3_pwm_mapping", make_protocol_definitions(board_config.pwm_mappings[2])),
|
||||
#endif
|
||||
make_protocol_object("gpio4_pwm_mapping", make_protocol_definitions(board_config.pwm_mappings[3]))
|
||||
),
|
||||
make_protocol_object("gpio4_pwm_mapping", make_protocol_definitions(board_config.pwm_mappings[3])),
|
||||
|
||||
make_protocol_object("gpio3_analog_mapping", make_protocol_definitions(board_config.analog_mappings[2])),
|
||||
make_protocol_object("gpio4_analog_mapping", make_protocol_definitions(board_config.analog_mappings[3]))
|
||||
),
|
||||
make_protocol_object("axis0", axes[0]->make_protocol_definitions()),
|
||||
make_protocol_object("axis1", axes[1]->make_protocol_definitions()),
|
||||
make_protocol_object("can", can1_ctx.make_protocol_definitions()),
|
||||
|
||||
+2
-14
@@ -14,21 +14,9 @@ Table of Contents:
|
||||
<!-- /TOC -->
|
||||
|
||||
## Error codes
|
||||
If your ODrive is not working as expected, run `odrivetool` and type `hex(<axis>.error)` <kbd>Enter</kbd> where `<axis>` is the axis that isn't working. This will display a [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) representation of the error code. Each bit represents one error flag.
|
||||
|
||||
<details><summary markdown="span">Example</summary><div markdown="block">
|
||||
Say you got this error output:
|
||||
```python
|
||||
In [1]: hex(odrv0.axis0.error)
|
||||
Out[1]: '0x6'
|
||||
```
|
||||
|
||||
Written in binary, the number `0x6` corresponds to `110`, that means bits 1 and 2 are set (counting starts at 0).
|
||||
Looking at the reference below, this means that both `ERROR_DC_BUS_UNDER_VOLTAGE` and `ERROR_DC_BUS_OVER_VOLTAGE` occurred.
|
||||
</div></details>
|
||||
|
||||
The axis error may say that some other component has failed. Say it reports `ERROR_ENCODER_FAILED`, then you need to go check the encoder error: `hex(<axis>.encoder.error)`.
|
||||
If your ODrive is not working as expected, run `odrivetool` and type `dump_errors(odrv0)` <kbd>Enter</kbd>. This will dump a list of all the errors that are present. To also clear all the errors, you can run `dump_errors(odrv0, True)`.
|
||||
|
||||
The following sections will give some guidance on the most common errors. You may also check the code for the full list of errors:
|
||||
* Axis error flags defined [here](../Firmware/MotorControl/axis.hpp).
|
||||
* Motor error flags defined [here](../Firmware/MotorControl/motor.hpp).
|
||||
* Encoder error flags defined [here](../Firmware/MotorControl/encoder.hpp).
|
||||
|
||||
+41
-11
@@ -11,17 +11,47 @@ AXIS_STATE_ENCODER_INDEX_SEARCH = 6
|
||||
AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7
|
||||
AXIS_STATE_CLOSED_LOOP_CONTROL = 8
|
||||
|
||||
AXIS_ERROR_NONE = 0
|
||||
AXIS_ERROR_INVALID_STATE = 1
|
||||
#AXIS_ERROR_DC_BUS_UNDER_VOLTAGE = 2
|
||||
#AXIS_ERROR_DC_BUS_OVER_VOLTAGE = 3
|
||||
#AXIS_ERROR_CURRENT_MEASUREMENT_TIMEOUT = 4
|
||||
#AXIS_ERROR_CONTROL_LOOP_TIMEOUT = 5
|
||||
#AXIS_ERROR_MOTOR_FAILED = 6
|
||||
#AXIS_ERROR_SENSORLESS_ESTIMATOR_FAILED = 7
|
||||
#AXIS_ERROR_ENCODER_FAILED = 8
|
||||
#AXIS_ERROR_CONTROLLER_FAILED = 9
|
||||
#AXIS_ERROR_POS_CTRL_DURING_SENSORLESS = 10
|
||||
class errors:
|
||||
class axis:
|
||||
ERROR_NONE = 0x00
|
||||
ERROR_INVALID_STATE = 0x01 #<! an invalid state was requested
|
||||
ERROR_DC_BUS_UNDER_VOLTAGE = 0x02
|
||||
ERROR_DC_BUS_OVER_VOLTAGE = 0x04
|
||||
ERROR_CURRENT_MEASUREMENT_TIMEOUT = 0x08
|
||||
ERROR_BRAKE_RESISTOR_DISARMED = 0x10 #<! the brake resistor was unexpectedly disarmed
|
||||
ERROR_MOTOR_DISARMED = 0x20 #<! the motor was unexpectedly disarmed
|
||||
ERROR_MOTOR_FAILED = 0x40 # Go to motor.hpp for information, check odrvX.axisX.motor.error for error value
|
||||
ERROR_SENSORLESS_ESTIMATOR_FAILED = 0x80
|
||||
ERROR_ENCODER_FAILED = 0x100 # Go to encoder.hpp for information, check odrvX.axisX.encoder.error for error value
|
||||
ERROR_CONTROLLER_FAILED = 0x200
|
||||
ERROR_POS_CTRL_DURING_SENSORLESS = 0x400
|
||||
|
||||
class motor:
|
||||
ERROR_NONE = 0
|
||||
ERROR_PHASE_RESISTANCE_OUT_OF_RANGE = 0x0001
|
||||
ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE = 0x0002
|
||||
ERROR_ADC_FAILED = 0x0004
|
||||
ERROR_DRV_FAULT = 0x0008
|
||||
ERROR_CONTROL_DEADLINE_MISSED = 0x0010
|
||||
ERROR_NOT_IMPLEMENTED_MOTOR_TYPE = 0x0020
|
||||
ERROR_BRAKE_CURRENT_OUT_OF_RANGE = 0x0040
|
||||
ERROR_MODULATION_MAGNITUDE = 0x0080
|
||||
ERROR_BRAKE_DEADTIME_VIOLATION = 0x0100
|
||||
ERROR_UNEXPECTED_TIMER_CALLBACK = 0x0200
|
||||
ERROR_CURRENT_SENSE_SATURATION = 0x0400
|
||||
|
||||
class encoder:
|
||||
ERROR_NONE = 0
|
||||
ERROR_UNSTABLE_GAIN = 0x01
|
||||
ERROR_CPR_OUT_OF_RANGE = 0x02
|
||||
ERROR_NO_RESPONSE = 0x04
|
||||
ERROR_UNSUPPORTED_ENCODER_MODE = 0x08
|
||||
ERROR_ILLEGAL_HALL_STATE = 0x10
|
||||
ERROR_INDEX_NOT_FOUND_YET = 0x20
|
||||
|
||||
class controller:
|
||||
ERROR_NONE = 0
|
||||
ERROR_OVERSPEED = 0x01
|
||||
|
||||
MOTOR_TYPE_HIGH_CURRENT = 0
|
||||
#MOTOR_TYPE_LOW_CURRENT = 1
|
||||
|
||||
@@ -5,7 +5,7 @@ import threading
|
||||
import fibre
|
||||
import odrive
|
||||
import odrive.enums
|
||||
from odrive.utils import start_liveplotter
|
||||
from odrive.utils import start_liveplotter, dump_errors
|
||||
#from odrive.enums import * # pylint: disable=W0614
|
||||
|
||||
def print_banner():
|
||||
@@ -76,7 +76,8 @@ def launch_shell(args, logger, app_shutdown_token):
|
||||
"""
|
||||
|
||||
interactive_variables = {
|
||||
'start_liveplotter': start_liveplotter
|
||||
'start_liveplotter': start_liveplotter,
|
||||
'dump_errors': dump_errors
|
||||
}
|
||||
|
||||
# Expose all enums from odrive.enums
|
||||
|
||||
+39
-3
@@ -7,6 +7,7 @@ import platform
|
||||
import subprocess
|
||||
import os
|
||||
from fibre.utils import Event
|
||||
from odrive.enums import errors
|
||||
|
||||
try:
|
||||
if platform.system() == 'Windows':
|
||||
@@ -19,13 +20,48 @@ except ImportError:
|
||||
sys.stdout.flush()
|
||||
pass
|
||||
|
||||
data_rate = 100
|
||||
plot_rate = 10
|
||||
num_samples = 1000
|
||||
_VT100Colors = {
|
||||
'green': '\x1b[92;1m',
|
||||
'cyan': '\x1b[96;1m',
|
||||
'yellow': '\x1b[93;1m',
|
||||
'red': '\x1b[91;1m',
|
||||
'default': '\x1b[0m'
|
||||
}
|
||||
|
||||
class OperationAbortedException(Exception):
|
||||
pass
|
||||
|
||||
def dump_errors(odrv, clear=False):
|
||||
axes = [axis for name, axis in odrv._remote_attributes.items() if 'axis' in name]
|
||||
for num, axis in enumerate(axes):
|
||||
print('Axis{}:'.format(num))
|
||||
|
||||
# Flatten axis and submodules
|
||||
# (name, remote_obj, errorcode)
|
||||
module_decode_map = [
|
||||
('axis', axis, errors.axis),
|
||||
('motor', axis.motor, errors.motor),
|
||||
('encoder', axis.encoder, errors.encoder),
|
||||
('controller', axis.controller, errors.controller),
|
||||
]
|
||||
|
||||
# Module error decode
|
||||
for name, remote_obj, errorcodes in module_decode_map:
|
||||
prefix = ' '*2 + name + ": "
|
||||
if (remote_obj.error != errorcodes.ERROR_NONE):
|
||||
print(prefix + _VT100Colors['red'] + "Error(s):" + _VT100Colors['default'])
|
||||
errorcodes_tup = [(name, val) for name, val in errorcodes.__dict__.items() if 'ERROR_' in name]
|
||||
for codename, codeval in errorcodes_tup:
|
||||
if remote_obj.error & codeval != 0:
|
||||
print(" " + codename)
|
||||
if clear:
|
||||
remote_obj.error = errorcodes.ERROR_NONE
|
||||
else:
|
||||
print(prefix + _VT100Colors['green'] + "no error" + _VT100Colors['default'])
|
||||
|
||||
data_rate = 100
|
||||
plot_rate = 10
|
||||
num_samples = 1000
|
||||
def start_liveplotter(get_var_callback):
|
||||
"""
|
||||
Starts a liveplotter.
|
||||
|
||||
Reference in New Issue
Block a user