v## For instruction on writing tutorials

(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

SINAMICS G120 - User node

Description: This tutorial teaches you how to write simple ROS C++ node to control the speed of SINAMICS G120 drive

Keywords: SIEMENS, CP1616, SINAMICS, PROFINET, User node

Tutorial Level: INTERMEDIATE

Next Tutorial: siemens_cp1616/Tutorials/SINAMICS G120 - Runtime

This tutorial teaches you how to publish and subscribe data to topics generated by PROFINET IO Controller wrapper node. Following code is intended for use with hardware configuration described in previous tutorial. Complete IO Controller tutorial package is available in github siemens_tutorials repository.

The code

https://raw.githubusercontent.com/durovsky/siemens_tutorials/master/siemens_cp1616_sinamics_g120_tutorial/doc/doc_siemens_cp1616_sinamics_g120_tutorial_node.cpp

   1 #include <ros/ros.h>
   2 #include <std_msgs/MultiArrayDimension.h>
   3 #include <std_msgs/UInt8MultiArray.h>
   4 
   5 const int DRIVE_TELEGRAM_SIZE = 4;
   6 const int N_MAX = 1500;
   7 
   8 //declarations
   9 void driveTelegramCallback(const std_msgs::UInt8MultiArray::ConstPtr &msg);
  10 void setDriveTelegram(unsigned char drive_telegram[], int velocity);
  11 
  12 //Main function
  13 int main(int argc, char *argv[])
  14 {
  15   ros::init(argc, argv, "cp1616_sinamics_tutorial_node");
  16   ros::NodeHandle nh;
  17 
  18   //Subscribe to drive topic defined in yaml config file
  19   ros::Subscriber sub_drive = nh.subscribe("/drive_1_input_topic", 1,
  20                                            &driveTelegramCallback);
  21 
  22   //Create publihser for drive topic defined in yaml config file
  23   ros::Publisher pub_drive = nh.advertise<std_msgs::UInt8MultiArray>
  24                                          ("/drive_1_output_topic", 1);
  25 
  26   //Telegram 1 
  27   unsigned char drive_telegram_1[DRIVE_TELEGRAM_SIZE];
  28 
  29   //std_msgs::MultiArray variables
  30   std_msgs::MultiArrayDimension msg_dim;
  31   std_msgs::UInt8MultiArray msg;
  32 
  33   //Wait until communication starts properly
  34   ros::Duration(10).sleep();
  35   
  36   //-------------------------------------------------
  37   //First telegram - Master control by PLC
  38   //-------------------------------------------------
  39   msg_dim.label = "Drive_1_output";
  40   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  41   msg.layout.dim.clear();
  42   msg.layout.dim.push_back(msg_dim);
  43 
  44   msg.data.clear();
  45 
  46   drive_telegram_1[0] =  0b00000100;   //STW1 high
  47   drive_telegram_1[1] =  0b00000000;   //STW1 low
  48   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  49   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  50   
  51   msg.data.clear();
  52   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
  53     msg.data.push_back(drive_telegram_1[i]);
  54 
  55   pub_drive.publish(msg);
  56   
  57   //Wait for 2 seconds
  58   ros::Duration(2).sleep();
  59  
  60   //--------------------------------------------------
  61   //Second telegram - set ON/OFF2 ON/OFF/3 to true
  62   //--------------------------------------------------
  63   msg_dim.label = "Drive_1_output";
  64   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  65   msg.layout.dim.clear();
  66   msg.layout.dim.push_back(msg_dim);
  67 
  68   msg.data.clear();
  69 
  70   drive_telegram_1[0] =  0b00000100;   //STW1 high
  71   drive_telegram_1[1] =  0b10000110;   //STW1 low
  72   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  73   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  74   
  75   msg.data.clear();
  76   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
  77     msg.data.push_back(drive_telegram_1[i]);
  78 
  79   pub_drive.publish(msg);
  80   
  81   //Wait for 2 seconds
  82   ros::Duration(2).sleep();
  83     
  84   //---------------------------------------------------------------
  85   //Third telegram - set all necessary bytes except ON/OFF1 to true
  86   //----------------------------------------------------------------
  87   msg_dim.label = "Drive_1_output";
  88   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  89   msg.layout.dim.clear();
  90   msg.layout.dim.push_back(msg_dim);
  91 
  92   msg.data.clear();
  93 
  94   drive_telegram_1[0] =  0b00000100;   //STW1 high
  95   drive_telegram_1[1] =  0b11111110;   //STW1 low
  96   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  97   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  98   
  99   msg.data.clear();
 100   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
 101     msg.data.push_back(drive_telegram_1[i]);
 102 
 103   pub_drive.publish(msg);
 104     
 105   //Wait for 2 seconds
 106   ros::Duration(2).sleep();
 107   
 108   //-------------------------------------------
 109   // Main loop
 110   //-------------------------------------------
 111   while(ros::ok())
 112   {
 113     ROS_INFO("Goal speed: 500");
 114     
 115     //message header
 116     msg_dim.label = "Drive_1_output";
 117     msg_dim.size = DRIVE_TELEGRAM_SIZE;
 118     msg.layout.dim.clear();
 119     msg.layout.dim.push_back(msg_dim);
 120     
 121     setDriveTelegram(drive_telegram_1, 500);
 122         
 123     //copy telegram data
 124     msg.data.clear();
 125     for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
 126         msg.data.push_back(drive_telegram_1[i]);
 127 
 128     //publish
 129     pub_drive.publish(msg);
 130     
 131     //wait for 5 seconds
 132     ros::Duration(5).sleep();
 133     ros::spinOnce();
 134            
 135     //======================================================
 136     
 137     ROS_INFO("Goal speed: 1000");
 138     //message header
 139     msg_dim.label = "Drive_1_output";
 140     msg_dim.size = DRIVE_TELEGRAM_SIZE;
 141     msg.layout.dim.clear();
 142     msg.layout.dim.push_back(msg_dim);
 143         
 144     setDriveTelegram(drive_telegram_1, 1000);
 145     
 146     //copy telegram data
 147     msg.data.clear();
 148     for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
 149         msg.data.push_back(drive_telegram_1[i]);
 150     
 151     //publish
 152     pub_drive.publish(msg);
 153         
 154     //wait for 5 seconds
 155     ros::Duration(5).sleep();
 156     ros::spinOnce();
 157   }   
 158   return (EXIT_SUCCESS);
 159 }
 160     
 161 void setDriveTelegram(unsigned char drive_telegram[], int velocity)   
 162 { 
 163   //Scale to 0x4000 = 100% 
 164   int velocity_rel = (velocity * 0x4000) / N_MAX; 
 165     
 166   //Decompose velocity from int to 2*unsigned char
 167   unsigned char velocity_bytes[2]; 
 168   
 169   velocity_bytes[0] = (velocity_rel >> 8)  & 0xFF;
 170   velocity_bytes[1] = velocity_rel & 0xFF;   
 171   
 172   //Set telegram data
 173   drive_telegram[0] =  0b00000100;           //STW1 high
 174   drive_telegram[1] =  0b11111111;           //STW1 low
 175   drive_telegram[2] =  velocity_bytes[0];    //NSOLL_A high 
 176   drive_telegram[3] =  velocity_bytes[1];    //NSOLL_A low
 177 }
 178 
 179 void driveTelegramCallback(const std_msgs::UInt8MultiArray::ConstPtr &msg)
 180 {
 181   unsigned int received_byte_array[DRIVE_TELEGRAM_SIZE];
 182 
 183   for(unsigned int i = 0; i < DRIVE_TELEGRAM_SIZE; i++)
 184     received_byte_array[i] = msg->data[i];
 185 }

The code explained

In addition to ros.h we also need to include std_msgs::UInt8MultiArray headers:

   2 #include <std_msgs/MultiArrayDimension.h>
   3 #include <std_msgs/UInt8MultiArray.h>
   4 

Declare callback function for input wrapper topic:

   9 void driveTelegramCallback(const std_msgs::UInt8MultiArray::ConstPtr &msg);

Subscribe to input wrapper topic (CU240 -> CP1616) defined in yaml config file:

  19   ros::Subscriber sub_drive = nh.subscribe("/drive_1_input_topic", 1,
  20                                            &driveTelegramCallback);

Create publisher for output wrapper topic (CP1616 -> CU240) as defined in yaml config file:

  23   ros::Publisher pub_drive = nh.advertise<std_msgs::UInt8MultiArray>
  24                                          ("/drive_1_output_topic", 1);

Create drive_telegram_1 and std_msgs::MultiArray variables:

  27   unsigned char drive_telegram_1[DRIVE_TELEGRAM_SIZE];
  28 
  29   //std_msgs::MultiArray variables
  30   std_msgs::MultiArrayDimension msg_dim;
  31   std_msgs::UInt8MultiArray msg;

Following "sleep" method is very important, since PROFINET communication takes some time to start after launch file was executed. If you are experiencing problems, e.g. motor is not willing to initialize even despite the fact, that all data are being transmitted properly, change duration parameter, or add one aditional telegram to the initialization step.

  33   //Wait until communication starts properly
  34   ros::Duration(10).sleep();

To send a message in MultiArray format to the wrapper, you need to fill the "label" and "size" in "msg.layout.dim" structure in accordance with yaml config file first.

  39   msg_dim.label = "Drive_1_output";
  40   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  41   msg.layout.dim.clear();
  42   msg.layout.dim.push_back(msg_dim);

Fill drive_telegram_1 data with intial values - set Master control by PLC bit on:

  46   drive_telegram_1[0] =  0b00000100;   //STW1 high
  47   drive_telegram_1[1] =  0b00000000;   //STW1 low
  48   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  49   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  50 

Fill msg variable and publish First telegram to output wrapper topic:

  51   msg.data.clear();
  52   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
  53     msg.data.push_back(drive_telegram_1[i]);
  54 
  55   pub_drive.publish(msg);

To switch SINAMICS drive on, system needs to pass several initialization states. First three telegrams handle this initialization by switching bits of control word in correct order. While telegram sets Master control by PLC on, second switch on ON/OFF2,3 bits and third swtich on all remaining bits except ON/OFF1:

  60   //--------------------------------------------------
  61   //Second telegram - set ON/OFF2 ON/OFF/3 to true
  62   //--------------------------------------------------
  63   msg_dim.label = "Drive_1_output";
  64   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  65   msg.layout.dim.clear();
  66   msg.layout.dim.push_back(msg_dim);
  67 
  68   msg.data.clear();
  69 
  70   drive_telegram_1[0] =  0b00000100;   //STW1 high
  71   drive_telegram_1[1] =  0b10000110;   //STW1 low
  72   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  73   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  74   
  75   msg.data.clear();
  76   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
  77     msg.data.push_back(drive_telegram_1[i]);
  78 
  79   pub_drive.publish(msg);
  80   
  81   //Wait for 2 seconds
  82   ros::Duration(2).sleep();
  83     
  84   //---------------------------------------------------------------
  85   //Third telegram - set all necessary bytes except ON/OFF1 to true
  86   //----------------------------------------------------------------
  87   msg_dim.label = "Drive_1_output";
  88   msg_dim.size = DRIVE_TELEGRAM_SIZE;
  89   msg.layout.dim.clear();
  90   msg.layout.dim.push_back(msg_dim);
  91 
  92   msg.data.clear();
  93 
  94   drive_telegram_1[0] =  0b00000100;   //STW1 high
  95   drive_telegram_1[1] =  0b11111110;   //STW1 low
  96   drive_telegram_1[2] =  0b00000000;   //NSOLL_A high
  97   drive_telegram_1[3] =  0b00000000;   //NSOLL_A low
  98   
  99   msg.data.clear();
 100   for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
 101     msg.data.push_back(drive_telegram_1[i]);
 102 
 103   pub_drive.publish(msg);
 104     
 105   //Wait for 2 seconds
 106   ros::Duration(2).sleep();

After entering main loop, setDriveTelegram() function is used to set ON/OFF1 bit to true and for calculation and scalling of speed setpoint:

 115     //message header
 116     msg_dim.label = "Drive_1_output";
 117     msg_dim.size = DRIVE_TELEGRAM_SIZE;
 118     msg.layout.dim.clear();
 119     msg.layout.dim.push_back(msg_dim);
 120     
 121     setDriveTelegram(drive_telegram_1, 500);
 122         
 123     //copy telegram data
 124     msg.data.clear();
 125     for(size_t i = 0;  i < DRIVE_TELEGRAM_SIZE; i++)
 126         msg.data.push_back(drive_telegram_1[i]);
 127 
 128     //publish
 129     pub_drive.publish(msg);

Speed setpoint scalling: Siemens uses 0x4000 = 100% notation. It means that if max speed is for example 1500, in order to reach maximum you need to send 0x4000 hex = 16384 dec velocity command (NSOLL_A). setDriveTelegram does this scalling, accepting goal speed in rpm as an input.

 164   int velocity_rel = (velocity * 0x4000) / N_MAX; 
 165     

Notice the least significant bit in STW1 - it is ON/OFF1 bit:

 174   drive_telegram[1] =  0b11111111;           //STW1 low
 175 

driveTelegramCallback is called each time when data are recieved on input topic. Add your own code if you need to do some aditional processing:

 179 void driveTelegramCallback(const std_msgs::UInt8MultiArray::ConstPtr &msg)
 180 {
 181   unsigned int received_byte_array[DRIVE_TELEGRAM_SIZE];
 182 
 183   for(unsigned int i = 0; i < DRIVE_TELEGRAM_SIZE; i++)
 184     received_byte_array[i] = msg->data[i];

If you completed and successfully built your node, you can proceed to following tutorial and test the PROFINET communication between CP1616 and your CU.

Next tutorial: SINAMICS G120 - Runtime

Wiki: siemens_cp1616/Tutorials/SINAMICS G120 - User node (last edited 2015-11-25 21:05:54 by FrantisekDurovsky)