Merge branch 'devel' into input-filter

This commit is contained in:
Unknown
2019-01-02 12:54:30 -05:00
12 changed files with 132 additions and 36 deletions
+1
View File
@@ -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
+4 -2
View File
@@ -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;
+3 -1
View File
@@ -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)
)
);
}
+30 -1
View File
@@ -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);
}
+1
View File
@@ -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();
+2
View File
@@ -214,6 +214,8 @@ int odrive_main(void) {
axes[i]->start_thread();
}
start_analog_thread();
system_stats_.fully_booted = true;
return 0;
}
+1
View File
@@ -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_;
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3 -2
View File
@@ -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
View File
@@ -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.