Troubles with CAN bus, ODrive 3.6

I have three ODrive 3.6’s linked together in a CAN bus and haven’t been able to get any movement out of the motors through the bus. All ODrives work as expected through USB, and I’ve set the IDs of all axes to be unique (0 – 5). There is a 120 ohm resistor on both ends of the bus.

I’m using this USB-to-CAN adapter, set to the “slcan” mode. The following code works to print the heartbeat of all connected ODrives:

import can

bus = can.interface.Bus(interface='slcan', channel='COM12', bitrate=250000)

# Listen for 10 messages
for _ in range(10):
    msg = bus.recv()
    print(f"Received message: {msg}")

bus.shutdown()

And I can use the dbc database to decode the messages:

import can
import cantools

db = cantools.database.load_file("ODrive_CAN.dbc")

bus = can.interface.Bus(interface='slcan', channel='COM12', bitrate=250000)

# Listen for 10 messages
for _ in range(10):
    msg = bus.recv()
    print(f"Received message: {msg}")
    decoded_msg = db.decode_message(msg.arbitration_id, msg.data)
    print(f"Decoded message: {decoded_msg}")
    print()

bus.shutdown()

But whenever I try to send anything to the ODrives, nothing happens… Here’s an example of what I’ve been trying:

import can
import cantools

db = cantools.database.load_file("ODrive_CAN.dbc")

bus = can.interface.Bus(interface='slcan', channel='COM12', bitrate=250000)
axisID = 0x5  # I have tried all values from 0x0 to 0x9 to no avail

print("\nRequesting AXIS_STATE_FULL_CALIBRATION_SEQUENCE (0x03) on axisID: " + str(axisID))
msg = db.get_message_by_name('Axis5_Set_Axis_State')
data = msg.encode({'Axis_Requested_State': 0x03})
msg = can.Message(arbitration_id=msg.frame_id | axisID << 5, is_extended_id=False, data=data)
# msg = can.Message(arbitration_id=axisID << 5 | 0x07, data=[3, 0, 0, 0, 0, 0, 0, 0], dlc=8, is_extended_id=False)  # Neither of these have worked
print(db.decode_message('Axis5_Set_Axis_State', msg.data))
print(msg)

try:
    bus.send(msg)
    print("Message sent on {}".format(bus.channel_info))
except can.CanError:
    print("Message NOT sent!  Please verify can0 is working first")

print("Waiting for calibration to finish...")

bus.shutdown()

I have been able to get correct data back from the ODrives (via. the heartbeat messages) such as current state (I change it with USB, then check it with CAN), position, etc. but it seems like nothing I send is being received. At least, nothing physical is indicating reception of my messages.

I have tried everything I can think of from both the can_example and can_dbc_example examples (as well as the docs), but nothing has worked.

I am very new to CAN and I’ve tried to include all relevant information; I apologise if anything is missing.

Does anyone have any ideas of what I could try to move forward?

Thanks in advance!

Saw your YouTube video.

I’m wondering if what you’re sending isn’t actually bytes. I’ve been playing with a RS485 bus recently and was having similar problems.

Might be worth checking your types are what you think they are? Here’s an example of me sending a byte array to my bus:

data_bytes = [0x00,0x00,0x01]
data = bytes(data_bytes)
bus.write_bytes(data) # write_bytes is simply a wrapped call to the Serial lib.

Hope this helps :slight_smile:

Are you sure that the termination resistors are set up correctly? I think both the odrives and your CAN adapter have built-in termination resistors available. Are you using your own termination resistors? Have you checked the built-in ones?

If I were you I would also try simplifying the problem/eliminating variables. Have you tried running just a single drive via CAN?

I’m fairly confident that the termination resistors are set correctly; the jumper is on the USB adapter, and the dip switches are off for all ODrives except the last one in the chain.

I agree on simplifying/eliminating variables. I’ve tried a single ODrive (updating termination resistors appropriately), having the ODrive in different starting states, assigning different IDs, different comm. rates, and probably others that I can’t remember off the top of my head.

Really not sure what’s going on!

P.S. To address what Peter suggested, I haven’t had a chance to check the data types yet, so I’ll be doing that first thing when I get time.

Spent another day trying to crack this nut, to no avail. While I understand CAN theory much better now, I am still facing the same issue.

To add more detail to my setup:

  • I have tried both a twisted pair of just CANH and CANL, as well as my default setup with a three-core shielded cable (braided + foil shield), with CANH, CANL and GND connected to my USB > CAN adapter

  • My USB > CAN adapter is plugged into my Win10 PC via a USB isolator (I don’t think this is necessary, but I’ve already got it set up for the USB connection so I figure I may as well use it for CAN)

  • 120 ohm resistors are definitely set correctly.

  • Reading messages works; changing the rate of the heartbeat messages works.

  • Messages are of the type " class ‘can.message.Message’ ", which I believe is correct for the python-can library

  • ODrives are v3.6 and are up-to-date on the firmware (v0.5.6)

  • Not sure if this is relevant, but each of the ODrives appears (via USB) as eg. “unknown device 2079395B5841”. I think this is supposed to be “odrive 2079395B5841”?

The minimum working example of my code is:

import can
from can.interfaces.slcan import slcanBus
import cantools

db = cantools.database.load_file("ODrive_CAN.dbc")
bus = slcanBus(channel='COM12', bitrate=250000)
axisID = 0x04

msg = can.Message(arbitration_id=(axisID << 5 | 0x007), data=[8, 0, 0, 0, 0, 0, 0, 0], dlc=8, is_extended_id=False)
print("Message: ", msg, ". Type: ", type(msg))

try:
    bus.send(msg)
    print("Message sent on {}".format(bus.channel_info))
except can.CanError:
    print("Message NOT sent!  Please verify can0 is working first")

# Listen for 10 messages
for _ in range(10):
    msg_rec = bus.recv()
    print(f"Received message: {msg_rec}")
    decoded_msg = db.decode_message(msg.arbitration_id, msg_rec.data)
    print(f"Decoded message: {decoded_msg}")
    print()

bus.shutdown()
print("Shutdown")

The result of running this code for me is that nothing physical happens. 10 messages will print in the console, but no motor will do anything. I’ve tried sending all sorts of commands (“enter closed-loop control”, “enter calibration mode”, “get encoder position” etc) with no effect.

I massively appreciate any help or suggestions for possible things to try.

I don’t have any experience with the code library you’re using, so without getting into that can you rule out that the adapter is working in transmit mode at all?
Do you have a scope, logic analyzer, or just a fast multimeter? Make sure your CAN packets are actually getting sent. CAN_H and CAN_L should both be held at 2.5v when idle, and driven to 0 or 5V when a frame is sent. Test this with just your CAN adapter not plugged into the motors or anything else on the bus (just the 120O terminator)

If that works, test again with just one motor isolated, and then the whole system. If it’s confirmed not an electrical problem, then do you have a spare adapter? see if you can read back the same frame you’re sending on the first properly. If all of that looks good, then it’s more likely an issue with the motors themselves.

+1 for the approach of adding another CAN adapter to listen on the bus. It can be a scope/logic analyzer, but it will make your life easier if it translates the raw CAN traffic into message packets. Look for the transmit messages on the bus. They may not be there at all, may not be readable due to hardware later issues. If there is no acknowledge on the bus, the device will attempt to resend packets, you may see them repeated or they may be followed by additional packets that may help answer what’s going on. If you just see the packets you expect, then the question is why the Odrive isn’t reading them.

Good luck!

1 Like

That’s a very good idea! I don’t have another CAN adapter handy, but I do have an oscilloscope (though am very unfamiliar with it. This seems like a good learning opportunity!) I’ll hopefully have time later this week to play around with it.

Thanks for the suggestion!

Is the .dbc you’re using up to date (has 8 copies of every message, one per axis?)

Finally had a chance to do some more work on this.

Re. Oscilloscope: leaving everything as-is (standard heartbeat messages are ON) shows data going through the bus, as expected:

And when I turn off the heartbeat messages and send a message repeatedly, it looks like that data is being sent:

For reference, this is the code I was running for the above:

import can
from can.interfaces.slcan import slcanBus
import cantools

db = cantools.database.load_file("ODrive_CAN.dbc")
bus = slcanBus(channel='COM12', bitrate=250000)
axisID = 0x04

msg = can.Message(arbitration_id=(axisID << 5 | 0x007), data=[8, 0, 0, 0, 0, 0, 0, 0], dlc=8, is_extended_id=False)
print("Message: ", msg, ". Type: ", type(msg))

while 1:
    try:
        bus.send(msg)
        print("Message sent on {}".format(bus.channel_info))
    except can.CanError:
        print("Message NOT sent!  Please verify can0 is working first")

So I guess this means the data is being sent to the wrong address? Not really sure where the error could be coming from though. Axis IDs have been set correctly (I made sure they were saved after setting them).

Re. .dbc file: Yep - every message has 8 copies:

I wouldn’t think this would affect my current situation though, as I’m only using the dbc file for decoding the received message, not sending it in the first place. (I know that it’s better to use the dbc file for sending messages as well, but I’m trying to reduce variables)

PS. If anyone knows how to get cleaner oscilloscope readings, I’d love to know! Oscilloscopes/electronics in general are rather foreign to me :sweat_smile:

I recently remembered that I have a Teensy 4.0 lying around so I’m planning to set that up as the controller so that I can listen in using the USB-CAN adapter. Hopefully will have time to try that next week.

As always, I am very grateful for any guidance or suggestions of what to try next!

Did you resolve this?
Im having similar difficulty.
I can send message for VBus voltage and receieve the correct data from odrv0.
But I cant get a input_pos to axis 1 working in anyway.
I can verify the messages are sending and receiving ok with cansend and cansniffer.

Nope, and I probably won’t. I recently bought an NVIDIA Jetson Orin Nano Dev Kit and once I figure out how to get that set up, I’m hoping it won’t have the same CAN issues as my PC. Will update here when I get a chance to try it out.

I’ve finally had a chance to give this a test with the Jetson and all is working fine. No idea what’s causing the issues with Win10, but not a problem for me anymore.

I guess the solution is to just use Linux? Good luck to anyone who experiences this issue in the future!

1 Like