mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-08-20 22:14:34 +08:00
Mechanical Brake feature
This commit is contained in:
@@ -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),
|
||||
hw_config_(hw_config),
|
||||
config_(config),
|
||||
@@ -32,6 +33,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))),
|
||||
@@ -48,6 +50,7 @@ Axis::Axis(int axis_num,
|
||||
trap_traj_.axis_ = this;
|
||||
min_endstop_.axis_ = this;
|
||||
max_endstop_.axis_ = this;
|
||||
mechanical_brake_.axis_ = this;
|
||||
decode_step_dir_pins();
|
||||
watchdog_feed();
|
||||
}
|
||||
@@ -462,6 +465,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;
|
||||
@@ -474,6 +478,7 @@ void Axis::run_state_machine_loop() {
|
||||
|
||||
// arm!
|
||||
motor_.arm();
|
||||
mechanical_brake_.release();
|
||||
|
||||
for (;;) {
|
||||
// Load the task chain if a specific request is pending
|
||||
@@ -575,6 +580,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:
|
||||
|
||||
@@ -82,7 +82,8 @@ public:
|
||||
Motor& motor,
|
||||
TrapezoidalTrajectory& trap,
|
||||
Endstop& min_endstop,
|
||||
Endstop& max_endstop);
|
||||
Endstop& max_endstop,
|
||||
MechanicalBrake& mechanical_brake);
|
||||
|
||||
void setup();
|
||||
void start_thread();
|
||||
@@ -210,6 +211,7 @@ public:
|
||||
TrapezoidalTrajectory& trap_traj_;
|
||||
Endstop& min_endstop_;
|
||||
Endstop& max_endstop_;
|
||||
MechanicalBrake& mechanical_brake_;
|
||||
|
||||
// List of current_limiters and thermistors to
|
||||
// provide easy iteration.
|
||||
|
||||
@@ -21,6 +21,7 @@ Axis::Config_t axis_configs[AXIS_COUNT];
|
||||
TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT];
|
||||
Endstop::Config_t min_endstop_configs[AXIS_COUNT];
|
||||
Endstop::Config_t max_endstop_configs[AXIS_COUNT];
|
||||
MechanicalBrake::Config_t mechanical_brake_configs[AXIS_COUNT];
|
||||
|
||||
std::array<Axis*, AXIS_COUNT> axes;
|
||||
ODriveCAN *odCAN = nullptr;
|
||||
@@ -38,6 +39,7 @@ typedef Config<
|
||||
TrapezoidalTrajectory::Config_t[AXIS_COUNT],
|
||||
Endstop::Config_t[AXIS_COUNT],
|
||||
Endstop::Config_t[AXIS_COUNT],
|
||||
MechanicalBrake::Config_t[AXIS_COUNT],
|
||||
Axis::Config_t[AXIS_COUNT]> ConfigFormat;
|
||||
|
||||
void ODrive::save_configuration(void) {
|
||||
@@ -53,6 +55,7 @@ void ODrive::save_configuration(void) {
|
||||
&trap_configs,
|
||||
&min_endstop_configs,
|
||||
&max_endstop_configs,
|
||||
&mechanical_brake_configs,
|
||||
&axis_configs)) {
|
||||
printf("saving configuration failed\r\n"); osDelay(5);
|
||||
} else {
|
||||
@@ -75,6 +78,7 @@ extern "C" int load_configuration(void) {
|
||||
&trap_configs,
|
||||
&min_endstop_configs,
|
||||
&max_endstop_configs,
|
||||
&mechanical_brake_configs,
|
||||
&axis_configs)) {
|
||||
//If loading failed, restore defaults
|
||||
odrv.config_ = BoardConfig_t();
|
||||
@@ -93,6 +97,7 @@ extern "C" int load_configuration(void) {
|
||||
Axis::load_default_can_id(i, axis_configs[i]);
|
||||
min_endstop_configs[i] = Endstop::Config_t();
|
||||
max_endstop_configs[i] = Endstop::Config_t();
|
||||
mechanical_brake_configs[i] = MechanicalBrake::Config_t();
|
||||
controller_configs[i].load_encoder_axis = i;
|
||||
}
|
||||
} else {
|
||||
@@ -194,9 +199,10 @@ extern "C" int construct_objects(){
|
||||
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]);
|
||||
MechanicalBrake *mechanical_brake = new MechanicalBrake(mechanical_brake_configs[i]);
|
||||
axes[i] = new Axis(i, hw_configs[i].axis_config, axis_configs[i],
|
||||
*encoder, *sensorless_estimator, *controller, *fet_thermistor,
|
||||
*motor_thermistor, *motor, *trap, *min_endstop, *max_endstop);
|
||||
*motor_thermistor, *motor, *trap, *min_endstop, *max_endstop, *mechanical_brake);
|
||||
|
||||
controller_configs[i].parent = controller;
|
||||
encoder_configs[i].parent = encoder;
|
||||
@@ -204,6 +210,7 @@ extern "C" int construct_objects(){
|
||||
motor_configs[i].parent = motor;
|
||||
min_endstop_configs[i].parent = min_endstop;
|
||||
max_endstop_configs[i].parent = max_endstop;
|
||||
mechanical_brake_configs[i].parent = mechanical_brake;
|
||||
axis_configs[i].parent = axes[i];
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <odrive_main.h>
|
||||
|
||||
MechanicalBrake::MechanicalBrake(MechanicalBrake::Config_t& config)
|
||||
: config_(config) {
|
||||
update_config();
|
||||
}
|
||||
|
||||
bool MechanicalBrake::get_state() {
|
||||
return mechanical_brake_state_;
|
||||
}
|
||||
|
||||
void MechanicalBrake::update_config() {
|
||||
set_enabled(config_.enabled);
|
||||
}
|
||||
|
||||
void MechanicalBrake::engage() {
|
||||
uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num);
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num);
|
||||
HAL_GPIO_WritePin(gpio_port, gpio_pin, config_.is_active_low ? GPIO_PIN_RESET : GPIO_PIN_SET );
|
||||
}
|
||||
|
||||
void MechanicalBrake::release() {
|
||||
uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num);
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num);
|
||||
HAL_GPIO_WritePin(gpio_port, gpio_pin, config_.is_active_low ? GPIO_PIN_SET : GPIO_PIN_RESET );
|
||||
}
|
||||
|
||||
void MechanicalBrake::set_enabled(bool enable) {
|
||||
if (config_.gpio_num != 0) {
|
||||
uint16_t gpio_pin = get_gpio_pin_by_pin(config_.gpio_num);
|
||||
GPIO_TypeDef* gpio_port = get_gpio_port_by_pin(config_.gpio_num);
|
||||
if (enable) {
|
||||
HAL_GPIO_DeInit(gpio_port, gpio_pin);
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
GPIO_InitStruct.Pin = gpio_pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStruct.Pull = config_.pulldown ? GPIO_PULLDOWN : GPIO_PULLUP;
|
||||
HAL_GPIO_Init(gpio_port, &GPIO_InitStruct);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef __MECHANICAL_BRAKE_HPP
|
||||
#define __MECHANICAL_BRAKE_HPP
|
||||
|
||||
class MechanicalBrake {
|
||||
public:
|
||||
struct Config_t {
|
||||
uint16_t gpio_num = 0;
|
||||
bool enabled = false;
|
||||
bool is_active_low = true;
|
||||
bool pulldown = true;
|
||||
|
||||
// custom setters
|
||||
MechanicalBrake* parent = nullptr;
|
||||
void set_gpio_num(uint16_t value) { gpio_num = value; parent->update_config(); }
|
||||
void set_enabled(uint32_t value) { enabled = value; parent->update_config(); }
|
||||
};
|
||||
|
||||
explicit MechanicalBrake(MechanicalBrake::Config_t& config);
|
||||
|
||||
MechanicalBrake::Config_t& config_;
|
||||
Axis* axis_ = nullptr;
|
||||
|
||||
void update_config();
|
||||
void set_enabled(bool enabled);
|
||||
|
||||
void update();
|
||||
bool get_state();
|
||||
void release();
|
||||
void engage();
|
||||
|
||||
bool mechanical_brake_state_ = true;
|
||||
|
||||
private:
|
||||
bool pin_state_ = false;
|
||||
};
|
||||
#endif // __MECHANICAL_BRAKE_HPP
|
||||
@@ -210,6 +210,7 @@ enum TimingLog_t {
|
||||
#include <thermistor.hpp>
|
||||
#include <trapTraj.hpp>
|
||||
#include <endstop.hpp>
|
||||
#include <mechanical_brake.hpp>
|
||||
#include <axis.hpp>
|
||||
#include <communication/communication.h>
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ build{
|
||||
'MotorControl/thermistor.cpp',
|
||||
'MotorControl/encoder.cpp',
|
||||
'MotorControl/endstop.cpp',
|
||||
'MotorControl/mechanical_brake.cpp',
|
||||
'MotorControl/controller.cpp',
|
||||
'MotorControl/sensorless_estimator.cpp',
|
||||
'MotorControl/trapTraj.cpp',
|
||||
|
||||
@@ -364,6 +364,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.
|
||||
@@ -830,7 +831,6 @@ interfaces:
|
||||
accel_limit: float32
|
||||
decel_limit: float32
|
||||
|
||||
|
||||
ODrive.Endstop:
|
||||
c_is_class: True
|
||||
attributes:
|
||||
@@ -845,6 +845,16 @@ interfaces:
|
||||
pullup: 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}
|
||||
enabled: {type: bool, c_setter: set_enabled}
|
||||
is_active_low: bool
|
||||
pulldown: bool
|
||||
|
||||
valuetypes:
|
||||
ODrive.Can.Protocol:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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
|
||||
enabled | boolean | false
|
||||
is_active_low | boolean | true
|
||||
pulldown | 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>
|
||||
```
|
||||
|
||||
### enabled
|
||||
Enables/disables the operation of the mechanical brake.
|
||||
```
|
||||
<odrv>.<axis>.mechanical_brake.config.enabled = <True, False>
|
||||
```
|
||||
|
||||
### 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...
|
||||
|
||||
### pulldown
|
||||
If `true`, it enables the GPIO pulldown resistor. If `false`, it enables the GPIO pullup resistor.
|
||||
|
||||
### 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.
|
||||
|
||||
```
|
||||
<odrv>.<axis>.mechanical_brake.config.gpio_num = 5
|
||||
<odrv>.<axis>.mechanical_brake.config.enabled = True
|
||||
```
|
||||
|
||||
Don't forget to save and reboot:
|
||||
```
|
||||
<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 enabled, it will be 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()
|
||||
```
|
||||
Reference in New Issue
Block a user