Skip to main content

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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

codrive-0.2.1.tar.gz (15.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

codrive-0.2.1-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

File details

Details for the file codrive-0.2.1.tar.gz.

File metadata

  • Download URL: codrive-0.2.1.tar.gz
  • Upload date:
  • Size: 15.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for codrive-0.2.1.tar.gz
Algorithm Hash digest
SHA256 2591116420b79805f0506805595f841de5783a84e615030bba2cdcb1729890b7
MD5 cab297d7f8ed8dcaaf4996dc349c159c
BLAKE2b-256 2cdde1c01f4e414845af0be13892c677b2737b89955f31c1f988df05bdab3f18

See more details on using hashes here.

File details

Details for the file codrive-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: codrive-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 17.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for codrive-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4cb2e8629819632aac148a0adc18c4294b9d79268b81fbfdcbf69530128f464e
MD5 2c702d36fe3dc779f6f06bc532582263
BLAKE2b-256 0ba6468b5db2c14634e669f3546f6e387f2b3be9a6151bb212d982b4e7be5c23

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page