mirror of
https://github.com/odriverobotics/ODrive.git
synced 2026-09-21 15:34:33 +08:00
Fixed null pointer checks in controller.cpp
Fixed formatting in trapTraj.hpp Fixed how pos_setpoint is set in axis.cpp to avoid transients on startup so that it will handle circular position setpoints More unit corrections in the docs: radians -> turns
This commit is contained in:
@@ -320,9 +320,14 @@ bool Axis::run_closed_loop_control_loop() {
|
||||
}
|
||||
|
||||
// To avoid any transient on startup, we intialize the setpoint to be the current position
|
||||
// TODO: use circular src if in circular mode.
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_;
|
||||
controller_.input_pos_ = *controller_.pos_estimate_linear_src_;
|
||||
if(controller_.config_.circular_setpoints == true) {
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_;
|
||||
controller_.input_pos_ = *controller_.pos_estimate_circular_src_;
|
||||
}
|
||||
else {
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_;
|
||||
controller_.input_pos_ = *controller_.pos_estimate_linear_src_;
|
||||
}
|
||||
|
||||
// Avoid integrator windup issues
|
||||
controller_.vel_integrator_torque_ = 0.0f;
|
||||
@@ -372,8 +377,13 @@ bool Axis::run_homing() {
|
||||
}
|
||||
|
||||
// To avoid any transient on startup, we intialize the setpoint to be the current position
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_;
|
||||
|
||||
// note - input_pos_ is not set here. It is set to 0 earlier in this method and velocity control is used.
|
||||
if(controller_.config_.circular_setpoints == true) {
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_circular_src_;
|
||||
}
|
||||
else {
|
||||
controller_.pos_setpoint_ = *controller_.pos_estimate_linear_src_;
|
||||
}
|
||||
// Avoid integrator windup issues
|
||||
controller_.vel_integrator_torque_ = 0.0f;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ bool Controller::update(float* torque_setpoint_output) {
|
||||
}
|
||||
|
||||
// TODO also enable circular deltas for 2nd order filter, etc.
|
||||
if (config_.circular_setpoints && pos_estimate_circular) {
|
||||
if (config_.circular_setpoints) {
|
||||
// Keep pos setpoint from drifting
|
||||
input_pos_ = fmodf_pos(input_pos_, config_.circular_setpoint_range);
|
||||
}
|
||||
@@ -226,18 +226,22 @@ bool Controller::update(float* torque_setpoint_output) {
|
||||
float vel_des = vel_setpoint_;
|
||||
if (config_.control_mode >= CONTROL_MODE_POSITION_CONTROL) {
|
||||
float pos_err;
|
||||
if (!pos_estimate_linear || !pos_estimate_circular) {
|
||||
set_error(ERROR_INVALID_ESTIMATE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config_.circular_setpoints && pos_estimate_circular) {
|
||||
if (config_.circular_setpoints) {
|
||||
if(!pos_estimate_circular) {
|
||||
set_error(ERROR_INVALID_ESTIMATE);
|
||||
return false;
|
||||
}
|
||||
// Keep pos setpoint from drifting
|
||||
pos_setpoint_ = fmodf_pos(pos_setpoint_, *pos_wrap_src_);
|
||||
// Circular delta
|
||||
pos_err = pos_setpoint_ - *pos_estimate_circular;
|
||||
pos_err = wrap_pm(pos_err, 0.5f * *pos_wrap_src_);
|
||||
} else {
|
||||
if(!pos_estimate_linear) {
|
||||
set_error(ERROR_INVALID_ESTIMATE);
|
||||
return false;
|
||||
}
|
||||
pos_err = pos_setpoint_ - *pos_estimate_linear;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
class TrapezoidalTrajectory {
|
||||
public:
|
||||
struct Config_t {
|
||||
float vel_limit = 2.0f; // [turn/s]
|
||||
float accel_limit = 0.5f; // [turn/s^2]
|
||||
float decel_limit = 0.5f; // [turn/s^2]
|
||||
float vel_limit = 2.0f; // [turn/s]
|
||||
float accel_limit = 0.5f; // [turn/s^2]
|
||||
float decel_limit = 0.5f; // [turn/s^2]
|
||||
};
|
||||
|
||||
struct Step_t {
|
||||
|
||||
+2
-2
@@ -44,8 +44,8 @@ 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:
|
||||
|
||||
* `<axis>.controller.input_pos = <radians>`
|
||||
* `<axis>.controller.input_vel = <radians/s>`
|
||||
* `<axis>.controller.input_pos = <turn>`
|
||||
* `<axis>.controller.input_vel = <turn/s>`
|
||||
* `<axis>.controller.input_torque = <torque in Nm>`
|
||||
|
||||
Modes can be selected by changing `<axis>.controller.config.input_mode`.
|
||||
|
||||
@@ -250,7 +250,7 @@ Let's get motor 0 up and running. The procedure for motor 1 is exactly the same,
|
||||
</div></details>
|
||||
|
||||
2. Type `odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL` <kbd>Enter</kbd>. From now on the ODrive will try to hold the motor's position. If you try to turn it by hand, it will fight you gently. That is unless you bump up `odrv0.axis0.motor.config.current_lim`, in which case it will fight you more fiercely. If the motor begins to vibrate either immediately or after being disturbed you will need to [lower the controller gains](control.md).
|
||||
3. Send the motor a new position setpoint. `odrv0.axis0.controller.input_pos = 10` <kbd>Enter</kbd>. The units are in radians.
|
||||
3. Send the motor a new position setpoint. `odrv0.axis0.controller.input_pos = 1` <kbd>Enter</kbd>. The units are in turns.
|
||||
4. At this point you will probably want to [Properly tune](control.md) the motor controller in order to maximize system performance.
|
||||
|
||||
## Other control modes
|
||||
@@ -273,7 +273,7 @@ Asking the ODrive controller to go as hard as it can to raw setpoints may result
|
||||
You can use the second order position filter in these cases.
|
||||
Set the filter bandwidth: `axis.controller.config.input_filter_bandwidth = 2.0` [1/s]<br>
|
||||
Activate the setpoint filter: `axis.controller.config.input_mode = INPUT_MODE_POS_FILTER`.<br>
|
||||
You can now control the velocity with `axis.controller.input_pos = 10` [radians].
|
||||
You can now control the velocity with `axis.controller.input_pos = 1` [turns].
|
||||
|
||||
<br>
|
||||
Step response of a 1000 to 0 position input with a filter bandwidth of 1.0 [/sec].
|
||||
@@ -294,9 +294,9 @@ In the above image blue is position and orange is velocity.
|
||||
```
|
||||
|
||||
`vel_limit` is the maximum planned trajectory speed. This sets your coasting speed.<br>
|
||||
`accel_limit` is the maximum acceleration in radians / sec^2<br>
|
||||
`decel_limit` is the maximum deceleration in radians / sec^2<br>
|
||||
`controller.config.inertia` is a value which correlates acceleration (in counts / sec^2) and motor current. It is 0 by default. It is optional, but can improve response of your system if correctly tuned. Keep in mind this will need to change with the load / mass of your system.
|
||||
`accel_limit` is the maximum acceleration in turns / sec^2<br>
|
||||
`decel_limit` is the maximum deceleration in turns / sec^2<br>
|
||||
`controller.config.inertia` is a value which correlates acceleration (in turns / sec^2) and motor current. It is 0 by default. It is optional, but can improve response of your system if correctly tuned. Keep in mind this will need to change with the load / mass of your system.
|
||||
|
||||
All values should be strictly positive (>= 0).
|
||||
|
||||
@@ -333,7 +333,7 @@ To enable Circular position control, set `axis.controller.config.circular_setpoi
|
||||
This mode is useful for continuous incremental position movement. For example a robot rolling indefinitely, or an extruder motor or conveyor belt moving with controlled increments indefinitely.
|
||||
In the regular position mode, the `input_pos` would grow to a very large value and would lose precision due to floating point rounding.
|
||||
|
||||
In this mode, the controller will try to track the position within only one turn of the motor. Specifically, `input_pos` is expected in the range `[0, 1]`. If the `input_pos` is incremented to outside this range (say via step/dir input), it is automatically wrapped around into the correct value.
|
||||
In this mode, the controller will try to track the position within only one turn of the motor. Specifically, `input_pos` is expected in the range `[0, 1)`. If the `input_pos` is incremented to outside this range (say via step/dir input), it is automatically wrapped around into the correct value.
|
||||
Note that in this mode `encoder.pos_circular` is used for feedback instead of `encoder.pos_estimate`.
|
||||
|
||||
If you try to increment the axis with a large step in one go that exceeds `1` turn, the motor will go to the same angle around the wrong way. This is also the case if there is a large disturbance. If you have an application where you would like to handle larger steps, you can use a larger circular range. Set `controller.config.circular_setpoints_range = N`. Choose N to give you an appropriate circular space for your application.
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@ You can control the ODrive directly from an hobby RC receiver.
|
||||
Some GPIO pins can be used for PWM input, if they are not allocated to other functions. For example, you must disable the UART to use GPIO 1,2. See the [pin function priorities](#pin-function-priorities) for more detail.
|
||||
|
||||
Any of the numerical parameters that are writable from the ODrive Tool can be hooked up to a PWM input.
|
||||
As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 radians.
|
||||
As an example, we'll configure GPIO4 to control the angle of axis 0. We want the axis to move within a range of -2 to 2 turns.
|
||||
|
||||
1. Make sure you're able control the axis 0 angle by writing to `odrv0.axis0.controller.input_pos`. If you need help with this follow the [getting started guide](getting-started.md).
|
||||
2. If you want to control your ODrive with the PWM input without using anything else to activate the ODrive, you can configure the ODrive such that axis 0 automatically goes operational at startup. See [here](commands.md#startup-procedure) for more information.
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ For example, to plot the approximate motor torque [N.cm] and the velocity [RPM]
|
||||
# If you want to plot different values, change them here.
|
||||
# You can plot any number of values concurrently.
|
||||
cancellation_token = start_liveplotter(lambda: [
|
||||
((my_odrive.axis0.encoder.vel_estimate*60/(6.2832)), # radians to rpm
|
||||
((my_odrive.axis0.encoder.vel_estimate*60), # turns/s to rpm
|
||||
((my_odrive.axis0.motor.current_control.Iq_setpoint * my_odrive.axis0.motor.config.torque_constant), # Torque [Nm]
|
||||
])
|
||||
```
|
||||
|
||||
@@ -30,10 +30,10 @@ def print_help(args, have_devices):
|
||||
print('Type "odrv0." and press <tab>')
|
||||
print('This will present you with all the properties that you can reference')
|
||||
print('')
|
||||
print('For example: "odrv0.motor0.encoder.pos_estimate"')
|
||||
print('will print the current encoder position on motor 0')
|
||||
print('and "odrv0.motor0.input_pos = 10000"')
|
||||
print('will send motor0 to 10000')
|
||||
print('For example: "odrv0.axis0.encoder.pos_estimate"')
|
||||
print('will print the current encoder position on axis 0')
|
||||
print('and "odrv0.axis0.controller.input_pos = 0.5"')
|
||||
print('will send axis 0 to 0.5 turns')
|
||||
print('')
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user