Merge pull request #590 from odriverobotics/CAN_Guide

Add CAN Guide
This commit is contained in:
Oskar Weigl
2021-06-28 18:42:02 -07:00
committed by GitHub
12 changed files with 594 additions and 10 deletions
+6
View File
@@ -8,6 +8,12 @@ Please add a note of your changes below this heading if you make a Pull Request.
* ASCII protocol commands with multiline responses (`i`, `h`) now return the expected response (in v0.5.2 the response was corrupted)
* odrivetool no longer shows the message `<Task pending coro=... running at ...>` when closing
# Releases
## [0.5.3] - unreleased
### Fixed
* ASCII protocol commands with multiline responses (`i`, `h`) now return the expected response (in v0.5.2 the response was corrupted)
* odrivetool no longer shows the message `<Task pending coro=... running at ...>` when closing
# Releases
## [0.5.2] - 2021-05-21
+2
View File
@@ -29,6 +29,8 @@ sections:
url: /hoverboard
- title: Migration Guide
url: /migration
- title: CAN Guide
url: /can-guide
- title: Interfaces & Protocols
docs:
- title: Pinout
+134
View File
@@ -0,0 +1,134 @@
# CAN Bus Guide for ODrive
ODrive v3 supports CAN 2.0b. We've built a [simple protocol](can-protocol.md) (named CANSimple) so that most ODrive functions can be controlled without a full CAN Open or similar stack. This guide is intended for beginners to set up CAN on the ODrive and on their host device. We will be focusing on Raspberry Pi and Arduino-compatible devices using the MCP2515 CAN Controller.
## What is CAN bus?
Borrowing from [Wikipeda](https://en.wikipedia.org/wiki/CAN_bus):
> A Controller Area Network (CAN bus) is a robust vehicle bus standard designed to allow microcontrollers and devices to communicate with each other's applications without a host computer. It is a message-based protocol, designed originally for multiplex electrical wiring within automobiles to save on copper, but it can also be used in many other contexts. For each device, the data in a frame is transmitted sequentially but in such a way that if more than one device transmits at the same time, the highest priority device can continue while the others back off. Frames are received by all devices, including by the transmitting device.
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!
![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.
## Hardware Setup
ODrive assumes the CAN PHY is a standard differential twisted pair in a linear bus configuration with 120 ohm termination resistance at each end. ODrive versions less than V3.5 include a soldered 120 ohm termination resistor, but ODrive versions V3.5 and greater implement a dip switch to toggle the termination. ODrive uses 3.3v as the high output, but conforms to the CAN PHY requirement of achieving a differential voltage > 1.5V to represent a "0". As such, it is compatible with standard 5V bus architectures.
## Setting up CAN on ODrive
CANSimple breaks the CAN Message ID into two parts: An axis ID and a command ID. By default, CAN is enabled on the ODrive, where Axis 0 has ID 0, and Axis 1 has ID 1. The ID of each axis should be unique; each should be set via `odrivetool` before connecting to the bus with the command:
`<odrv>.<axis>.config.can.node_id = <number>`
By default, ODrive supports a value up to 63 (`0x3F`). See [can-protocol.md](can-protocol.md) for more information.
You should also set the CAN bus speed on ODrive with the command `<odrv>.can.config.baud_rate = <number>`
| Speed | Value |
| --------- | ------- |
| 125 kbps | 125000 |
| 250 kbps | 250000 |
| 500 kbps | 500000 |
| 1000 kbps | 1000000 |
That's it! You're ready to set up your host device.
### Example
```Python
odrv0.axis0.config.can.node_id = 0
odrv0.axis1.config.can.node_id = 1
odrv0.can.config.baud_rate = 250000
```
## Setting up a Raspberry Pi for CAN communications
First, you will need a CAN Hat for your Raspberry Pi. We are using [this CAN hat](https://www.amazon.com/Raspberry-Long-Distance-Communication-Transceiver-SN65HVD230/dp/B07DQPYFYV).
Setting up the Raspberry Pi essentially involves the following:
1. Enable SPI communications to the MCP2515
2. Install `can-utils` with `apt-get install can-utils`
3. Creating a connection between your application and the `can0` socket
There are many tutorials for this process. [This one is pretty good](https://www.hackster.io/youness/how-to-connect-raspberry-pi-to-can-bus-b60235), and [this recent forum post](https://www.raspberrypi.org/forums/viewtopic.php?t=296117) also works. However, be careful. You have to set the correct parameters for the particular CAN hat you're using!
1. Set the correct oscillator value
We configure the MCP2515 in section 2.2 of the tutorial, but the hat we recommend uses a 12MHz crystal instead of a 16 MHz crystal. If you're not sure what value to use, the top of the [oscillator](https://en.wikipedia.org/wiki/Crystal_oscillator) will have the value printed on it in MHz.
My Settings:
```
dtparam=spi-on
dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25
dtoverlay=spi0-hw-cs
```
2. Use the correct CAN baud rate
By default, ODrive uses 250 kbps (250000) but the tutorial is using 500 kbps. Make sure you use the value set earlier on the ODrive.
```
sudo ip link set can0 up type can bitrate 250000
```
### Wiring ODrive to CAN
The CANH and CANL pins on J2 are used for CAN communication. Connect CANH to CANH on all other devices, and CANL to CANL.
If your ODrive is the "last" (furthest) device on the bus, you can use the on-board 120 Ohm termination resistor by switching the DIP switch to "CAN 120R". Otherwise, add an external resistor.
### Verifying Communcation
By default, each ODrive axis will send a heartbeat message at 10Hz. We can confirm our ODrive communication is working by starting the `can0` interface, and then reading from it:
```
sudo ip link set can0 up type can bitrate 250000
candump can0 -xct z -n 10
```
This will read the first 10 messages from the ODrive and stop. If you'd like to see all messages, remove the `-n 10` part (hit CTRL+C to exit). The other flags (x, c, t) are adding extra information, colouring, and a timestamp, respectively.
```
$ candump can0 -xct z -n 10
(000.000000) can0 RX - - 001 [8] 00 00 00 00 01 00 00 00
(000.001995) can0 RX - - 021 [8] 00 00 00 00 08 00 00 00
(000.099978) can0 RX - - 001 [8] 00 00 00 00 01 00 00 00
(000.101963) can0 RX - - 021 [8] 00 00 00 00 08 00 00 00
(000.199988) can0 RX - - 001 [8] 00 00 00 00 01 00 00 00
(000.201980) can0 RX - - 021 [8] 00 00 00 00 08 00 00 00
(000.299986) can0 RX - - 001 [8] 00 00 00 00 01 00 00 00
(000.301976) can0 RX - - 021 [8] 00 00 00 00 08 00 00 00
(000.399986) can0 RX - - 001 [8] 00 00 00 00 01 00 00 00
(000.401972) can0 RX - - 021 [8] 00 00 00 00 08 00 00 00
```
Alternatively, if you have python can installed (`pip3 install python-can`), you can use the can.viewer script:
`python3 -m can.viewer -c "can0" -i "socketcan"` which will give you a nice readout. See [the python-can docs](https://python-can.readthedocs.io/en/master/scripts.html#can-viewer) for an example.
## Commanding the ODrive
Now that we've verified the communication is working, we can try commanding the ODrive. Make sure your ODrive is configured and working properly over USB with `odrivetool` before continuing. See the [Getting Started Guide](getting-started.md) for help with first-time configuration.
To move the ODrive, we use the command `Set Input Pos`, or cmd ID `0x00C`. First we create a message with this ID, and then "OR" in the axis ID. Then we create an 8-byte array of data with input position that we want, with a float value turned into bytes... this can be a pain though.
## DBC Files
A DBC file (.dbc) is a database of all the messages and signals in a CAN protocol. This file can be used with Python cantools to serialize and deserialize messages without having to handle the bitshifting etc yourself. We have generated a .dbc for CANSimple for you!
* [CANSimple DBC File](../tools/odrive-cansimple.dbc)
* [CANSimple DBC Generator Script](../tools/create_can_dbc.py)
Instead of manually writing values into the data, we can create a dictionary of signal:value pairs and serialize the data according to the database definition.
1. Load the database into memory
2. Use `encode_message()` to get a byte array representation of data for sending
3. Use `decode_message()` to get a dictionary representation of data for receiving
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.
+2 -8
View File
@@ -1,13 +1,7 @@
# CAN Protocol
## Hardware Setup
ODrive assumes the CAN PHY is a standard differential twisted pair in a linear bus configuration with 120 ohm termination resistance at each end. ODrive versions less than V3.5 include a soldered 120 ohm termination resistor, but ODrive versions V3.5 and greater implement a dip switch to toggle the termination. ODrive uses 3.3v as the high output, but conforms to the CAN PHY requirement of achieving a differential voltage > 1.5V to represent a "0". As such, it is compatible with standard 5V bus architectures.
This document describes teh CAN Protocol. For examples of usage, check out our [CAN Guide!](can-guide.md)
ODrive currently supports the following CAN baud rates:
* 125 kbps
* 250 kbps (default)
* 500 kbps
* 1000 kbps
---
## Configuring ODrive for CAN
@@ -94,7 +88,7 @@ You can deconflict with CANopen like this:
It may not be obvious, but this allows for some compatibility with CANOpen. Although the address space 0x200 and 0x300 correspond to receive PDO base addresses, we can guarantee they will not conflict if all CANopen node IDs are >= 32. E.g.:
CANopen nodeID = 35 = 0x23
CANopen nodeID = 35 = 0x23
Receive PDO 0x200 + nodeID = 0x223, which does not conflict with the range [0x200 : 0x21F]
Be careful that you don't assign too many nodeIDs per PDO group. Four CAN Simple nodes (32*4) is all of the available address space of a single PDO. If the bus is strictly ODrive CAN Simple nodes, a simple sequential Node ID assignment will work fine.
+1 -1
View File
@@ -63,7 +63,7 @@ sudo apt-get install python3 python3-yaml python3-jinja2 python3-jsonschema
#### Linux (Ubuntu >= 20.04)
```bash
sudo apt install gcc-arm-embedded
sudo apt install gcc-arm-none-eabi
sudo apt install openocd
sudo apt install git-lfs
sudo apt install tup
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

+12
View File
@@ -0,0 +1,12 @@
import can
bus1 = can.interface.Bus('can0', bustype='virtual')
bus2 = can.interface.Bus('can0', bustype='virtual')
msg1 = can.Message(arbitration_id=0xabcde, data=[1,2,3])
bus1.send(msg1)
msg2 = bus2.recv()
print(hex(msg1.arbitration_id))
print(hex(msg2.arbitration_id))
assert msg1.arbitration_id == msg2.arbitration_id
+80
View File
@@ -0,0 +1,80 @@
import math
import can
import cantools
import time
db = cantools.database.load_file("odrive-cansimple.dbc")
# print(db)
# bus = can.Bus("vcan0", bustype="virtual")
bus = can.Bus("can0", bustype="socketcan")
axisID = 0x1
print("\nRequesting AXIS_STATE_FULL_CALIBRATION_SEQUENCE (0x03) on axisID: " + str(axisID))
msg = db.get_message_by_name('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)
print(db.decode_message('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...")
# Read messages infinitely and wait for the right ID to show up
while True:
msg = bus.recv()
if msg.arbitration_id == ((axisID << 5) | db.get_message_by_name('Heartbeat').frame_id):
current_state = db.decode_message('Heartbeat', msg.data)['Axis_State']
if current_state == 0x1:
print("\nAxis has returned to Idle state.")
break
for msg in bus:
if msg.arbitration_id == ((axisID << 5) | db.get_message_by_name('Heartbeat').frame_id):
errorCode = db.decode_message('Heartbeat', msg.data)['Axis_Error']
if errorCode == 0x00:
print("No errors")
else:
print("Axis error! Error code: "+str(hex(errorCode)))
break
print("\nPutting axis",axisID,"into AXIS_STATE_CLOSED_LOOP_CONTROL (0x08)...")
data = db.encode_message('Set_Axis_State', {'Axis_Requested_State': 0x08})
msg = can.Message(arbitration_id=0x07 | axisID << 5, is_extended_id=False, data=data)
print(msg)
try:
bus.send(msg)
print("Message sent on {}".format(bus.channel_info))
except can.CanError:
print("Message NOT sent!")
for msg in bus:
if msg.arbitration_id == 0x01 | axisID << 5:
print("\nReceived Axis heartbeat message:")
msg = db.decode_message('Heartbeat', msg.data)
print(msg)
if msg['Axis_State'] == 0x8:
print("Axis has entered closed loop")
else:
print("Axis failed to enter closed loop")
break
target = 0
data = db.encode_message('Set_Limits', {'Velocity_Limit':10.0, 'Current_Limit':10.0})
msg = can.Message(arbitration_id=axisID << 5 | 0x00F, is_extended_id=False, data=data)
bus.send(msg)
t0 = time.monotonic()
while True:
setpoint = 4.0 * math.sin((time.monotonic() - t0)*2)
print("goto " + str(setpoint))
data = db.encode_message('Set_Input_Pos', {'Input_Pos':setpoint, 'Vel_FF':0.0, 'Torque_FF':0.0})
msg = can.Message(arbitration_id=axisID << 5 | 0x00C, data=data, is_extended_id=False)
bus.send(msg)
time.sleep(0.01)
+53
View File
@@ -0,0 +1,53 @@
import can
bus = can.Bus("can0", bustype="socketcan")
axisID = 0x1
print("Requesting AXIS_STATE_FULL_CALIBRATION_SEQUENCE (0x03) on axisID: " + str(axisID))
msg = can.Message(arbitration_id=axisID << 5 | 0x07, data=[3, 0, 0, 0, 0, 0, 0, 0], dlc=8, is_extended_id=False)
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...")
# Read messages infinitely and wait for the right ID to show up
while True:
msg = bus.recv()
if msg.arbitration_id == (axisID << 5 | 0x01):
current_state = msg.data[4] | msg.data[5] << 8 | msg.data[6] << 16 | msg.data[7] << 24
if current_state == 0x1:
print("\nAxis has returned to Idle state.")
break
for msg in bus:
if(msg.arbitration_id == (axisID << 5 | 0x01)):
errorCode = msg.data[0] | msg.data[1] << 8 | msg.data[2] << 16 | msg.data[3] << 24
print("\nReceived Axis heartbeat message:")
if errorCode == 0x0:
print("No errors")
else:
print("Axis error! Error code: "+str(hex(errorCode)))
break
print("\nPutting axis",axisID,"into AXIS_STATE_CLOSED_LOOP_CONTROL (0x08)...")
msg = can.Message(arbitration_id=axisID << 5 | 0x07, data=[8, 0, 0, 0, 0, 0, 0, 0], dlc=8, is_extended_id=False)
print(msg)
try:
bus.send(msg)
print("Message sent on {}".format(bus.channel_info))
except can.CanError:
print("Message NOT sent!")
for msg in bus:
if msg.arbitration_id == (axisID << 5 | 0x01):
print("\nReceived Axis heartbeat message:")
if msg.data[4] == 0x8:
print("Axis has entered closed loop")
else:
print("Axis failed to enter closed loop")
break
+166
View File
@@ -0,0 +1,166 @@
import cantools
# 0x00 - NMT Message (Reserved)
# 0x001 - Heartbeat
axisError = cantools.database.can.Signal("Axis_Error", 0, 32)
axisState = cantools.database.can.Signal("Axis_State", 32, 32)
heartbeatMsg = cantools.database.can.Message(
0x001, "Heartbeat", 8, [axisError, axisState]
)
# 0x003 - Motor Error
motorError = cantools.database.can.Signal("Motor_Error", 0, 32)
motorErrorMsg = cantools.database.can.Message(0x003, "Get_Motor_Error", 8, [motorError])
# 0x004 - Encoder Error
encoderError = cantools.database.can.Signal("Encoder_Error", 0, 32)
encoderErrorMsg = cantools.database.can.Message(
0x004, "Get_Encoder_Error", 8, [encoderError]
)
# 0x005 - Sensorless Error
sensorlessError = cantools.database.can.Signal("Sensorless_Error", 0, 32)
sensorlessErrorMsg = cantools.database.can.Message(
0x005, "Get_Sensorless_Error", 8, [sensorlessError]
)
# 0x006 - Axis Node ID
axisNodeID = cantools.database.can.Signal("Axis_Node_ID", 0, 32)
axisNodeMsg = cantools.database.can.Message(0x006, "Set_Axis_Node_ID", 8, [axisNodeID])
# 0x007 - Requested State
axisRequestedState = cantools.database.can.Signal("Axis_Requested_State", 0, 32)
setAxisState = cantools.database.can.Message(
0x007, "Set_Axis_State", 8, [axisRequestedState]
)
# 0x008 - Startup Config (Reserved)
# 0x009 - Encoder Estimates
encoderPosEstimate = cantools.database.can.Signal("Pos_Estimate", 0, 32, is_float=True)
encoderVelEstimate = cantools.database.can.Signal("Vel_Estimate", 32, 32, is_float=True)
encoderEstimates = cantools.database.can.Message(
0x009, "Get_Encoder_Estimates", 8, [encoderPosEstimate, encoderVelEstimate]
)
# 0x00A - Get Encoder Count
encoderShadowCount = cantools.database.can.Signal("Shadow_Count", 0, 32)
encoderCountInCPR = cantools.database.can.Signal("Count_in_CPR", 32, 32)
encoderCountMsg = cantools.database.can.Message(
0x00A, "Get_Encoder_Count", 8, [encoderShadowCount, encoderCountInCPR]
)
# 0x00B - Set Controller Modes
controlMode = cantools.database.can.Signal("Control_Mode", 0, 32)
inputMode = cantools.database.can.Signal("Input_Mode", 32, 32)
setControllerModeMsg = cantools.database.can.Message(
0x00B, "Set_Controller_Mode", 8, [controlMode, inputMode]
)
# 0x00C - Set Input Pos
inputPos = cantools.database.can.Signal("Input_Pos", 0, 32, is_float=True)
velFF = cantools.database.can.Signal("Vel_FF", 32, 16, is_signed=True, scale=0.001)
torqueFF = cantools.database.can.Signal(
"Torque_FF", 48, 16, is_signed=True, scale=0.001
)
setInputPosMsg = cantools.database.can.Message(
0x00C, "Set_Input_Pos", 8, [inputPos, velFF, torqueFF]
)
# 0x00D - Set Input Vel
inputVel = cantools.database.can.Signal("Input_Vel", 0, 32, is_float=True)
inputTorqueFF = cantools.database.can.Signal("Input_Torque_FF", 32, 32, is_float=True)
setInputVelMsg = cantools.database.can.Message(
0x00D, "Set_Input_Vel", 8, [inputVel, inputTorqueFF]
)
# 0x00E - Set Input Torque
inputTorque = cantools.database.can.Signal("Input_Torque", 0, 32, is_float=True)
setInputTqMsg = cantools.database.can.Message(
0x00E, "Set_Input_Torque", 8, [inputTorque]
)
# 0x00F - Set Velocity Limit
velLimit = cantools.database.can.Signal("Velocity_Limit", 0, 32, is_float=True)
currentLimit = cantools.database.can.Signal("Current_Limit", 32, 32, is_float=True)
setVelLimMsg = cantools.database.can.Message(0x00F, "Set_Limits", 8, [velLimit, currentLimit])
# 0x010 - Start Anticogging
startAnticoggingMsg = cantools.database.can.Message(0x010, "Start_Anticogging", 0, [])
# 0x011 - Set Traj Vel Limit
trajVelLim = cantools.database.can.Signal("Traj_Vel_Limit", 0, 32, is_float=True)
setTrajVelMsg = cantools.database.can.Message(
0x011, "Set_Traj_Vel_Limit", 8, [trajVelLim]
)
# 0x012 - Set Traj Accel Limits
trajAccelLim = cantools.database.can.Signal("Traj_Accel_Limit", 0, 32, is_float=True)
trajDecelLim = cantools.database.can.Signal("Traj_Decel_Limit", 32, 32, is_float=True)
setTrajAccelMsg = cantools.database.can.Message(
0x012, "Set_Traj_Accel_Limits", 8, [trajAccelLim, trajDecelLim]
)
# 0x013 - Set Traj Inertia
trajInertia = cantools.database.can.Signal("Traj_Inertia", 0, 32, is_float=True)
trajInertiaMsg = cantools.database.can.Message(
0x013, "Set_Traj_Inertia", 8, [trajInertia]
)
# 0x014 - Get Iq
iqSetpoint = cantools.database.can.Signal("Iq_Setpoint", 0, 32, is_float=True)
iqMeasured = cantools.database.can.Signal("Iq_Measured", 32, 32, is_float=True)
getIqMsg = cantools.database.can.Message(0x014, "Get_Iq", 8, [iqSetpoint, iqMeasured])
# 0x015 - Get Sensorless Estimates
sensorlessPosEstimate = cantools.database.can.Signal(
"Sensorless_Pos_Estimate", 0, 32, is_float=True
)
sensorlessVelEstimate = cantools.database.can.Signal(
"Sensorless_Vel_Estimate", 32, 32, is_float=True
)
getSensorlessEstMsg = cantools.database.can.Message(
0x015, "Get_Sensorless_Estimates", 8, [sensorlessPosEstimate, sensorlessVelEstimate]
)
# 0x016 - Reboot ODrive
rebootMsg = cantools.database.can.Message(0x016, "Reboot", 0, [])
# 0x017 - Get vbus Voltage
vbusVoltage = cantools.database.can.Signal("Vbus_Voltage", 0, 32, is_float=True)
getVbusVMsg = cantools.database.can.Message(0x017, "Get_Vbus_Voltage", 8, [vbusVoltage])
# 0x018 - Clear Errors
clearErrorsMsg = cantools.database.can.Message(0x018, "Clear_Errors", 0, [])
db = cantools.database.can.Database(
[
heartbeatMsg,
motorErrorMsg,
encoderErrorMsg,
sensorlessErrorMsg,
axisNodeMsg,
setAxisState,
encoderEstimates,
encoderCountMsg,
setControllerModeMsg,
setInputPosMsg,
setInputVelMsg,
setInputTqMsg,
setVelLimMsg,
startAnticoggingMsg,
setTrajVelMsg,
setTrajAccelMsg,
trajInertiaMsg,
getIqMsg,
getSensorlessEstMsg,
rebootMsg,
getVbusVMsg,
]
)
cantools.database.dump_file(db, "odrive-cansimple.dbc")
db = cantools.database.load_file("odrive-cansimple.dbc")
print(db)
+137
View File
@@ -0,0 +1,137 @@
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_
BS_:
BU_:
BO_ 1 Heartbeat: 8 Vector__XXX
SG_ Axis_State : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Axis_Error : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 3 Get_Motor_Error: 8 Vector__XXX
SG_ Motor_Error : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 4 Get_Encoder_Error: 8 Vector__XXX
SG_ Encoder_Error : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 5 Get_Sensorless_Error: 8 Vector__XXX
SG_ Sensorless_Error : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 6 Set_Axis_Node_ID: 8 Vector__XXX
SG_ Axis_Node_ID : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 7 Set_Axis_State: 8 Vector__XXX
SG_ Axis_Requested_State : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 9 Get_Encoder_Estimates: 8 Vector__XXX
SG_ Vel_Estimate : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Pos_Estimate : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 10 Get_Encoder_Count: 8 Vector__XXX
SG_ Count_in_CPR : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Shadow_Count : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 11 Set_Controller_Mode: 8 Vector__XXX
SG_ Input_Mode : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Control_Mode : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 12 Set_Input_Pos: 8 Vector__XXX
SG_ Torque_FF : 48|16@1- (0.001,0) [0|0] "" Vector__XXX
SG_ Vel_FF : 32|16@1- (0.001,0) [0|0] "" Vector__XXX
SG_ Input_Pos : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 13 Set_Input_Vel: 8 Vector__XXX
SG_ Input_Torque_FF : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Input_Vel : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 14 Set_Input_Torque: 8 Vector__XXX
SG_ Input_Torque : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 15 Set_Limits: 8 Vector__XXX
SG_ Current_Limit : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Velocity_Limit : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 16 Start_Anticogging: 0 Vector__XXX
BO_ 17 Set_Traj_Vel_Limit: 8 Vector__XXX
SG_ Traj_Vel_Limit : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 18 Set_Traj_Accel_Limits: 8 Vector__XXX
SG_ Traj_Decel_Limit : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Traj_Accel_Limit : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 19 Set_Traj_Inertia: 8 Vector__XXX
SG_ Traj_Inertia : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 20 Get_Iq: 8 Vector__XXX
SG_ Iq_Measured : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Iq_Setpoint : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 21 Get_Sensorless_Estimates: 8 Vector__XXX
SG_ Sensorless_Vel_Estimate : 32|32@1+ (1,0) [0|0] "" Vector__XXX
SG_ Sensorless_Pos_Estimate : 0|32@1+ (1,0) [0|0] "" Vector__XXX
BO_ 22 Reboot: 0 Vector__XXX
BO_ 23 Get_Vbus_Voltage: 8 Vector__XXX
SG_ Vbus_Voltage : 0|32@1+ (1,0) [0|0] "" Vector__XXX
SIG_VALTYPE_ 9 Pos_Estimate : 1;
SIG_VALTYPE_ 9 Vel_Estimate : 1;
SIG_VALTYPE_ 12 Input_Pos : 1;
SIG_VALTYPE_ 13 Input_Vel : 1;
SIG_VALTYPE_ 13 Input_Torque_FF : 1;
SIG_VALTYPE_ 14 Input_Torque : 1;
SIG_VALTYPE_ 15 Velocity_Limit : 1;
SIG_VALTYPE_ 15 Current_Limit : 1;
SIG_VALTYPE_ 17 Traj_Vel_Limit : 1;
SIG_VALTYPE_ 18 Traj_Accel_Limit : 1;
SIG_VALTYPE_ 18 Traj_Decel_Limit : 1;
SIG_VALTYPE_ 19 Traj_Inertia : 1;
SIG_VALTYPE_ 20 Iq_Setpoint : 1;
SIG_VALTYPE_ 20 Iq_Measured : 1;
SIG_VALTYPE_ 21 Sensorless_Pos_Estimate : 1;
SIG_VALTYPE_ 21 Sensorless_Vel_Estimate : 1;
SIG_VALTYPE_ 23 Vbus_Voltage : 1;
+1 -1
View File
@@ -39,7 +39,7 @@ for i in [1,2,3,4]:
# A sine wave to test
t0 = time.monotonic()
while True:
setpoint = 10000.0 * math.sin((time.monotonic() - t0)*2)
setpoint = 4.0 * math.sin((time.monotonic() - t0)*2)
print("goto " + str(int(setpoint)))
my_drive.axis0.controller.pos_setpoint = setpoint
time.sleep(0.01)