CoDrive
Simple Python API for Cosyne motor controllers over CAN FD.
The controller speaks CoLink, a simplified CiA 402 profile. codrive handles the bus,
the frame coding and the node addressing, so you work with drives instead of COB-IDs.
Install
pip install codrive
Connect to drive
from codrive import Link
# Auto-connect to the shipped USB adapter
link = Link()
link.connect()
# Add drive with ID 1
drive = link.add_drive(node_id=1)
# Print state name
print(drive.state.name)
# Disconnect from bus
link.disconnect()
connect() opens the shipped adapter and configures it automatically. add_drive waits
for the drive to answer, so an absent node raises TimeoutError.
In one scope, a with block connects and disconnects for you:
with Link() as link:
drive = link.add_drive(1)
Other CAN FD adapters
Pass any CAN FD capable python-can bus to Link(). Configure it for the controller's
bus yourself as SAE J2284-5: 500 kbit/s arbitration at an 80 % sample point, 5 Mbit/s
data at a 75 % sample point.
Using a PEAK PCAN-USB FD adapter:
import can
from codrive import Link
timing = can.BitTimingFd(
f_clock=80_000_000,
nom_brp=1, nom_tseg1=127, nom_tseg2=32, nom_sjw=32,
data_brp=1, data_tseg1=11, data_tseg2=4, data_sjw=4,
)
bus = can.Bus(interface="pcan", channel="PCAN_USBBUS1", timing=timing)
with Link(bus) as link:
drive = link.add_drive(node_id=1)
print(drive.state.name)
bus.shutdown()
A bus you open stays yours — the link leaves it open on disconnect, so close it yourself.
Move
Enable and set profile
Set the ramp limits once, enable, then command a mode. Each mode has its own call, so the units are visible at the call site:
drive.set_profile(velocity=800.0, acceleration=4000.0, torque_slope=5.0)
drive.enable()
enable() sets no mode, and disable(), quick_stop() and reset_fault() clear the
mode and setpoints. An enable therefore never starts motion by itself: after a stop or a
fault the drive comes back up idle. The set_profile() limits are kept, since a limit
cannot produce motion on its own.
Profile position mode
# Move to 1 rad using the velocity and acceleration from set_profile()
drive.move_to(1.0)
drive.wait_for_target()
# Move to 2 rad using the specified velocity and acceleration
drive.move_to(2.0, velocity=200.0, acceleration=1000.0)
drive.wait_for_target()
# Start position control without movement
current_position = drive.position
drive.move_to(current_position)
Profile velocity mode
# Run at 300 rad/s using the acceleration from set_profile()
drive.run_at(300.0)
drive.wait_for_target()
# Run at 50 rad/s using the specified acceleration
drive.run_at(50.0, acceleration=1000.0)
drive.wait_for_target()
Profile torque mode
# Set drive torque to 0.2 Nm using the torque slope from set_profile()
drive.apply_torque(0.2)
drive.wait_for_target()
# Set drive torque to 0.0 Nm using the specified torque slope
drive.apply_torque(0.0, slope=2.0)
drive.wait_for_target()
Stop and disable
# Standard method
drive.run_at(0.0)
drive.wait_for_target()
drive.disable()
# Also supported
drive.quick_stop()
drive.wait_for_target()
drive.disable()
wait_for_target() means the profile generator reached the commanded value, not that the
axis did — there is no following-error term, so it can return while the machine is still
turning. Add your own dwell before cutting torque.
Important: Do not call drive.disable() while the drive is spinning!
Record and plot
Once the drive has answered, its latest status is available as
print(drive.position) # rad
print(drive.velocity) # rad/s
print(drive.torque) # Nm
print(drive.state.name) # e.g. OPERATION_ENABLED
status = drive.status # for all status information
The received status can be recorded using
rec = link.record()
drive.move_to(1.0)
drive.wait_for_target()
rec.stop()
rec.plot()
Start and stop can sit in different cells or functions. Inside one scope, use a with
block:
with link.record() as rec:
drive.move_to(1.0)
drive.wait_for_target()
Read the columns yourself for anything beyond plot():
import matplotlib.pyplot as plt
samples = rec.samples()
plt.plot(samples.time, samples.position)
Faults
A fault ends the motion directly. reset_fault() only clears the fault if its cause is
gone; while it persists the drive stays in FAULT and the call raises TimeoutError.
from codrive import DriveState
if drive.state is DriveState.FAULT:
print(repr(drive.error)) # e.g. <ErrorCode.DC_LINK_UNDERVOLTAGE: 12832>
drive.reset_fault()
drive.error returns ErrorCode.NONE when there is no fault, and None for a code
codrive does not name — drive.status.error_code still holds the raw number. Both are
falsy, so compare against ErrorCode.NONE rather than testing for truth.
Multiple drives
All drives share one receive task, dispatched by node id:
with Link() as link:
axis1 = link.add_drive(1)
axis2 = link.add_drive(2)
for axis in (axis1, axis2):
axis.set_profile(velocity=800.0, acceleration=4000.0)
axis.enable()
with link.record() as rec:
axis1.move_to(1.57) # both axes move at once
axis2.move_to(-1.57)
axis1.wait_for_target()
axis2.wait_for_target()
axis1.disable()
axis2.disable()
rec.plot() # every node, one colour each
rec.plot(axis1) # one drive, by object or node id
Status
Early, and the API may still change.
License
Apache 2.0
Metadata
Release files for codrive 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| codrive-0.2.1.tar.gz | 15.2 kB | Without provenance | Details for codrive-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| codrive-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for codrive-0.2.1-py3-none-any.whl |
Total release size: 33.1 kB
Release files / codrive-0.2.1.tar.gz
| Download URL | codrive-0.2.1.tar.gz |
|---|---|
| Size | 15.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2591116420b79805f0506805595f841de5783a84e615030bba2cdcb1729890b7
|
|
BLAKE2b-256 checksum How to use checksums |
2cdde1c01f4e414845af0be13892c677b2737b89955f31c1f988df05bdab3f18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / codrive-0.2.1-py3-none-any.whl
| Download URL | codrive-0.2.1-py3-none-any.whl |
|---|---|
| Size | 17.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4cb2e8629819632aac148a0adc18c4294b9d79268b81fbfdcbf69530128f464e
|
|
BLAKE2b-256 checksum How to use checksums |
0ba6468b5db2c14634e669f3546f6e387f2b3be9a6151bb212d982b4e7be5c23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for codrive 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.