Skip to content

Conversation

@dankirsdot
Copy link
Contributor

I believe it might be useful to add an example demonstrating how to handle the watchdog timer. While I understand the motivation for keeping the original can_simple.py example truly simple, I consider it necessary to illustrate safe practices when working with motors and to demonstrate the concept of the watchdog timer and its proper handling. I did not find a similar example in this repository; please direct me to it if I have overlooked one, and I apologize in advance.

Note: This example uses the deprecated bustype parameter for bus initialization, consistent with existing scripts. This should be updated to interface if my prior pull request addressing the deprecation is accepted and merged.

The script has been tested on Python 3.14 with python-can 4.4.0.

I welcome feedback and am available to make further adjustments. Thank you for considering this contribution.

@CLAassistant
Copy link

CLAassistant commented Nov 26, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Member

@samuelsadok samuelsadok left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the point of demonstrating watchdog feeding. I would actually argue that it can be added to can_simple.py, because I expect that it's possible by adding only ~5 more lines of code or so. And feeding the watchdog is not a special message, it's just sending setpoints continuously, which most users would do anyway. This would save us from having to maintain two very similar scripts.

Can you make a version that addresses the comments below, but integrates it into can_simple.py?


while True:
t = time.time()
velocity_setpoint = math.sin(2 * math.pi * f * t) # turns / s
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not really comment on the sinusoidal signal we supply the ODrive with, but I think its calculation should be straightforward. I also imported the whole module math instead of importing sin and pi. Looks OK to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the math is not what we're showing off, we can compress this into one line. Also use time.monotonic() instead of .time().

Suggested change
velocity_setpoint = math.sin(2 * math.pi * f * t) # turns / s
velocity_setpoint = math.sin(2 * math.pi * 0.5 * time.monotonic()) # turns / s


while True:
t = time.time()
velocity_setpoint = math.sin(2 * math.pi * f * t) # turns / s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the math is not what we're showing off, we can compress this into one line. Also use time.monotonic() instead of .time().

Suggested change
velocity_setpoint = math.sin(2 * math.pi * f * t) # turns / s
velocity_setpoint = math.sin(2 * math.pi * 0.5 * time.monotonic()) # turns / s

Comment on lines 45 to 46
# Define an asynchronous listener to handle and print encoder feedback
def encoder_listener(msg):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Define an asynchronous listener to handle and print encoder feedback
def encoder_listener(msg):
# Handler for incoming CAN messages to print encoder feedback
def on_rx_message(msg: can.Message):

Comment on lines 2 to 8
Example for controlling an ODrive via the CANSimple protocol with watchdog handling.
Puts the ODrive into closed loop control mode, sends a velocity setpoint of 1.0
and then prints the encoder feedback.
Puts the ODrive into closed loop control mode, sends periodic sinusoidal velocity
setpoints, and asynchronously prints the encoder feedback. The regular velocity
setpoint update resets the watchdog, which is a safety feature that disengages
the axis if no valid commands are received within the configured timeout period,
avoiding uncontrolled behavior.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I think about it, watchdog handling is just a side effect of continuous setpoints. There's nothing that's explicitly for watchdog handling and it works just as well with the watchdog disabled. So we don't have to mention it in the headline and most of the description can stay as it was (except that it's now a sine function instead of constant vel).

You can mention the watchdog as a side thought in just a small paragraph, such as:

If the watchdog is enabled on the ODrive, it is fed implicitly by the continuous velocity setpoint message and the motor will stop when the script is terminated. The heartbeat interval should be shorter than the watchdog timeout to ensure timely confirmation of the axis entering closed loop control mode without triggering the watchdog.

…_rx_message, and updated docstring for watchdog handling and assumptions
@dankirsdot
Copy link
Contributor Author

Hello, Samuel! I apologize for the long delay in responding, it took quite long to return to this. Thank you for your suggestions regarding the docstring, monotonic time, math simplification, and renaming the notifier callback. They all make sense.

I believe I have addressed all of them in the latest commit. Please take a look when you have a moment!

@samuelsadok
Copy link
Member

samuelsadok commented Dec 10, 2025

Looks good now! Can you confirm on real hardware that the script works as intended and the axis moves at sinusoidal velocity? For these demo scripts we don't have automated tests right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants