diff --git a/.github/actions/release-firmware/action.yml b/.github/actions/release-firmware/action.yml index 0ce949da..1cd85e7a 100644 --- a/.github/actions/release-firmware/action.yml +++ b/.github/actions/release-firmware/action.yml @@ -19,6 +19,9 @@ runs: cd ${{ github.workspace }}/Firmware + mkdir -p autogen + python ../tools/odrive/version.py --output autogen/version.c + echo "CONFIG_STRICT=true" > tup.config echo "CONFIG_BOARD_VERSION=${{ inputs.board_version }}" >> tup.config tup init diff --git a/Dockerfile b/Dockerfile index 1f9a5840..e3bcbfd0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,10 @@ WORKDIR ODrive/Firmware # Must attach the firmware tree into the container CMD \ + # Regenerate autogen/version.c + mkdir -p autogen && \ + python ../tools/odrive/version.py \ + --output autogen/version.c && \ # Regenerate python interface mkdir ../Firmware/autogen; \ python interface_generator_stub.py \ diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 4149b30d..546847ed 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -209,7 +209,7 @@ bool Controller::update() { if (axis_->trap_traj_.t_ > axis_->trap_traj_.Tf_) { // Drop into position control mode when done to avoid problems on loop counter delta overflow config_.control_mode = CONTROL_MODE_POSITION_CONTROL; - pos_setpoint_ = input_pos_; + pos_setpoint_ = axis_->trap_traj_.Xf_; vel_setpoint_ = 0.0f; torque_setpoint_ = 0.0f; trajectory_done_ = true; diff --git a/dockerbuild.sh b/dockerbuild.sh index 62959930..87b51648 100755 --- a/dockerbuild.sh +++ b/dockerbuild.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash function cleanup { echo "Removing previous build artifacts" rm -rf build/ Firmware/autogen Firmware/build Firmware/.tup diff --git a/docs/can-guide.md b/docs/can-guide.md index 827d3635..e008e940 100644 --- a/docs/can-guide.md +++ b/docs/can-guide.md @@ -10,8 +10,12 @@ Borrowing from [Wikipeda](https://en.wikipedia.org/wiki/CAN_bus): In simple terms, CAN is a way of communicating between many devices over a single twisted pair of wires. The signal is transmitted as the difference in voltage between the two wires (differential signalling), which makes it very robust against noise. Instead of using a unique address (like I2C) or a select pin (like SPI), CAN *messages* have a unique ID that also acts as the priority. At the beginning of a message frame, all devices talk and read at the same time. As the message ID is transmitted, the lowest value "wins" and that message will be transmitted (ID **0** has the *highest* priority). All other devices will wait for the next chance to send. If two devices send the same message ID at the same time, they will conflict and a bus failure may occur. Make sure your devices can never send the same message ID at the same time! +See also this great article from Danfoss that quickly describes how to put together the wiring for a CAN bus https://danfosseditron.zendesk.com/hc/en-gb/articles/360042232992-CAN-bus-physical-layer + ![CAN picture](screenshots/CAN_Bus_Drawing.png) + + ## Why use CAN? CAN is convenient for its simple and robust Physical Layer (PHY) that requires only a twisted pair of wires and a 120ohm termination resistor at each end. It has low jitter and low latency, because there is no host computer. It is relatively fast (CAN 2.0b supports 1 Mbps). Messages are easy to configure and load with data. Transceivers and controllers are inexpensive and widely available, thanks to its use in automotive. @@ -131,4 +135,4 @@ Instead of manually writing values into the data, we can create a dictionary of The [CAN DBC Example](../tools/can_dbc_example.py) script shows you how this can be used. This is the recommended method of serializing and deserializing. -If you're using C++, then you can use the [CANHelpers](..firmware/communication/../../../Firmware/communication/can/can_helpers.hpp) single-header library to do this instead, although the DBC file isn't used. \ No newline at end of file +If you're using C++, then you can use the [CANHelpers](..firmware/communication/../../../Firmware/communication/can/can_helpers.hpp) single-header library to do this instead, although the DBC file isn't used. diff --git a/docs/can-protocol.md b/docs/can-protocol.md index ac396a0f..77008f45 100644 --- a/docs/can-protocol.md +++ b/docs/can-protocol.md @@ -84,6 +84,18 @@ All multibyte values are little endian (aka Intel format, aka least significant --- +### Cyclic Messages +Cyclic messages are sent by ODrive on a timer without a request. As of `fw0.5.4`, the Cyclic messsages are: + +ID | Name | Rate (ms) +--: | :-- | :-- +0x001 | ODrive Heartbeat Message | 100 +0x009 | Encoder Estimates | 10 + +These can be configured for each axis, see e.g. `axis.config.can`. + +--- + ### Interoperability with CANopen You can deconflict with CANopen like this: diff --git a/docs/control-modes.md b/docs/control-modes.md index a1473558..d896d6e1 100644 --- a/docs/control-modes.md +++ b/docs/control-modes.md @@ -97,4 +97,4 @@ You can now control the velocity with `axis.controller.input_vel = 1` [turn/s]. Set `axis.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL`.
You can now control the torque with `axis.controller.input_torque = 0.1` [Nm]. -Note: If you exceed `vel_limit` in torque control mode, the current is reduced. To disable this, set `axis.controller.enable_current_mode_vel_limit = False`. +Note: If you exceed `vel_limit` in torque control mode, the current is reduced. To disable this, set `axis.controller.enable_torque_mode_vel_limit = False`. diff --git a/tools/odrive_demo.py b/tools/odrive_demo.py index 38d2c69b..8ede3901 100755 --- a/tools/odrive_demo.py +++ b/tools/odrive_demo.py @@ -26,7 +26,7 @@ my_drive.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL print("Bus voltage is " + str(my_drive.vbus_voltage) + "V") # Or to change a value, just assign to the property -my_drive.axis0.controller.pos_setpoint = 3.14 +my_drive.axis0.controller.input_pos = 3.14 print("Position setpoint is " + str(my_drive.axis0.controller.pos_setpoint)) # And this is how function calls are done: @@ -38,7 +38,7 @@ t0 = time.monotonic() while True: setpoint = 4.0 * math.sin((time.monotonic() - t0)*2) print("goto " + str(int(setpoint))) - my_drive.axis0.controller.pos_setpoint = setpoint + my_drive.axis0.controller.input_pos = setpoint time.sleep(0.01) # Some more things you can try: