Merge branch 'devel' of https://github.com/madcowswe/ODrive into gui

Merge devel into gui
This commit is contained in:
PAJohnson
2020-10-02 22:01:37 -04:00
35 changed files with 302 additions and 83 deletions
+11 -4
View File
@@ -2,7 +2,7 @@ name: Build and publish HTML documentation website
on:
push:
branches: [ feature/doc_autogen ]
branches: [ master ]
jobs:
jekyll:
@@ -52,15 +52,22 @@ jobs:
cd /srv/jekyll/docs && \
bundle config path vendor/bundle && \
bundle install && \
JEKYLL_ENV=production bundle exec jekyll build
bundle exec jekyll build --baseurl \"\"
cd ..
mv docs/_site _site
rm -rdf docs
mv _site docs
touch docs/.nojekyll
"
touch .nojekyll
# Extra checks to reduce likelihood of defect build
test -f docs/CNAME
test -f docs/index.html
- name: Push to documentation branch
run: |
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
git add -f docs/_site
git add -f docs
git commit -m "jekyll build from Action ${GITHUB_SHA}"
git push --force origin HEAD:${REMOTE_BRANCH}
env:
+2 -3
View File
@@ -62,8 +62,7 @@ ODrive\.includes
Firmware/Tests/bin/
# Electron-builder output
# GUI
GUI/dist_electron
# Node Modules
GUI/node_modules
GUI/build
+10 -3
View File
@@ -1,5 +1,7 @@
# Unreleased Features
Please add a note of your changes below this heading if you make a Pull Request.
### Added
* [Mechanical brake support](docs/mechanical-brakes.md)
### Changed
@@ -15,8 +17,8 @@ Please add a note of your changes below this heading if you make a Pull Request.
* `<axis>.motor.gate_driver` was moved to `<axis>.gate_driver`.
* `<axis>.min_endstop.pullup` and `<axis>.max_endstop.pullup` were removed. Use `<odrv>.config.gpioX_mode = GPIO_MODE_DIGITAL / GPIO_MODE_DIGITAL_PULL_UP / GPIO_MODE_DIGITAL_PULL_DOWN` instead.
# Release Candidate
## [0.5.1] - Date TBD
# Releases
## [0.5.1] - 2020-09-27
### Added
* Added motor `torque_constant`: units of torque are now [Nm] instead of just motor current.
* [Motor thermistors support](docs/thermistors.md)
@@ -29,9 +31,14 @@ Please add a note of your changes below this heading if you make a Pull Request.
* `axis.motor.thermal_current_lim` has been removed. Instead a new property is available `axis.motor.effective_current_lim` which contains the effective current limit including any thermal limits.
* `axis.motor.get_inverter_temp()`, `axis.motor.inverter_temp_limit_lower` and `axis.motor.inverter_temp_limit_upper` have been moved to seperate fet thermistor object under `axis.fet_thermistor`. `get_inverter_temp()` function has been renamed to `temp` and is now a read-only property.
* `axis.config.counts_per_step` is now `axis.config.turns_per_step`
* Outputs of `axis.sensorless_estimator` are now in turns/s instead of electrical rad/s
* Fixed bug of high current during lockin-ramp caused by `motor::update()` expecting a torque command instead of current
* Fixed bug where commanded velocity was extremely high just after sensorless ramp when using `input_mode` INPUT_MODE_VEL_RAMP caused by `vel_setpoint` and `axis.config.sensorless_ramp.vel` being in different units
### Fixed
* Fixed bug of high current during lockin-ramp caused by `motor::update()` expecting a torque command instead of current
* Fixed bug where commanded velocity was extremely high just after sensorless ramp when using `input_mode` INPUT_MODE_VEL_RAMP caused by `vel_setpoint` and `axis.config.sensorless_ramp.vel` being in different units
# Releases
## [0.5.0] - 2020-08-03
### Added
* AC Induction Motor support.
+13 -7
View File
@@ -7,16 +7,22 @@ RUN add-apt-repository ppa:team-gcc-arm-embedded/ppa
RUN add-apt-repository ppa:jonathonf/tup
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get -y install gcc-arm-embedded openocd tup python3.7 build-essential git
RUN apt-get -y install gcc-arm-embedded openocd tup python3.7 python3-yaml python3-jinja2 python3-jsonschema build-essential git
# Build step below does not know about debian's python naming schemme
RUN ln -s /usr/bin/python3.7 /usr/bin/python
# Copy the firmware tree into the container
RUN mkdir ODrive
COPY . ODrive
RUN mkdir -p ODrive
WORKDIR ODrive/Firmware
# Hack around Tup's dependency on FUSE
RUN tup generate build.sh
RUN ./build.sh
# Must attach the firmware tree into the container
CMD \
# Regenerate python interface
python interface_generator_stub.py \
--definitions odrive-interface.yaml \
--template ../tools/enums_template.j2 \
--output ../tools/odrive/enums.py && \
# Hack around Tup's dependency on FUSE
tup init && \
tup generate build.sh && \
./build.sh
+8
View File
@@ -35,6 +35,7 @@
#include "stm32f4xx.h"
#include "stm32f4xx_it.h"
#include "cmsis_os.h"
#include <stdbool.h>
/* USER CODE BEGIN 0 */
#include <Drivers/STM32/stm32_system.h>
@@ -85,6 +86,13 @@ void get_regs(void** stack_ptr) {
void* volatile pc __attribute__((unused)) = stack_ptr[6]; // Program counter
void* volatile psr __attribute__((unused)) = stack_ptr[7]; // Program status register
void* volatile cfsr __attribute__((unused)) = (void*)SCB->CFSR; // Configurable fault status register
void* volatile cpacr __attribute__((unused)) = (void*)SCB->CPACR;
void* volatile fpccr __attribute__((unused)) = (void*)FPU->FPCCR;
volatile bool preciserr __attribute__((unused)) = (uint32_t)cfsr & 0x200;
volatile bool ibuserr __attribute__((unused)) = (uint32_t)cfsr & 0x100;
volatile int stay_looping = 1;
while(stay_looping);
}
+3
View File
@@ -96,6 +96,7 @@ Encoder encoders[AXIS_COUNT] = {
// TODO: this has no hardware dependency and should be allocated depending on config
Endstop endstops[2 * AXIS_COUNT];
MechanicalBrake mechanical_brakes[AXIS_COUNT];
SensorlessEstimator sensorless_estimators[AXIS_COUNT];
Controller controllers[AXIS_COUNT];
@@ -116,6 +117,7 @@ std::array<Axis, AXIS_COUNT> axes{{
motors[0], // motor
trap[0], // trap
endstops[0], endstops[1], // min_endstop, max_endstop
mechanical_brakes[0], // mechanical brake
},
{
1, // axis_num
@@ -135,6 +137,7 @@ std::array<Axis, AXIS_COUNT> axes{{
motors[1], // motor
trap[1], // trap
endstops[2], endstops[3], // min_endstop, max_endstop
mechanical_brakes[1], // mechanical brake
},
}};
+12 -6
View File
@@ -19,7 +19,8 @@ Axis::Axis(int axis_num,
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop)
Endstop& max_endstop,
MechanicalBrake& mechanical_brake)
: axis_num_(axis_num),
default_step_gpio_pin_(default_step_gpio_pin),
default_dir_gpio_pin_(default_dir_gpio_pin),
@@ -33,6 +34,7 @@ Axis::Axis(int axis_num,
trap_traj_(trap),
min_endstop_(min_endstop),
max_endstop_(max_endstop),
mechanical_brake_(mechanical_brake),
current_limiters_(make_array(
static_cast<CurrentLimiter*>(&fet_thermistor),
static_cast<CurrentLimiter*>(&motor_thermistor))),
@@ -49,6 +51,7 @@ Axis::Axis(int axis_num,
trap_traj_.axis_ = this;
min_endstop_.axis_ = this;
max_endstop_.axis_ = this;
mechanical_brake_.axis_ = this;
}
Axis::LockinConfig_t Axis::default_calibration() {
@@ -235,9 +238,9 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) {
float x = 0.0f;
run_control_loop([&]() {
float phase = wrap_pm_pi(lockin_config.ramp_distance * x);
float I_mag = lockin_config.current * x;
float torque = lockin_config.current * motor_.config_.torque_constant * x;
x += current_meas_period / lockin_config.ramp_time;
if (!motor_.update(I_mag, phase, 0.0f))
if (!motor_.update(torque, phase, 0.0f))
return false;
return x < 1.0f;
});
@@ -266,7 +269,7 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) {
distance += vel * current_meas_period;
phase = wrap_pm_pi(phase + vel * current_meas_period);
if (!motor_.update(lockin_config.current, phase, vel))
if (!motor_.update(lockin_config.current * motor_.config_.torque_constant, phase, vel))
return false;
return !spin_done(true); //vel_override to go to next phase
});
@@ -282,7 +285,7 @@ bool Axis::run_lockin_spin(const LockinConfig_t &lockin_config) {
distance += vel * current_meas_period;
phase = wrap_pm_pi(phase + vel * current_meas_period);
if (!motor_.update(lockin_config.current, phase, vel))
if (!motor_.update(lockin_config.current * motor_.config_.torque_constant, phase, vel))
return false;
return !spin_done();
});
@@ -460,6 +463,7 @@ bool Axis::run_idle_loop() {
// run_control_loop ignores missed modulation timing updates
// if and only if we're in AXIS_STATE_IDLE
safety_critical_disarm_motor_pwm(motor_);
mechanical_brake_.engage();
set_step_dir_active(config_.enable_step_dir && config_.step_dir_always_on);
run_control_loop([this]() {
return true;
@@ -472,6 +476,7 @@ void Axis::run_state_machine_loop() {
// arm!
motor_.arm();
mechanical_brake_.release();
for (;;) {
// Load the task chain if a specific request is pending
@@ -556,7 +561,7 @@ void Axis::run_state_machine_loop() {
if (status) {
// call to controller.reset() that happend when arming means that vel_setpoint
// is zeroed. So we make the setpoint the spinup target for smooth transition.
controller_.vel_setpoint_ = config_.sensorless_ramp.vel;
controller_.vel_setpoint_ = config_.sensorless_ramp.vel / (2.0f * M_PI * motor_.config_.pole_pairs);
status = run_sensorless_control_loop();
}
} break;
@@ -573,6 +578,7 @@ void Axis::run_state_machine_loop() {
case AXIS_STATE_IDLE: {
run_idle_loop();
status = motor_.arm(); // done with idling - try to arm the motor
mechanical_brake_.release();
} break;
default:
+4 -1
View File
@@ -8,6 +8,7 @@ class Axis;
#include "controller.hpp"
#include "trapTraj.hpp"
#include "endstop.hpp"
#include "mechanical_brake.hpp"
#include "low_level.h"
#include "utils.hpp"
#include "communication/interface_uart.h" // TODO: remove once uart_poll() is gone
@@ -90,7 +91,8 @@ public:
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop);
Endstop& max_endstop,
MechanicalBrake& mechanical_brake);
bool apply_config();
void clear_config();
@@ -220,6 +222,7 @@ public:
TrapezoidalTrajectory& trap_traj_;
Endstop& min_endstop_;
Endstop& max_endstop_;
MechanicalBrake& mechanical_brake_;
// List of current_limiters and thermistors to
// provide easy iteration.
+1 -1
View File
@@ -380,7 +380,7 @@ void vbus_sense_adc_cb(ADC_HandleTypeDef* hadc, bool injected) {
}
// This is the callback from the ADC that we expect after the PWM has triggered an ADC conversion.
// TODO: Document how the phasing is done, link to timing diagram
// Timing diagram: Firmware/timing_diagram_v3.png
void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) {
#define calib_tau 0.2f //@TOTO make more easily configurable
constexpr float calib_filter_k = CURRENT_MEAS_PERIOD / calib_tau;
+10 -1
View File
@@ -48,6 +48,7 @@ static bool config_read_all() {
config_manager.read(&axes[i].trap_traj_.config_) &&
config_manager.read(&axes[i].min_endstop_.config_) &&
config_manager.read(&axes[i].max_endstop_.config_) &&
config_manager.read(&axes[i].mechanical_brake_.config_) &&
config_manager.read(&motors[i].config_) &&
config_manager.read(&fet_thermistors[i].config_) &&
config_manager.read(&axes[i].motor_thermistor_.config_) &&
@@ -67,6 +68,7 @@ static bool config_write_all() {
config_manager.write(&axes[i].trap_traj_.config_) &&
config_manager.write(&axes[i].min_endstop_.config_) &&
config_manager.write(&axes[i].max_endstop_.config_) &&
config_manager.write(&axes[i].mechanical_brake_.config_) &&
config_manager.write(&motors[i].config_) &&
config_manager.write(&fet_thermistors[i].config_) &&
config_manager.write(&axes[i].motor_thermistor_.config_) &&
@@ -86,6 +88,7 @@ static void config_clear_all() {
axes[i].trap_traj_.config_ = {};
axes[i].min_endstop_.config_ = {};
axes[i].max_endstop_.config_ = {};
axes[i].mechanical_brake_.config_ = {};
motors[i].config_ = {};
fet_thermistors[i].config_ = {};
axes[i].motor_thermistor_.config_ = {};
@@ -152,7 +155,7 @@ void ODrive::enter_dfu_mode() {
static void usb_deferred_interrupt_thread(void * ctx) {
(void) ctx; // unused parameter
for (;;) {
// Wait for signalling from USB interrupt (OTG_FS_IRQHandler)
osStatus semaphore_status = osSemaphoreWait(sem_usb_irq, osWaitForever);
@@ -419,6 +422,7 @@ extern "C" int main(void) {
if (mode == ODriveIntf::GPIO_MODE_DIGITAL ||
mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_UP ||
mode == ODriveIntf::GPIO_MODE_DIGITAL_PULL_DOWN ||
mode == ODriveIntf::GPIO_MODE_MECH_BRAKE ||
mode == ODriveIntf::GPIO_MODE_ANALOG_IN) {
GPIO_InitStruct.Alternate = 0;
} else {
@@ -515,6 +519,11 @@ extern "C" int main(void) {
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
case ODriveIntf::GPIO_MODE_MECH_BRAKE: {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
} break;
default: {
odrv.misconfigured_ = true;
continue;
@@ -0,0 +1,13 @@
#include <odrive_main.h>
void MechanicalBrake::engage() {
if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){
get_gpio(config_.gpio_num).write(config_.is_active_low ? 0 : 1);
}
}
void MechanicalBrake::release() {
if (odrv.config_.gpio_modes[config_.gpio_num] == ODriveIntf::GPIO_MODE_MECH_BRAKE){
get_gpio(config_.gpio_num).write(config_.is_active_low ? 1 : 0);
}
}
@@ -0,0 +1,25 @@
#ifndef __MECHANICAL_BRAKE_HPP
#define __MECHANICAL_BRAKE_HPP
#include <autogen/interfaces.hpp>
class MechanicalBrake : public ODriveIntf::MechanicalBrakeIntf {
public:
struct Config_t {
uint16_t gpio_num = 0;
bool is_active_low = true;
// custom setters
MechanicalBrake* parent = nullptr;
void set_gpio_num(uint16_t value) { gpio_num = value; }
};
MechanicalBrake() {}
MechanicalBrake::Config_t config_;
Axis* axis_ = nullptr;
void release();
void engage();
};
#endif // __MECHANICAL_BRAKE_HPP
+1
View File
@@ -49,6 +49,7 @@ void Motor::reset_current_control() {
current_control_.v_current_control_integral_d = 0.0f;
current_control_.v_current_control_integral_q = 0.0f;
current_control_.acim_rotor_flux = 0.0f;
current_control_.Ibus = 0.0f;
}
// @brief Tune the current controller based on phase resistance and inductance
+1
View File
@@ -142,6 +142,7 @@ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast<ENUMTYPE>(~static_c
#include <thermistor.hpp>
#include <trapTraj.hpp>
#include <endstop.hpp>
#include <mechanical_brake.hpp>
#include <axis.hpp>
#include <communication/communication.h>
@@ -66,13 +66,15 @@ bool SensorlessEstimator::update() {
}
// predict PLL phase with velocity
pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * vel_estimate_);
pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * vel_estimate_erad_);
// update PLL phase with observer permanent magnet phase
phase_ = fast_atan2(eta[1], eta[0]);
float delta_phase = wrap_pm_pi(phase_ - pll_pos_);
pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * pll_kp * delta_phase);
// update PLL velocity
vel_estimate_ += current_meas_period * pll_ki * delta_phase;
vel_estimate_erad_ += current_meas_period * pll_ki * delta_phase;
// convert to mechanical turns/s for controller usage.
vel_estimate_ = vel_estimate_erad_ / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI);
vel_estimate_valid_ = true;
return true;
@@ -18,7 +18,8 @@ public:
Error error_ = ERROR_NONE;
float phase_ = 0.0f; // [rad]
float pll_pos_ = 0.0f; // [rad]
float vel_estimate_ = 0.0f; // [rad/s]
float vel_estimate_ = 0.0f; // [turn/s]
float vel_estimate_erad_ = 0.0f; // [rad/s]
bool vel_estimate_valid_ = false;
// float pll_kp_ = 0.0f; // [rad/s / rad]
// float pll_ki_ = 0.0f; // [(rad/s^2) / rad]
+3 -1
View File
@@ -133,6 +133,7 @@ FLAGS += '-DUSE_HAL_DRIVER'
FLAGS += '-mthumb'
FLAGS += '-mfloat-abi=hard'
FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'}
FLAGS += '-g'
-- linker flags
LDFLAGS += board.ldflags
@@ -142,7 +143,7 @@ LDFLAGS += '-Wl,--undefined=uxTopUsedPriority'
-- debug build
if tup.getconfig("DEBUG") == "true" then
FLAGS += '-g -gdwarf-2'
FLAGS += '-gdwarf-2'
OPT += '-Og'
else
OPT += '-O2'
@@ -189,6 +190,7 @@ sources = {
'MotorControl/thermistor.cpp',
'MotorControl/encoder.cpp',
'MotorControl/endstop.cpp',
'MotorControl/mechanical_brake.cpp',
'MotorControl/controller.cpp',
'MotorControl/sensorless_estimator.cpp',
'MotorControl/trapTraj.cpp',
+2 -1
View File
@@ -328,7 +328,7 @@ def regularize_valuetype(path, name, elem):
elem['flags'][k]['value'] = 0 if current_bit is None else (1 << current_bit)
bit = bit if current_bit is None else current_bit + 1
if 'nullflag' in elem:
elem['flags'] = OrderedDict([(elem['nullflag'], {'value': 0, 'bit': None}), *elem['flags'].items()])
elem['flags'] = OrderedDict([(elem['nullflag'], OrderedDict({'name': elem['nullflag'], 'value': 0, 'bit': None})), *elem['flags'].items()])
elem['values'] = elem['flags']
elem['is_flags'] = True
elem['is_enum'] = True
@@ -653,6 +653,7 @@ env.filters['skip_first'] = lambda x: list(x)[1:]
env.filters['to_c_string'] = lambda x: '\n'.join(('"' + line.replace('"', '\\"') + '"') for line in json.dumps(x, separators=(',', ':')).replace('{"name"', '\n{"name"').split('\n'))
env.filters['tokenize'] = tokenize
env.filters['diagonalize'] = lambda lst: [lst[:i + 1] for i in range(len(lst))]
env.filters['debug'] = lambda x: print(x)
template = env.from_string(template_file.read())
+17 -1
View File
@@ -454,6 +454,7 @@ interfaces:
trap_traj: TrapezoidalTrajectory
min_endstop: Endstop
max_endstop: Endstop
mechanical_brake: MechanicalBrake
functions:
watchdog_feed:
doc: Feed the watchdog to prevent watchdog timeouts.
@@ -902,7 +903,6 @@ interfaces:
accel_limit: float32
decel_limit: float32
ODrive.Endstop:
c_is_class: True
attributes:
@@ -916,6 +916,21 @@ interfaces:
is_active_high: bool
debounce_ms: {type: uint32, c_setter: set_debounce_ms}
ODrive.MechanicalBrake:
c_is_class: True
attributes:
config:
c_is_class: False
attributes:
gpio_num: {type: uint16, c_setter: set_gpio_num}
is_active_low: bool
functions:
engage:
doc: |
This function engages the mechanical brake if one is present and enabled.
release:
doc: |
This function releases the mecahncal brake if one is present and enabled.
valuetypes:
ODrive.GpioMode:
@@ -944,6 +959,7 @@ valuetypes:
Enc0: {doc: The pin is used by quadrature encoder 0.}
Enc1: {doc: The pin is used by quadrature encoder 1.}
Enc2: {doc: This mode is not supported on ODrive v3.x.}
MechBrake: {doc: This is to support external mechanical brakes.}
ODrive.Can.Protocol:
values: {Simple: }
Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

+4 -7
View File
@@ -1,6 +1,6 @@
function cleanup {
echo "Removing previous build artifacts"
rm -rf build
rm -rf build/ Firmware/autogen Firmware/build Firmware/.tup
docker rm odrive-build-cont
}
@@ -13,14 +13,11 @@ function gc {
function build {
cleanup
echo "Building the firmware"
echo "Building the build-environment image"
docker build -t odrive-build-img .
echo "Create container"
docker create --name odrive-build-cont odrive-build-img:latest
echo "Extract build artifacts"
docker cp odrive-build-cont:ODrive/Firmware/build .
echo "Build in container"
docker run -v $(pwd):/ODrive --name odrive-build-cont odrive-build-img:latest
}
function usage {
+2
View File
@@ -17,6 +17,8 @@ sections:
url: /encoders
- title: Homing & Endstops
url: /endstops
- title: Mechanical Brakes
url: /mechanical-brakes
- title: Thermistors
url: /thermistors
- title: Control & Tuning
+3 -2
View File
@@ -16,6 +16,7 @@
<![endif]-->
</head>
<body>
<div style="position: absolute;top: 0;left: 0;width: 100%;background-color: #5bc0de;height: 20px;text-align: center;font-weight: 600">The docs reflect firmware version 0.5.1. There are many breaking <a href="https://github.com/madcowswe/ODrive/blob/master/CHANGELOG.md">changes</a>. Please find docs for v0.4.12 <a href="https://github.com/madcowswe/ODrive/blob/fw-v0.4.12/docs/getting-started.md">here</a>.</div>
<div class="wrapper">
<header>
<div>
@@ -78,7 +79,7 @@
<path fill-rule="evenodd" d="M0 12v3h3l8-8-3-3-8 8zm3 2H1v-2h1v1h1v1zm10.3-9.3L12 6 9 3l1.3-1.3a.996.996 0 0 1 1.41 0l1.59 1.59c.39.39.39 1.02 0 1.41z"></path>
</svg>
{% if page.edit_url %}
{% assign edit_url = "https://www.github.com/madcowswe/ODrive/edit/master/" | append: edit_url %}
{% assign edit_url = "https://www.github.com/madcowswe/ODrive/edit/master/" | append: page.edit_url %}
{% else %}
{% assign edit_url = "https://www.github.com/madcowswe/ODrive/edit/master/docs/" | append: pagename | append: ".md" %}
{% endif %}
@@ -90,7 +91,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="10" height="10" style="fill: #656565;">
<path d="M216 0h80c13.3 0 24 10.7 24 24v168h87.7c17.8 0 26.7 21.5 14.1 34.1L269.7 378.3c-7.5 7.5-19.8 7.5-27.3 0L90.1 226.1c-12.6-12.6-3.7-34.1 14.1-34.1H192V24c0-13.3 10.7-24 24-24zm296 376v112c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V376c0-13.3 10.7-24 24-24h146.7l49 49c20.1 20.1 52.5 20.1 72.6 0l49-49H488c13.3 0 24 10.7 24 24zm-124 88c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20zm64 0c0-11-9-20-20-20s-20 9-20 20 9 20 20 20 20-9 20-20z"/>
</svg>
<a href="https://www.github.com/madcowswe/ODrive/edit/master/{{page.download.url}}">{{page.download.text}}</a>
<a href="https://www.github.com/madcowswe/ODrive/blob/master/{{page.download.url}}">{{page.download.text}}</a>
</div>
{% endif %}
</div>
+1 -1
View File
@@ -73,7 +73,7 @@ Configuration of the CAN parameters should be done via USB before putting the de
To set the desired baud rate, use `<odrv>.can.set_baud_rate(<value>)`. The baud rate can be done without rebooting the device. If you'd like to keep the baud rate, simply call `<odrv>.save_configuration()` before rebooting.
Each axis looks like a separate node on the bus. Thus, they both have the two properties `can_node_id` and `can_node_id_extended`. The node ID can be from 0 to 63 (0x3F) inclusive, or, if extended CAN IDs are used, from 0 to 16777215 (0xFFFFFF).
Each axis looks like a separate node on the bus. Thus, they both have the two properties `can_node_id` and `can_node_id_extended`. The node ID can be from 0 to 63 (0x3F) inclusive, or, if extended CAN IDs are used, from 0 to 16777215 (0xFFFFFF). If you want to connect more than one ODrive on a CAN bus, you must set different node IDs for the second ODrive or they will conflict and crash the bus.
### Example Configuration
+5 -4
View File
@@ -44,6 +44,7 @@ Possible values are listed [here](api/odrive.axis.controller.controlmode).
As of version v0.5.0, ODrive now intercepts the incoming commands and can apply filters to them. The old protocol values `pos_setpoint`, `vel_setpoint`, and `current_setpoint` are still used internally by the closed-loop cascade control, but the user cannot write to them directly. This allows us to condense the number of ways the ODrive accepts motion commands. The new commands are:
### Control Commands
* `<axis>.controller.input_pos = <turn>`
* `<axis>.controller.input_vel = <turn/s>`
* `<axis>.controller.input_torque = <torque in Nm>`
@@ -82,15 +83,15 @@ All variables that are part of a `[...].config` object can be saved to non-volat
## Setting up sensorless
The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hunderd RPM.
To give an example, suppose you have a motor with 7 pole pairs, and you want to spin it at 3000 RPM. Then you would set the `input_vel` to `3000 * 2*pi/60 * 7 = 2199 rad/s electrical`.
Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work.
Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. Motor calibration and setup must also be completed before sensorless mode will work.
```
odrv0.axis0.controller.config.vel_gain = 0.01
odrv0.axis0.controller.config.vel_integrator_gain = 0.05
odrv0.axis0.controller.config.control_mode = 2
odrv0.axis0.controller.input_vel = 400
odrv0.axis0.controller.input_vel = 10
odrv0.axis0.controller.config.vel_limit = <a value greater than input_vel>
odrv0.axis0.motor.config.current_lim = 2 * odrv0.axis0.config.sensorless_ramp.current
odrv0.axis0.motor.config.direction = 1
odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / (<pole pairs> * <motor kv>)
```
+2
View File
@@ -29,6 +29,8 @@ voltage_integral += current_error * current_integrator_gain
voltage_cmd = current_error * current_gain + voltage_integral (+ voltage_feedforward when we have motor model)
```
Note: `current_gain` and `current_integrator_gain` are automatically set according to `motor.config.current_control_bandwidth`
For more detail refer to [controller.cpp](https://github.com/madcowswe/ODrive/blob/master/Firmware/MotorControl/controller.cpp#L86).
### Controller Details:
+10 -5
View File
@@ -43,7 +43,7 @@ That's it, now on every reboot the motor will turn in one direction until it fin
* If your motor has problems reaching the index location due to the mechanical load, you can increase `<axis>.motor.config.calibration_current`.
### Reversing index search
Sometimes you would like the index search to only happen in a particular direction (the reverse of the default), instead of swapping the motor leads, you can ensure the following three values are negative:
Sometimes you would like the index search to only happen in a particular direction (the reverse of the default). Instead of swapping the motor leads, you can ensure that the following three values are negative:
* `<axis0>.config.calibration_lockin.vel`
* `<axis0>.config.calibration_lockin.accel`
* `<axis0>.config.calibration_lockin.ramp_distance`
@@ -78,7 +78,7 @@ Do you still have no errors? Awesome. Now, setup the motor and encoder to use kn
* `<axis>.encoder.config.pre_calibrated = True`
* `<axis>.motor.config.pre_calibrated = True `
And see if ODrive agrees that calibration worked by just running
And see if ODrive agrees that the calibration worked by just running
* `<axis>.encoder.config.pre_calibrated`
(using no "= True" ). Make sure that 'pre_calibrated' is in fact True.
@@ -121,6 +121,7 @@ Connect to the I pin, see if you get a pulse on a complete rotation. Sometimes t
If you are using SPI, use a logic analyzer and connect to the CLK, MISO, and CS pins. Set a trigger for the CS pin and ensure that the encoder position is being sent and is increasing/decreasing as you spin the motor. There is extremely cheap hardware that is supported by [Sigrok](https://sigrok.org/) for protocol analysis.
## 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 interfering 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:
@@ -128,13 +129,13 @@ Noise is found in all circuits, life is just about figuring out if it is prevent
* 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 susceptible noise.
The following _might_ mitigate noise problems. Use shielded cable, or use twisted pairs, where one side of each twisted pair is tied to ground and 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 susceptible 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:
If you are using an encoder with an index signal, another problem that has been encountered is 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.
One easy step that _might_ fix the noise on the Z input is to solder a 22nF-47nF capacitor to the Z pin and the GND pin on the underside of the ODrive board.
## Hall feedback pinout
If position accuracy is not a concern, you can use A/B/C hall effect encoders for position feedback.
@@ -170,12 +171,16 @@ Apart from (incremental) quadrature encoders, ODrive also supports absolute SPI
Some of these chips come with evaluation boards that can simplify mounting the chips to your motor. For our purposes if you are using an evaluation board you should select the settings for 3.3v.
**Note:** The AMT23x family has a hardware bug that causes them to not properly tristate the MISO line. To use them with ODrive, there are two workarounds. One is to sequence power to the encoder a second or two after the ODrive recieves power. This allows 1 encoder to be used without issue. Another solution is to add a tristate buffer, such as the 74AHC1G125SE, on the MISO line between the ODrive and each AMT23x encoder. Tie the enable pin on the buffer to the CS line for the respective encoder. This allows for more than one AMT23x encoder, or one AMT23x and another SPI encoder, to be used at the same time.
1. Connect the encoder to the ODrive's SPI interface:
- The encoder's SCK, MISO (aka "DATA" on CUI encoders), GND and 3.3V should connect to the ODrive pins with the same label.
- The encoder's MOSI should be tied to 3.3V (AMS encoders only. CUI encoders don't have this pin.)
- The encoder's Chip Select (aka nCS/CSn) can be connected to any of the ODrive's GPIOs (caution: GPIOs 1 and 2 are usually used by UART).
If you are having calibration problems, make sure that your magnet is centered on the axis of rotation on the motor. Some users report that this has a significant impact on calibration. Also make sure that your magnet height is within range of the spec sheet.
2. In `odrivetool`, run:
<axis>.encoder.config.abs_spi_cs_gpio_pin = 4 # or which ever GPIO pin you choose
+13 -8
View File
@@ -19,6 +19,7 @@ permalink: /
- [Other control modes](#other-control-modes)
- [Watchdog Timer](#watchdog-timer)
- [What's next?](#whats-next)
- [Upgrading from 0.4.12](#upgrading-from-0412)
<!-- /TOC -->
@@ -41,7 +42,7 @@ permalink: /
</div></details>
* A power supply (12V-24V for the 24V board variant, 12V-48V for the 48V board variant). A battery is also fine. Some advice on choosing a power supply can be found [here](https://things-in-motion.blogspot.com/2018/12/how-to-select-right-power-source-for.html).
* A power supply (12V-24V for the 24V board variant, 12V-56V for the 56V board variant). A battery is also fine. Some advice on choosing a power supply can be found [here](https://things-in-motion.blogspot.com/2018/12/how-to-select-right-power-source-for.html).
<details><summary markdown="span">What voltage variant do I have?</summary><div markdown="block">
On all ODrives shipped July 2018 or after have a silkscreen label clearly indicating the voltage variant.
@@ -61,13 +62,13 @@ All non-power I/O is 3.3V output and 5V tolerant on input, on ODrive v3.3 and ne
### Wiring up the encoders
Connect the encoder(s) to J4. The A,B phases are required, and the Z (index pulse) is optional. The A,B and Z lines have 3.3k pull up resistors, for use with open-drain encoder outputs. For single ended push-pull signals with weak drive current (\<4mA), you may want to desolder the pull-ups.
![Image of ODrive all hooked up](https://docs.google.com/drawings/d/e/2PACX-1vTCD0P40Cd-wvD7Fl8UYEaxp3_UL81oI4qUVqrrCJPi6tkJeSs2rsffIXQRpdu6rNZs6-2mRKKYtILG/pub?w=1716&h=1281)
![Image of ODrive all hooked up](https://docs.google.com/drawings/d/e/2PACX-1vTpJziAisrkvV1kTL4vckAJkmJ-BAvTwN1GeZZNCNwpTHv47Cf8bpz-gJqK2Z3un6FCHT4E-rcuUg6c/pub?w=1716&h=1281)
### Safety & Power UP
<div class="alert">
Always think safety before powering up the ODrive if motors are attached. Consider what might happen if the motor spins as soon as power is applied.
</div>
* Unlike some devices, the ODrive does not recieve power over the USB port so the 24/48 volt power input is required even just to communicate with it using USB. It is ok to power up the ODrive before or after connecting the USB cable.
* Unlike some devices, the ODrive does not recieve power over the USB port so the 24/56 volt power input is required even just to communicate with it using USB. It is ok to power up the ODrive before or after connecting the USB cable.
* To power up the ODrive, connect the power source to the DC terminals. Make sure to pay attention to the polarity. A small spark is normal. This is caused by the capacitors charging up.
## Downloading and Installing Tools
@@ -81,7 +82,7 @@ Most instructions in this guide refer to a utility called `odrivetool`, so you s
2. Launch the command prompt.
* __Anaconda__: In the start menu, type `Anaconda Prompt` <kbd>Enter</kbd>
* __Standalone Python__: In the start menu, type `cmd` <kbd>Enter</kbd>
3. Install the ODrive tools by typing `pip install odrive` <kbd>Enter</kbd>
3. Install the ODrive tools by typing `pip install --upgrade odrive` <kbd>Enter</kbd>
4. Plug in a USB cable into the microUSB connector on ODrive, and connect it to your PC.
5. Use the [Zadig](http://zadig.akeo.ie/) utility to set ODrive driver to libusb-win32.
* Check 'List All Devices' from the options menu, and select 'ODrive 3.x Native Interface (Interface 2)'. With that selected in the device list choose 'libusb-win32' from the target driver list and then press the large 'install driver' button.
@@ -107,13 +108,13 @@ brew install libusb
```
5. Now that you have Python 3 and all the package managers, run:
```bash
pip3 install odrive
pip3 install --upgrade odrive
```
__Troubleshooting__
1. Permission Errors: Just run the previous command in sudo
```bash
sudo pip3 install odrive
sudo pip3 install --upgrade odrive
```
2. Dependency Errors: If the installer doesn't complete and you get a dependency
@@ -126,7 +127,7 @@ Try step 5 again
### Linux
1. [Install Python 3](https://www.python.org/downloads/). (for example, on Ubuntu, `sudo apt install python3 python3-pip`)
2. Install the ODrive tools by opening a terminal and typing `sudo pip3 install odrive` <kbd>Enter</kbd>
2. Install the ODrive tools by opening a terminal and typing `sudo pip3 install --upgrade odrive` <kbd>Enter</kbd>
* This should automatically add the udev rules. If this fails for some reason you can add them manually:
```bash
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d[0-9][0-9]", MODE="0666"' | sudo tee /etc/udev/rules.d/91-odrive.rules
@@ -230,7 +231,7 @@ You can save all `.config` parameters to persistent memory so the ODrive remembe
## Position control of M0
Let's get motor 0 up and running. The procedure for motor 1 is exactly the same, so feel free to substitute `axis0` wherever it says `axis0`.
Let's get motor 0 up and running. The procedure for motor 1 is exactly the same, so feel free to substitute `axis1` wherever it says `axis0`.
1. Type `odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE` <kbd>Enter</kbd>. After about 2 seconds should hear a beep. Then the motor will turn slowly in one direction for a few seconds, then back in the other direction.
@@ -373,3 +374,7 @@ You can now:
* See how you can improve the behavior during the startup procedure, like [bypassing encoder calibration](encoders.md#encoder-with-index-signal).
If you have any issues or any questions please get in touch. The [ODrive Community](https://discourse.odriverobotics.com/) warmly welcomes you.
## Upgrading from 0.4.12
A new version (0.5.1) of ODrive firmware has released, complete with a new odrivetool. Follow the installation instructions, making sure to add the `--upgrade` flag to pip commands, and check out the [Changelog](../CHANGELOG.md) for changes!
+1 -1
View File
@@ -98,7 +98,7 @@ Check the status of the encoder object:
odrv0.axis0.encoder
```
Check that there are no errors. If your hall sensors has a standard timing angle then `offset_float` should be close to 0.5.
Check that there are no errors. If your hall sensors has a standard timing angle then `offset_float` should be close to 0.5 or 1.5.
```txt
error = 0x0000 (int)
offset_float = 0.5126956701278687 (float)
+19 -19
View File
@@ -18,25 +18,25 @@ The ODrive can be controlled over various ports and protocols. If you're comfort
## Pinout
| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` |
|----|---------------|------------------------|-----------------------|-------------------|------------------|------------------|------------------|------------------|------------------|
| 0 | _not a pin_ | | | | | | | | |
| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | PWM0.0 | | | | |
| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | PWM0.1 | | | | |
| 3 | GPIO3 | general purpose | **analog input** | | PWM0.2 | | | | |
| 4 | GPIO4 | general purpose | **analog input** | | PWM0.3 | | | | |
| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | |
| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | |
| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | |
| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | |
| 9 | M0.A | general purpose | | | | | | **ENC0.A** | |
| 10 | M0.B | general purpose | | | | | | **ENC0.B** | |
| 11 | M0.Z | **general purpose** | | | | | | | |
| 12 | M1.A | general purpose | | | | | I2C.SCL | | **ENC1.A** |
| 13 | M1.B | general purpose | | | | | I2C.SDA | | **ENC1.B** |
| 14 | M1.Z | **general purpose** | | | | | | | |
| 15 | _not exposed_ | general purpose | | | | **CAN0.RX** | I2C.SCL | | |
| 16 | _not exposed_ | general purpose | | | | **CAN0.TX** | I2C.SDA | | |
| # | Label | `GPIO_MODE_DIGITAL` | `GPIO_MODE_ANALOG_IN` | `GPIO_MODE_UART0` | `GPIO_MODE_PWM0` | `GPIO_MODE_CAN0` | `GPIO_MODE_I2C0` | `GPIO_MODE_ENC0` | `GPIO_MODE_ENC1` | `GPIO_MODE_MECH_BRAKE` |
|----|---------------|------------------------|-----------------------|-------------------|------------------|------------------|------------------|------------------|------------------|------------------------|
| 0 | _not a pin_ | | | | | | | | | |
| 1 | GPIO1 (+) | general purpose | analog input | **UART0.TX** | PWM0.0 | | | | | mechanical brake |
| 2 | GPIO2 (+) | general purpose | analog input | **UART0.RX** | PWM0.1 | | | | | mechanical brake |
| 3 | GPIO3 | general purpose | **analog input** | | PWM0.2 | | | | | mechanical brake |
| 4 | GPIO4 | general purpose | **analog input** | | PWM0.3 | | | | | mechanical brake |
| 5 | GPIO5 | general purpose | **analog input** (*) | | | | | | | mechanical brake |
| 6 | GPIO6 (*) (+) | **general purpose** | | | | | | | | mechanical brake |
| 7 | GPIO7 (*) (+) | **general purpose** | | | | | | | | mechanical brake |
| 8 | GPIO8 (*) (+) | **general purpose** | | | | | | | | mechanical brake |
| 9 | M0.A | general purpose | | | | | | **ENC0.A** | | |
| 10 | M0.B | general purpose | | | | | | **ENC0.B** | | |
| 11 | M0.Z | **general purpose** | | | | | | | | |
| 12 | M1.A | general purpose | | | | | I2C.SCL | | **ENC1.A** | |
| 13 | M1.B | general purpose | | | | | I2C.SDA | | **ENC1.B** | |
| 14 | M1.Z | **general purpose** | | | | | | | | |
| 15 | _not exposed_ | general purpose | | | | **CAN0.RX** | I2C.SCL | | | |
| 16 | _not exposed_ | general purpose | | | | **CAN0.TX** | I2C.SDA | | | |
(*) ODrive v3.5 and later <br>
+61
View File
@@ -0,0 +1,61 @@
# Mechanical Brake
Some systems employ mechanical brakes on motors as a safety feature. These brakes can also be engaged as a power-saving function if the motor is not moving, but still under load.
ODrive supports the use of its GPIO pins to connect to external brake drive electronics.
When the ODrive engages the drive electronics, the brake will be disabled. When the drive enters a fault or idle state, the brake will be re-engaged.
---
## Mechanical Brake Configuration
Each axis supports one mechanical brake. The following properties are accessible through `odrivetool`:
Name | Type | Default
--- | -- | --
gpio_num | int | 0
is_active_low | boolean | true
### gpio_num
The GPIO pin number, according to the silkscreen labels on ODrive. Set with these commands:
```
<odrv>.<axis>.mechanical_brake.config.gpio_num = <1, 2, 3, 4, 5, 6, 7, 8>
```
After GPIO pin number is changed, you'll need to run `<odrv>.save_configuration()` and `<odrv>.reboot()` for changes to take effect.
### is_active_low
Most safety braking systems are active low, e.g. when the power is off, the brake is on. If the system uses brake drive electronics which use active high logic, flip this bit then reconsider the safety implications of your design...
### Enabling
The configuration of the mechanical brake will enable the brake functionality. There's no need to specifically 'enable' this feature.
### Example
Let's say we're hacking away on an old ABB robotic arm. We've wired a 24V brake drive circuit triggered by GPIO5. When GPIO5 is driven, it will release the brakes on the axis we're moving.
We need to notify the axis of the GPIO number we've attached our brake to, and configure the ODrive pin mode to `GPIO_MODE_MECH_BRAKE`:
```
<odrv>.<axis>.mechanical_brake.config.gpio_num = 5
<odrv>.config.gpio5_mode = GPIO_MODE_MECH_BRAKE
```
Pin configurations only take effect after a save/reboot so don't forget to run:
```
<odrv>.save_configuration()
<odrv>.reboot()
```
### Testing The Mechanical Brakes
Depending on your system this could be a dangerous experiment. Ensure that you have taken all necessary precautions to confirm if the wrong brake were inadvertently released it would not lead to injury or damage to equipment.
```
<odrv>.<axis>.mechanical_brake.release()
```
Note: If a brake is configured, it will be automatically engaged/disengaged during the next state machine step.
After you're satisfied with the testing, you can re-enable the brake using the command
```
<odrv>.<axis>.mechanical_brake.engage()
```
+2 -1
View File
@@ -85,11 +85,12 @@ To compile firmware from source, refer to the [developer guide](developer-guide)
### Troubleshooting
* __Windows__: During the update, a new device called "STM32 BOOTLOADER" will appear. Open the [Zadig utility](http://zadig.akeo.ie/) and set the driver for "STM32 BOOTLOADER" to libusb-win32. After that the firmware update will continue.
* __Linux__: Try running `sudo odrivetool dfu` instead of `odrivetool dfu`.
* On some machines you will need to unplug and plug back in the USB cable to make the PC understand that we switched from regular mode to bootloader mode.
* If the DFU script can't find the device, try forcing it into DFU mode.
<details><summary markdown="span">How to force DFU mode (ODrive v3.5 and newer)</summary><div markdown="block">
Flick the DIP switch that "DFU, RUN" to "DFU" and power cycle the board. After you're done upgrading firmware, don't forget to put the switch back into the "RUN" position and power cycle the board again.
Flick the DIP switch that says "DFU, RUN" to "DFU" and power cycle the board. If that alone doesn't work, also connect the pin "GPIO6" to "GND". After you're done upgrading firmware, don't forget to put the switch back into the "RUN" position and power cycle the board again.
</div></details>
<details><summary markdown="span">How to force DFU mode (ODrive v3.1, v3.2)</summary><div markdown="block">
+6 -3
View File
@@ -223,9 +223,12 @@ def put_into_dfu_mode(device, cancellation_token):
Puts the specified device into DFU mode
"""
if not hasattr(device, "enter_dfu_mode"):
print("The firmware on device {} does not support DFU. You need to \n"
"flash the firmware once using STLink (`make flash`), after that \n"
"DFU with this script should work fine."
print("The firmware on device {} cannot soft enter DFU mode.\n"
"Please remove power, put the DFU switch into DFU mode,\n"
"then apply power again. Then try again.\n"
"If it still doesn't work, you can try to use the DeFuse app or \n"
"dfu-util, see the odrive documentation.\n"
"You can also flash the firmware using STLink (`make flash`)"
.format(device.__channel__.usb_device.serial_number))
return
+1
View File
@@ -18,6 +18,7 @@ GPIO_MODE_PWM0 = 10
GPIO_MODE_ENC0 = 11
GPIO_MODE_ENC1 = 12
GPIO_MODE_ENC2 = 13
GPIO_MODE_MECH_BRAKE = 14
# ODrive.Can.Protocol
PROTOCOL_SIMPLE = 0
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
declare -a tests=('analog_input_test.py'
'calibration_test.py'
'can_test.py'
'closed_loop_test.py'
'encoder_test.py'
'fibre_test.py'
'integration_test.py'
'nvm_test.py'
'pwm_input_test.py'
'step_dir_test.py'
'uart_ascii_test.py'
)
summary=""
for test in "${tests[@]}"; do
(ipython3 "$test" -- --test-rig-yaml ../../test-rig-rpi.yaml || true) | tee /tmp/odrivetest.log
if grep "All tests passed!" /tmp/odrivetest.log; then
summary="$summary - $test: passed"$'\n'
else
summary="$summary - $test: failed"$'\n'
fi
echo "########################"
echo "Current status:"
echo -n "$summary"
echo "########################"
done