Skip to content

CANopen Brushless Motor Position Profile Control with Arduino and SOLO

Arduino

Introduction

If you are making an industrial Motion system that is going to be used in harsh environments, under noisy conditions with tight safety requirements most probably your first choice of Main Controlling Unit (MCU), especially for a product will not be a bare Arduino as they are infamous for lacking the quality and sophistication to be used in real-time systems where every micro-seconds matters!

Don’t get me wrong, Arduino is probably the best thing that happened so far for hardware education, and their role to make the use of challenging microcontrollers easy is not going to be forgotten any time soon, but believe it or not, it still among industries that the reliability is one of the most important factors, they are facing difficulties, mostly due to the fact that they are not initially built for such purposes and their use-cases were majorly targeted for Labs or educational environments.

However this paradigm is changing, especially for Motor controlling solutions that SOLO is offering in conjunction with Arduino devices, we were aware of the fact that many people and industries will want to use sweat Arduino even in harsh conditions and they want it to operate extremely well (we all love magics ;) ), so that’s why we adapted our CANopen solution with Arduino devices, to provide a robust and industry-standard protocol for all those who were waiting to finally make a reliable Motion controlling solution with Arduino.

As a side note, we believe SOLO is the first or let’s say one of the early adapters of CANopen solutions for Arduino devices at this scale, if you doubt it have a look on our Git library developed for Arduino here!

What is CANopen

image1

You might have already heard about CAN protocol, but you might have been wondering what is this CANopen that people are talking about, to answer it simply, one can say, CANopen is the software layer built on top of CAN communication protocol that is acting like a soul to a body, in another word, CAN protocol operates on two differential signal lines known as CAN High and CAN Low, and initially developed for Automotive solutions to provide them more robust and reliable solutions compared to already existing protocols at the time like RS232 or RS485, etc, but like any other hardware communication protocol, the whole job of the CAN bus is to deliver a bunch of bytes from one side to the other side safely, but then what happens after that?

And here comes the role of CANopen, as we know ourselves as engineers, we love to be creative and make our own rules, but imagine a network of devices that each are having its own creative data packeting and formatting! Then what happens if you want to put all these devices together to work in a network? The answer is simple, a huge unmanageable mess… and here comes the beauty of something like CANopen, that puts standards for people across various industries on how to deal with CAN messages, and how to format and communicate with other devices in a harmonious manner, so they can all work together without interfering with each other.

So all seems beautiful till you start going from CAN to CANopen and then you’ll see it requires a huge amount of work to make a CAN-enabled system compatible with CANopen standards, maybe this is the biggest drawback of something like CANopen that many simply won’t have the time to adapt to it…

Story short, we actually were not one of those, and we tried to adapt our products fully with CANopen standards introduced by CIA organization especially for Motor Drives and Servo solutions.

How to commit an Arduino with SOLO operating on a CANbus

The answer to this question lies in the very first fact that Arduino devices ( at least till recently) are not offering any native on-chip CAN capabilities, but this will not stop you from making the CAN protocol work with them, but the question is How?

Simply using SPI to CAN converter chips made by Microchip Technology including MCP2515 and MCP2551 used in various Arduino shields like CAN-BUS Shield V2.0 or Arduino MKR CAN Shield.

image2

CAN-BUS Shield V2.0 for interfacing Arduino to CANbus

After using one of these chips or shields properly, by just connecting the CANH and CANL to CANH and CANL of SOLO and making sure the GND of both devices are shared at least in a single point, you’d be able to fully SET or READ any command in the existing dictionary of SOLO Motor Controllers devices following our CANopen user manual.

Example

The example below demonstrates how you can robustly communicate with a SOLO MINI unit through CANopen and programming your Arduino to command SOLO for having some cyclic position profiles on a Brushless motor equipped with Incremental Encoders with very high performance in Field-Oriented control mode.

Wiring and System Setup

After using one of these chips or shields properly, by just connecting the CANH and CANL to CANH and CANL of SOLO and making sure the GND of both devices are shared at least in a single point, you’d be able to fully SET or READ any command in the existing dictionary of SOLO Motor Controllers devices following our CANopen user manual.

SOLO MINI to Arduino CAN shield V2 for operating in CANopen mode

SOLO MINI schematic CANopen

Check the product

SOLO UNO to Arduino CAN shield V2 for operating in CANopen mode

SOLO UNO schematic to CANOpen

Check the product

Notes before diving into the code:

1. Prior to running this example on your SOLO unit, you need to make sure your Encoder and Motor are fully calibrated with SOLO, to do that you need to follow our tutorial here at least one time till you find the best combination for Motor and encoder’s wirings, as long as your Encoder and Motor remains the same this calibration will hold, and there will be no need to repeat it every single time you turn on your unit.

2. Make sure your SOLO unit is in Closed-loop mode, this can happen by pressing pin# 5 on SOLO UNO’s piano switch down or on SOLO MINI keeping the mode switch at the “CL” position.

Code

Canopen_Digital_PMSM_Position_Profile_Encoders_Arduino_UNO.ino
// Copyright: (c) 2021, SOLO motor controllers project
// GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

/*
     Title: Position Control of a BLDC-PMSM Motor with Arduino and SOLO - Position Profile
     Author: SOLOMOTORCONTROLLERS
     Date: 2022
     Code version: 4.1.0
     Availability: https://github.com/Solo-FL/SOLO-motor-controllers-ARDUINO-library
     Please make sure you are applying the right wiring between SOLO and your ARDUINO
     The Code below has been tested on Arduino UNO
     The Motor used for Testings: teknic m-2310P-LN-04K
     Read more about this code here:https://www.solomotorcontrollers.com/position-control-brushless-arduino-and-solo/
*/

#include "SOLOMotorControllersCanopen.h"
#define PI 3.1415926535897932384626433832795
#define sgn(x) ((x) < 0 ? -1 : ((x) > 0 ? 1 : 0))

//For this Test, make sure you have calibrated your Encoder before
//to know more please read: https://www.solomotorcontrollers.com/how-to-connect-calibrate-incremental-encoder-with-solo/

//instanciate a SOLO object:
SOLOMotorControllers *SOLO_Obj1;

//the device address of SOLO:
unsigned char SOLO_address1 = 0;

//Desired Switching or PWM Frequency at Output
long pwmFrequency = 20;

//Motor's Number of Poles
long numberOfPoles = 8;

//Motor's Number of Encoder Lines (PPR pre-quad)
long numberOfEncoderLines = 1000;

//Speed controller Kp
float speedControllerKp = 0.08;

//Speed controller Ki
float speedControllerKi = 0.008;

//Position controller Kp
float positionControllerKp = 0.25;

//Position controller Ki
float positionControllerKi = 0.02;

// Current Limit of the Motor
float currentLimit = 10.0;

// Battery or Bus Voltage
float busVoltage = 0;

// Desired Speed Limit[RPM]
long desiredSpeedLimit = 1000;

// Desired Position Reference
long desiredPositionReference = 0;

// Motor speed feedback
long actualMotorSpeed = 0;

// Motor position feedback
long actualMotorPosition = 0;

void setup() {
  //In this example, make sure you put SOLO into Closed-Loop by
  // pressing the Piano Switch NO# 5 DOWN. in SOLO UNO

  //used for Monitoring in Arduino Only
  Serial.begin(115200);

  //Initialize the SOLO object
  int SOLOdeviceAddress = 0;
  int chipSelectPin = 9; //SPI CS pin for CANshield
  SOLO_Obj1 = new SOLOMotorControllersCanopen(SOLOdeviceAddress, chipSelectPin);
  delay(1000);

  Serial.println("\n Trying to Connect To SOLO");
  delay(1000);
  //wait here till communication is established
  while (SOLO_Obj1->CommunicationIsWorking() == false)
  {
    delay(500);
  }

  Serial.println("\n Communication Established succuessfully!");

  // Initial Configurations
  SOLO_Obj1->SetOutputPwmFrequencyKhz(pwmFrequency);
  SOLO_Obj1->SetCurrentLimit(currentLimit);
  SOLO_Obj1->SetMotorPolesCounts(numberOfPoles);
  SOLO_Obj1->SetIncrementalEncoderLines(numberOfEncoderLines);
  SOLO_Obj1->SetCommandMode(SOLOMotorControllers::CommandMode::digital);
  SOLO_Obj1->SetMotorType(SOLOMotorControllers::MotorType::bldcPmsm);

  //run the motor identification to Auto-tune the current controller gains Kp and Ki needed for Torque Loop
  //run ID. always after selecting the Motor Type!
  //ID. doesn't need to be called everytime, only one time after wiring up the Motor will be enough
  //the ID. values will be remembered by SOLO after power recycling
  SOLO_Obj1->MotorParametersIdentification(SOLOMotorControllers::Action::start);
  Serial.println("\n Identifying the Motor");
  //wait at least for 2sec till ID. is done
  delay(2000);

  //Operate while using Quadrature Incremental Encoders
  SOLO_Obj1->SetFeedbackControlMode(SOLOMotorControllers::FeedbackControlMode::encoders);
  SOLO_Obj1->SetControlMode(SOLOMotorControllers::ControlMode::positionMode);
  //Speed Controller Tunings
  SOLO_Obj1->SetSpeedControllerKp(speedControllerKp);
  SOLO_Obj1->SetSpeedControllerKi(speedControllerKi);
  //Position Controller Tunings
  SOLO_Obj1->SetPositionControllerKp(positionControllerKp);
  SOLO_Obj1->SetPositionControllerKi(positionControllerKi);
}

void loop() {
  //Homming
  //Speed Controller Tunings
  SOLO_Obj1->SetSpeedControllerKp(speedControllerKp);
  SOLO_Obj1->SetSpeedControllerKi(speedControllerKi);
  desiredSpeedLimit = 2000;
  SOLO_Obj1->SetSpeedLimit(desiredSpeedLimit);
  SOLO_Obj1->SetPositionReference(0);
  actualMotorPosition = SOLO_Obj1->GetPositionCountsFeedback();
  Serial.println("\n Number of Pulses passed: ");
  Serial.println(actualMotorPosition);
  delay(2000);

  //Sine profile
  //set a desired Speed Limit for trajectory in RPM
  desiredSpeedLimit = 6000;
  SOLO_Obj1->SetSpeedLimit(desiredSpeedLimit);
  for (int j = 0; j <= 3; j += 1) {
    for (float i = 0; i <= 2 * PI; i += 0.01) {
      SOLO_Obj1->SetPositionReference(+80000 * sin(i));
      delay(1);
      actualMotorPosition = SOLO_Obj1->GetPositionCountsFeedback();
      Serial.println("\n Number of Pulses passed: ");
      Serial.println(actualMotorPosition);
    }
  }
  delay(1000);

  desiredPositionReference = -1;
  //set a desired Speed Limit for trajectory in RPM
  desiredSpeedLimit = 2000;
  SOLO_Obj1->SetSpeedLimit(desiredSpeedLimit);
  //Speed Controller Tunings
  SOLO_Obj1->SetSpeedControllerKp(0.03);
  SOLO_Obj1->SetSpeedControllerKi(0.003);

  //Step profile
  for (int j = 0; j < 2; j += 1) {
    desiredPositionReference = -1 * 80000 * sgn(desiredPositionReference);
    SOLO_Obj1->SetPositionReference(desiredPositionReference);
    delay(3000);
    actualMotorPosition = SOLO_Obj1->GetPositionCountsFeedback();
    Serial.println("\n Number of Pulses passed: ");
    Serial.println(actualMotorPosition);
  }
  delay(1000);

}

Results

As you could probably see in the video above, we were able to send Motion profiles in Sinosoidal as well as Step type of shapes in cyclic mode, the most interesting part is under CANopen, you can reach up to 1ms of loop delay within arduino environment to communicate with SOLO, commanding to it and reading parameters in a robust manner.

IMG1 PositionProfile

Position profile sent to SOLO MINI to follow from CANopen lines

The speed feedback is also can be seen below, and as shown is having a very high resolution offering up to +/-1RPM in steady-state conditions, it worth mentioning that in this code, the Speed profile has not been controlled exclusively, and we just set a maximum speed ( speed limit) for the profile.

IMG2 Position Speed Profile

Speed and Position profile feedbacks

As some one who deals with servo-driving systems, you should always have an eye on torque on the motor too, even if the motion is great, but the torque controlling capabilities can make a lot of differences specially in energy consumptions as well as the system response under loaded conditions.

IMG3 Position Torque Profile

Torque and Position profile feedbacks

One of the biggest advantages of Motion systems operating under FOC is the fact that they make sure the whole motion is controlled by controlling Torque, Speed and Positions at the same time, resulting in having higher performances and safety levels, below you can see the three plots brought together:

IMG4 Position Speed Torque Profile

Torque, Speed and Position profile feedbacks at the same time

Products in this article

SOLO MINI V2

SOLO MINI V2

159.00€

In Stock

SOLO UNO V2

SOLO UNO V2

175.00€

In Stock

Browse all categories

Drive your next innovation today

Discover our High-Performance Motor Controllers, Test Benches, and Services trusted worldwide.