I am glad to work with the ODrive S1 to drive a differential robot. Using ramped velocity control, the robot exactly drives around how I want. I am using a Raspberry Pi and a Waveshare RS485 CAN HAT to control the ODrive S1 via the CAN bus (1 Mbps).
However, I ran into a strange issue and hope someone knows a solution.
When the motors are engaged and I want to set a motor velocity to (for example) 0.5 m/s, the motors do so instantly (nearly a second). However, the velocity estimate (Vel_Estimate) is somehow lagging. As you can see in the graphs below, the velocity estimates (I converted it to m/s) slowly build up, although the motor already reached the expected velocity. It can take like 15 seconds until I see the correct velocity. Is there a reason why I receive the values so late?
To bisect the issue between CAN communcation delays, or velocity estimator issues, can you plug in USB on the ODrive and plot the velocity in the web gui?
If the issue exists in the gui also: check the axis.config.encoder_bandwidth make sure it’s something between 100 and 1000.
If the issue doesn’t exist in the GUI: I would suggest looking for issues with your raspberry pi host code, and try CAN debug utils like candump to see what might be going on.
If all else fails, try erasing the ODrive and setting it up again.
Thank you very much for the hints and your quick answer!
The ODrive is working correctly. The web GUI shows an instant velocity estimate change. Also, candump reveals that the packages are correctly sent by the ODrive over time.
I need to debug my code and Raspberry settings. Let’s see.
Apparently I made a rookie mistake and just used the RX code from the tutorial without thinking. My Python function was as follows (abstracted it a bit to make it universal).
def send_rx_cmd(command_id, data_format, data_bytes):
for msg in bus:
if msg.arbitration_id == (node_id << node_id_offset | command_id):
return struct.unpack(data_format, bytes(msg.data[:data_bytes]))
_, velocity = send_rx_cmd(0x09, "<ff", 8)
I didn’t have time to dive deeper into the can library, but I guess this code always returns the first message in the CAN queue. Thus, the queue has to fill completely and new messages need to throw out the result so that there is a new one. Obviously, the signal I measured lagged.
I then changed the code to compare the current time with the CAN message time. Take only the messages from the last 1 ms. This is my new code:
def send_rx_cmd(command_id, data_format, data_bytes, read_period):
for msg in bus:
if msg.arbitration_id == (node_id << node_id_offset | command_id):
if time.time() - msg.timestamp <= read_period:
return struct.unpack(data_format, bytes(msg.data[:data_bytes]))
_, velocity = send_rx_cmd(0x09, "<ff", 8, .001)
Works like a charm! I mean you could always optimize the code (e.g. with a Listener) but it’s working to my requirements.