CAN Communication Issue

Hi all,

For my application, I would like to specify when I receive messages from my ODrive S1, so I followed the steps listed in the documentation, sending and receiving messages based on how it was explained. In my code (using CAN library in Python on a Pi 4), I am getting a message by executing this function:

try:
    can0.send(can.Message(
        arbitration_id=(node_id << 5 | 0x09), # 0x09: Get_Encoder_Estimates
        data=struct.pack('<ff', 0.0, 0.0), # velocity, torque feedforward
        is_remote_frame=True,
        is_extended_id=False
        ))
    encoder_msg = can0.recv()
    if encoder_msg.arbitration_id == (node_id << 5 | 0x09):
        P, V = struct.unpack('<ff', bytes(encoder_msg.data))
except:
    print("Motor message error...")
    return P,V

can0 is what I call the bus object:

can0 = can.interface.Bus(channel = 'can0', bustype = 'socketcan_ctypes')

My specific issue is that (seemingly at random) the code gets stuck at can0.recv(), causing the motor to get stuck at whatever velocity was set at the previous loop. I am unsure if sending a message of (0.0, 0.0) is correct. I am sending an RTR bit to avoid message send/recv issues, but I am unsure if sending (0.0,0.0) is causing the message error. Thanks!

Definitely looks like you’re dropping CAN messages somehow. What CAN interface are you using on the Pi? What bus baudrate? Two termination resistors? And why are you using RTR instead of setting the Get_Encoder_Estimates message as periodic on the ODrive?

Hi, I’m using this CAN Hat for my Pi, and it has the termination resistors connected. The baud rate is 1000000.

As for your last question, this is where my knowledge falters a little. My current main control loop should run at 200Hz. However, every once in a while, the loop takes a bit longer to run, for example dropping to 195 Hz for a second or so. For control, I thought if this occurs, the period messages will get “stuck” in the bus, and the most recent message will be stuck in the queue. I know there are better ways around this, but I thought an easy fix would just be to receive messages as needed. Perhaps I am overthinking or not understanding this right, apologies if this issue is due to me being naive in this area. Thank you.

Just to confirm, you have both the termination resistor on the HAT and also the termination resistor on the last ODrive in the CAN bus connected?

Could you post a few pictures of your wiring? What are you connecting the CAN HAT ground to?

I mainly ask because this definitely sounds like a signal integrity or wiring issue leading to a dropped message.

Generally it’s more efficient to just use periodic messages than a RTR message, and that way if something happens for you to miss it, you’ll be able to get the next one.

Hi,

Yes, both resistors are connected. I can upload a wiring diagram later tomorrow, but it is quite simple where I have two wires connected to the screw terminals attached to the HAT directly to my S1 (I am only using one ODrive currently). Its otherwise isolated from the rest of the circuit and I have ensured that the connections are stable. I was doing some error handling (added some code to the original post’s) and followed your suggestion in a way, where I skip the message drop and move on to the next loop to control. This error occurs pretty rarely, so I do not think it is a problem. However, upon figuring this out, I found a more pressing issue that isnt as easily solved. I attached the code and corresponding error message I get below.

def readMotorMsg(node_id, can0, pos, vel):
    try:
        can0.send(can.Message(
            arbitration_id=(node_id << 5 | 0x09), # 0x09: Get_Encoder_Estimates
            data=struct.pack('<ff', mpos, mvel), # position,veclocity
            is_remote_frame=True,
            is_extended_id=False
        ))
        # time.sleep(0.001)
        encoder_msg = can0.recv(timeout = 0.002)
        if encoder_msg is None:
            print('none message. skipping')
            return pos, vel
        elif encoder_msg.arbitration_id == (node_id << 5 | 0x09):
            mpos, mvel = struct.unpack('<ff', bytes(encoder_msg.data))
            return pos, vel
        else:
            print('Weird message error')
            print(encoder_msg)
            print(struct.unpack('<ff', bytes(encoder_msg.data)))
            return mpos, mvel
    except:
        print('Unexpected Error: Killing Motor')
        killMotor(can0, node_id)


This seemingly happens at random again. These messages always come in pairs like this, and if I skip that message and wait for the next one, the code goes back to outputting normal readings. Am I perhaps sending another message before the S1 can process the previous one? Causing it to bounce back? I’m unsure what could cause the signal dropping issue since the wires are pretty short and the resistors on both ends are connected, plus I twisted the CAN wires together. Again, my code isn’t the best (and I cant fully rewrite things right now), varies its loop rate by quite a lot so I’m wondering if this is cause for this issue.

Sorry I cannot upload any wiring pics/diagram at the moment but can if that would help more than the description I posted here. Thanks again for your great support, I do really appreciate it