crazyflie_server.cpp
Go to the documentation of this file.
00001 #include "ros/ros.h"
00002 #include "crazyflie_driver/AddCrazyflie.h"
00003 #include "crazyflie_driver/RemoveCrazyflie.h"
00004 #include "crazyflie_driver/LogBlock.h"
00005 #include "crazyflie_driver/GenericLogData.h"
00006 #include "crazyflie_driver/UpdateParams.h"
00007 #include "crazyflie_driver/sendPacket.h"
00008 #include "crazyflie_driver/crtpPacket.h"
00009 #include "crazyflie_cpp/Crazyradio.h"
00010 #include "crazyflie_cpp/crtp.h"
00011 #include "std_srvs/Empty.h"
00012 #include "geometry_msgs/Twist.h"
00013 #include "geometry_msgs/PointStamped.h"
00014 #include "sensor_msgs/Imu.h"
00015 #include "sensor_msgs/Temperature.h"
00016 #include "sensor_msgs/MagneticField.h"
00017 #include "std_msgs/Float32.h"
00018 
00019 //#include <regex>
00020 #include <thread>
00021 #include <mutex>
00022 
00023 #include <string>
00024 #include <map>
00025 
00026 #include <crazyflie_cpp/Crazyflie.h>
00027 
00028 constexpr double pi() { return 3.141592653589793238462643383279502884; }
00029 
00030 double degToRad(double deg) {
00031     return deg / 180.0 * pi();
00032 }
00033 
00034 double radToDeg(double rad) {
00035     return rad * 180.0 / pi();
00036 }
00037 
00038 class CrazyflieROS
00039 {
00040 public:
00041   CrazyflieROS(
00042     const std::string& link_uri,
00043     const std::string& tf_prefix,
00044     float roll_trim,
00045     float pitch_trim,
00046     bool enable_logging,
00047     bool enable_parameters,
00048     std::vector<crazyflie_driver::LogBlock>& log_blocks,
00049     bool use_ros_time,
00050     bool enable_logging_imu,
00051     bool enable_logging_temperature,
00052     bool enable_logging_magnetic_field,
00053     bool enable_logging_pressure,
00054     bool enable_logging_battery,
00055     bool enable_logging_packets)
00056     : m_cf(link_uri)
00057     , m_tf_prefix(tf_prefix)
00058     , m_isEmergency(false)
00059     , m_roll_trim(roll_trim)
00060     , m_pitch_trim(pitch_trim)
00061     , m_enableLogging(enable_logging)
00062     , m_enableParameters(enable_parameters)
00063     , m_logBlocks(log_blocks)
00064     , m_use_ros_time(use_ros_time)
00065     , m_enable_logging_imu(enable_logging_imu)
00066     , m_enable_logging_temperature(enable_logging_temperature)
00067     , m_enable_logging_magnetic_field(enable_logging_magnetic_field)
00068     , m_enable_logging_pressure(enable_logging_pressure)
00069     , m_enable_logging_battery(enable_logging_battery)
00070     , m_enable_logging_packets(enable_logging_packets)
00071     , m_serviceEmergency()
00072     , m_serviceUpdateParams()
00073     , m_subscribeCmdVel()
00074     , m_subscribeExternalPosition()
00075     , m_pubImu()
00076     , m_pubTemp()
00077     , m_pubMag()
00078     , m_pubPressure()
00079     , m_pubBattery()
00080     , m_pubRssi()
00081     , m_sentSetpoint(false)
00082     , m_sentExternalPosition(false)
00083   {
00084     ros::NodeHandle n;
00085     m_subscribeCmdVel = n.subscribe(tf_prefix + "/cmd_vel", 1, &CrazyflieROS::cmdVelChanged, this);
00086     m_subscribeExternalPosition = n.subscribe(tf_prefix + "/external_position", 1, &CrazyflieROS::positionMeasurementChanged, this);
00087     m_serviceEmergency = n.advertiseService(tf_prefix + "/emergency", &CrazyflieROS::emergency, this);
00088 
00089     if (m_enable_logging_imu) {
00090       m_pubImu = n.advertise<sensor_msgs::Imu>(tf_prefix + "/imu", 10);
00091     }
00092     if (m_enable_logging_temperature) {
00093       m_pubTemp = n.advertise<sensor_msgs::Temperature>(tf_prefix + "/temperature", 10);
00094     }
00095     if (m_enable_logging_magnetic_field) {
00096       m_pubMag = n.advertise<sensor_msgs::MagneticField>(tf_prefix + "/magnetic_field", 10);
00097     }
00098     if (m_enable_logging_pressure) {
00099       m_pubPressure = n.advertise<std_msgs::Float32>(tf_prefix + "/pressure", 10);
00100     }
00101     if (m_enable_logging_battery) {
00102       m_pubBattery = n.advertise<std_msgs::Float32>(tf_prefix + "/battery", 10);
00103     }
00104     if (m_enable_logging_packets) {
00105       m_pubPackets = n.advertise<crazyflie_driver::crtpPacket>(tf_prefix + "/packets", 10);
00106     }
00107     m_pubRssi = n.advertise<std_msgs::Float32>(tf_prefix + "/rssi", 10);
00108 
00109     for (auto& logBlock : m_logBlocks)
00110     {
00111       m_pubLogDataGeneric.push_back(n.advertise<crazyflie_driver::GenericLogData>(tf_prefix + "/" + logBlock.topic_name, 10));
00112     }
00113 
00114     m_sendPacketServer = n.advertiseService(tf_prefix + "/send_packet"  , &CrazyflieROS::sendPacket, this);
00115 
00116     m_thread = std::thread(&CrazyflieROS::run, this);
00117   }
00118 
00119   void stop()
00120   {
00121     ROS_INFO("Disconnecting ...");
00122     m_isEmergency = true;
00123     m_thread.join();
00124   }
00125 
00132   bool sendPacket (
00133     crazyflie_driver::sendPacket::Request &req,
00134     crazyflie_driver::sendPacket::Response &res)
00135   {
00137     crtpPacket_t packet;
00138     packet.size = req.packet.size;
00139     packet.header = req.packet.header;
00140     for (int i = 0; i < CRTP_MAX_DATA_SIZE; i++) {
00141       packet.data[i] = req.packet.data[i];
00142     }
00143     m_cf.queueOutgoingPacket(packet);
00144     return true;
00145   }
00146 
00147 private:
00148   ros::ServiceServer m_sendPacketServer;
00149 
00154   void publishPackets() {
00155     std::vector<Crazyradio::Ack> packets = m_cf.retrieveGenericPackets();
00156     if (!packets.empty())
00157     {
00158       std::vector<Crazyradio::Ack>::iterator it;
00159       for (it = packets.begin(); it != packets.end(); it++)
00160       {
00161         crazyflie_driver::crtpPacket packet;
00162         packet.size = it->size;
00163         packet.header = it->data[0];
00164         for(int i = 0; i < packet.size; i++)
00165         {
00166           packet.data[i] = it->data[i+1];
00167         }
00168         m_pubPackets.publish(packet);
00169       }
00170     }
00171   }
00172 
00173 private:
00174   struct logImu {
00175     float acc_x;
00176     float acc_y;
00177     float acc_z;
00178     float gyro_x;
00179     float gyro_y;
00180     float gyro_z;
00181   } __attribute__((packed));
00182 
00183   struct log2 {
00184     float mag_x;
00185     float mag_y;
00186     float mag_z;
00187     float baro_temp;
00188     float baro_pressure;
00189     float pm_vbat;
00190   } __attribute__((packed));
00191 
00192 private:
00193   bool emergency(
00194     std_srvs::Empty::Request& req,
00195     std_srvs::Empty::Response& res)
00196   {
00197     ROS_FATAL("Emergency requested!");
00198     m_isEmergency = true;
00199 
00200     return true;
00201   }
00202 
00203   template<class T, class U>
00204   void updateParam(uint8_t id, const std::string& ros_param) {
00205       U value;
00206       ros::param::get(ros_param, value);
00207       m_cf.setParam<T>(id, (T)value);
00208   }
00209 
00210   bool updateParams(
00211     crazyflie_driver::UpdateParams::Request& req,
00212     crazyflie_driver::UpdateParams::Response& res)
00213   {
00214     ROS_INFO("Update parameters");
00215     for (auto&& p : req.params) {
00216       std::string ros_param = "/" + m_tf_prefix + "/" + p;
00217       size_t pos = p.find("/");
00218       std::string group(p.begin(), p.begin() + pos);
00219       std::string name(p.begin() + pos + 1, p.end());
00220 
00221       auto entry = m_cf.getParamTocEntry(group, name);
00222       if (entry)
00223       {
00224         switch (entry->type) {
00225           case Crazyflie::ParamTypeUint8:
00226             updateParam<uint8_t, int>(entry->id, ros_param);
00227             break;
00228           case Crazyflie::ParamTypeInt8:
00229             updateParam<int8_t, int>(entry->id, ros_param);
00230             break;
00231           case Crazyflie::ParamTypeUint16:
00232             updateParam<uint16_t, int>(entry->id, ros_param);
00233             break;
00234           case Crazyflie::ParamTypeInt16:
00235             updateParam<int16_t, int>(entry->id, ros_param);
00236             break;
00237           case Crazyflie::ParamTypeUint32:
00238             updateParam<uint32_t, int>(entry->id, ros_param);
00239             break;
00240           case Crazyflie::ParamTypeInt32:
00241             updateParam<int32_t, int>(entry->id, ros_param);
00242             break;
00243           case Crazyflie::ParamTypeFloat:
00244             updateParam<float, float>(entry->id, ros_param);
00245             break;
00246         }
00247       }
00248       else {
00249         ROS_ERROR("Could not find param %s/%s", group.c_str(), name.c_str());
00250       }
00251     }
00252     return true;
00253   }
00254 
00255   void cmdVelChanged(
00256     const geometry_msgs::Twist::ConstPtr& msg)
00257   {
00258     if (!m_isEmergency) {
00259       float roll = msg->linear.y + m_roll_trim;
00260       float pitch = - (msg->linear.x + m_pitch_trim);
00261       float yawrate = msg->angular.z;
00262       uint16_t thrust = std::min<uint16_t>(std::max<float>(msg->linear.z, 0.0), 60000);
00263 
00264       m_cf.sendSetpoint(roll, pitch, yawrate, thrust);
00265       m_sentSetpoint = true;
00266     }
00267   }
00268 
00269   void positionMeasurementChanged(
00270     const geometry_msgs::PointStamped::ConstPtr& msg)
00271   {
00272     m_cf.sendExternalPositionUpdate(msg->point.x, msg->point.y, msg->point.z);
00273     m_sentExternalPosition = true;
00274   }
00275 
00276   void run()
00277   {
00278     // m_cf.reboot();
00279 
00280     auto start = std::chrono::system_clock::now();
00281 
00282     m_cf.logReset();
00283 
00284     std::function<void(float)> cb_lq = std::bind(&CrazyflieROS::onLinkQuality, this, std::placeholders::_1);
00285     m_cf.setLinkQualityCallback(cb_lq);
00286 
00287 
00288 
00289     if (m_enableParameters)
00290     {
00291       ROS_INFO("Requesting parameters...");
00292       m_cf.requestParamToc();
00293       for (auto iter = m_cf.paramsBegin(); iter != m_cf.paramsEnd(); ++iter) {
00294         auto entry = *iter;
00295         std::string paramName = "/" + m_tf_prefix + "/" + entry.group + "/" + entry.name;
00296         switch (entry.type) {
00297           case Crazyflie::ParamTypeUint8:
00298             ros::param::set(paramName, m_cf.getParam<uint8_t>(entry.id));
00299             break;
00300           case Crazyflie::ParamTypeInt8:
00301             ros::param::set(paramName, m_cf.getParam<int8_t>(entry.id));
00302             break;
00303           case Crazyflie::ParamTypeUint16:
00304             ros::param::set(paramName, m_cf.getParam<uint16_t>(entry.id));
00305             break;
00306           case Crazyflie::ParamTypeInt16:
00307             ros::param::set(paramName, m_cf.getParam<int16_t>(entry.id));
00308             break;
00309           case Crazyflie::ParamTypeUint32:
00310             ros::param::set(paramName, (int)m_cf.getParam<uint32_t>(entry.id));
00311             break;
00312           case Crazyflie::ParamTypeInt32:
00313             ros::param::set(paramName, m_cf.getParam<int32_t>(entry.id));
00314             break;
00315           case Crazyflie::ParamTypeFloat:
00316             ros::param::set(paramName, m_cf.getParam<float>(entry.id));
00317             break;
00318         }
00319       }
00320       ros::NodeHandle n;
00321       m_serviceUpdateParams = n.advertiseService(m_tf_prefix + "/update_params", &CrazyflieROS::updateParams, this);
00322     }
00323 
00324     std::unique_ptr<LogBlock<logImu> > logBlockImu;
00325     std::unique_ptr<LogBlock<log2> > logBlock2;
00326     std::vector<std::unique_ptr<LogBlockGeneric> > logBlocksGeneric(m_logBlocks.size());
00327     if (m_enableLogging) {
00328 
00329       std::function<void(const crtpPlatformRSSIAck*)> cb_ack = std::bind(&CrazyflieROS::onEmptyAck, this, std::placeholders::_1);
00330       m_cf.setEmptyAckCallback(cb_ack);
00331 
00332       ROS_INFO("Requesting Logging variables...");
00333       m_cf.requestLogToc();
00334 
00335       if (m_enable_logging_imu) {
00336         std::function<void(uint32_t, logImu*)> cb = std::bind(&CrazyflieROS::onImuData, this, std::placeholders::_1, std::placeholders::_2);
00337 
00338         logBlockImu.reset(new LogBlock<logImu>(
00339           &m_cf,{
00340             {"acc", "x"},
00341             {"acc", "y"},
00342             {"acc", "z"},
00343             {"gyro", "x"},
00344             {"gyro", "y"},
00345             {"gyro", "z"},
00346           }, cb));
00347         logBlockImu->start(1); // 10ms
00348       }
00349 
00350       if (   m_enable_logging_temperature
00351           || m_enable_logging_magnetic_field
00352           || m_enable_logging_pressure
00353           || m_enable_logging_battery)
00354       {
00355         std::function<void(uint32_t, log2*)> cb2 = std::bind(&CrazyflieROS::onLog2Data, this, std::placeholders::_1, std::placeholders::_2);
00356 
00357         logBlock2.reset(new LogBlock<log2>(
00358           &m_cf,{
00359             {"mag", "x"},
00360             {"mag", "y"},
00361             {"mag", "z"},
00362             {"baro", "temp"},
00363             {"baro", "pressure"},
00364             {"pm", "vbat"},
00365           }, cb2));
00366         logBlock2->start(10); // 100ms
00367       }
00368 
00369       // custom log blocks
00370       size_t i = 0;
00371       for (auto& logBlock : m_logBlocks)
00372       {
00373         std::function<void(uint32_t, std::vector<double>*, void* userData)> cb =
00374           std::bind(
00375             &CrazyflieROS::onLogCustom,
00376             this,
00377             std::placeholders::_1,
00378             std::placeholders::_2,
00379             std::placeholders::_3);
00380 
00381         logBlocksGeneric[i].reset(new LogBlockGeneric(
00382           &m_cf,
00383           logBlock.variables,
00384           (void*)&m_pubLogDataGeneric[i],
00385           cb));
00386         logBlocksGeneric[i]->start(logBlock.frequency / 10);
00387         ++i;
00388       }
00389 
00390 
00391     }
00392 
00393     ROS_INFO("Ready...");
00394     auto end = std::chrono::system_clock::now();
00395     std::chrono::duration<double> elapsedSeconds = end-start;
00396     ROS_INFO("Elapsed: %f s", elapsedSeconds.count());
00397 
00398     // Send 0 thrust initially for thrust-lock
00399     for (int i = 0; i < 100; ++i) {
00400        m_cf.sendSetpoint(0, 0, 0, 0);
00401     }
00402 
00403     while(!m_isEmergency) {
00404       // make sure we ping often enough to stream data out
00405       if (m_enableLogging && !m_sentSetpoint && !m_sentExternalPosition) {
00406         m_cf.transmitPackets();
00407         m_cf.sendPing();
00408         if(m_enable_logging_packets) {
00409           this->publishPackets();
00410         }
00411       }
00412       m_sentSetpoint = false;
00413       m_sentExternalPosition = false;
00414       std::this_thread::sleep_for(std::chrono::milliseconds(1));
00415     }
00416 
00417     // Make sure we turn the engines off
00418     for (int i = 0; i < 100; ++i) {
00419        m_cf.sendSetpoint(0, 0, 0, 0);
00420     }
00421 
00422   }
00423 
00424   void onImuData(uint32_t time_in_ms, logImu* data) {
00425     if (m_enable_logging_imu) {
00426       sensor_msgs::Imu msg;
00427       if (m_use_ros_time) {
00428         msg.header.stamp = ros::Time::now();
00429       } else {
00430         msg.header.stamp = ros::Time(time_in_ms / 1000.0);
00431       }
00432       msg.header.frame_id = m_tf_prefix + "/base_link";
00433       msg.orientation_covariance[0] = -1;
00434 
00435       // measured in deg/s; need to convert to rad/s
00436       msg.angular_velocity.x = degToRad(data->gyro_x);
00437       msg.angular_velocity.y = degToRad(data->gyro_y);
00438       msg.angular_velocity.z = degToRad(data->gyro_z);
00439 
00440       // measured in mG; need to convert to m/s^2
00441       msg.linear_acceleration.x = data->acc_x * 9.81;
00442       msg.linear_acceleration.y = data->acc_y * 9.81;
00443       msg.linear_acceleration.z = data->acc_z * 9.81;
00444 
00445       m_pubImu.publish(msg);
00446     }
00447   }
00448 
00449   void onLog2Data(uint32_t time_in_ms, log2* data) {
00450 
00451     if (m_enable_logging_temperature) {
00452       sensor_msgs::Temperature msg;
00453       if (m_use_ros_time) {
00454         msg.header.stamp = ros::Time::now();
00455       } else {
00456         msg.header.stamp = ros::Time(time_in_ms / 1000.0);
00457       }
00458       msg.header.frame_id = m_tf_prefix + "/base_link";
00459       // measured in degC
00460       msg.temperature = data->baro_temp;
00461       m_pubTemp.publish(msg);
00462     }
00463 
00464     if (m_enable_logging_magnetic_field) {
00465       sensor_msgs::MagneticField msg;
00466       if (m_use_ros_time) {
00467         msg.header.stamp = ros::Time::now();
00468       } else {
00469         msg.header.stamp = ros::Time(time_in_ms / 1000.0);
00470       }
00471       msg.header.frame_id = m_tf_prefix + "/base_link";
00472 
00473       // measured in Tesla
00474       msg.magnetic_field.x = data->mag_x;
00475       msg.magnetic_field.y = data->mag_y;
00476       msg.magnetic_field.z = data->mag_z;
00477       m_pubMag.publish(msg);
00478     }
00479 
00480     if (m_enable_logging_pressure) {
00481       std_msgs::Float32 msg;
00482       // hPa (=mbar)
00483       msg.data = data->baro_pressure;
00484       m_pubPressure.publish(msg);
00485     }
00486 
00487     if (m_enable_logging_battery) {
00488       std_msgs::Float32 msg;
00489       // V
00490       msg.data = data->pm_vbat;
00491       m_pubBattery.publish(msg);
00492     }
00493   }
00494 
00495   void onLogCustom(uint32_t time_in_ms, std::vector<double>* values, void* userData) {
00496 
00497     ros::Publisher* pub = reinterpret_cast<ros::Publisher*>(userData);
00498 
00499     crazyflie_driver::GenericLogData msg;
00500     if (m_use_ros_time) {
00501       msg.header.stamp = ros::Time::now();
00502     } else {
00503       msg.header.stamp = ros::Time(time_in_ms / 1000.0);
00504     }
00505     msg.header.frame_id = m_tf_prefix + "/base_link";
00506     msg.values = *values;
00507 
00508     pub->publish(msg);
00509   }
00510 
00511   void onEmptyAck(const crtpPlatformRSSIAck* data) {
00512       std_msgs::Float32 msg;
00513       // dB
00514       msg.data = data->rssi;
00515       m_pubRssi.publish(msg);
00516   }
00517 
00518   void onLinkQuality(float linkQuality) {
00519       if (linkQuality < 0.7) {
00520         ROS_WARN("Link Quality low (%f)", linkQuality);
00521       }
00522   }
00523 
00524 private:
00525   Crazyflie m_cf;
00526   std::string m_tf_prefix;
00527   bool m_isEmergency;
00528   float m_roll_trim;
00529   float m_pitch_trim;
00530   bool m_enableLogging;
00531   bool m_enableParameters;
00532   std::vector<crazyflie_driver::LogBlock> m_logBlocks;
00533   bool m_use_ros_time;
00534   bool m_enable_logging_imu;
00535   bool m_enable_logging_temperature;
00536   bool m_enable_logging_magnetic_field;
00537   bool m_enable_logging_pressure;
00538   bool m_enable_logging_battery;
00539   bool m_enable_logging_packets;
00540 
00541   ros::ServiceServer m_serviceEmergency;
00542   ros::ServiceServer m_serviceUpdateParams;
00543   ros::Subscriber m_subscribeCmdVel;
00544   ros::Subscriber m_subscribeExternalPosition;
00545   ros::Publisher m_pubImu;
00546   ros::Publisher m_pubTemp;
00547   ros::Publisher m_pubMag;
00548   ros::Publisher m_pubPressure;
00549   ros::Publisher m_pubBattery;
00550   ros::Publisher m_pubPackets;
00551   ros::Publisher m_pubRssi;
00552   std::vector<ros::Publisher> m_pubLogDataGeneric;
00553 
00554   bool m_sentSetpoint, m_sentExternalPosition;
00555 
00556   std::thread m_thread;
00557 };
00558 
00559 static std::map<std::string, CrazyflieROS*> crazyflies;
00560 
00561 bool add_crazyflie(
00562   crazyflie_driver::AddCrazyflie::Request  &req,
00563   crazyflie_driver::AddCrazyflie::Response &res)
00564 {
00565   ROS_INFO("Adding %s as %s with trim(%f, %f). Logging: %d, Parameters: %d, Use ROS time: %d",
00566     req.uri.c_str(),
00567     req.tf_prefix.c_str(),
00568     req.roll_trim,
00569     req.pitch_trim,
00570     req.enable_parameters,
00571     req.enable_logging,
00572     req.use_ros_time);
00573 
00574   // Ignore if the uri is already in use
00575   if (crazyflies.find(req.uri) != crazyflies.end()) {
00576     ROS_ERROR("Cannot add %s, already added.", req.uri.c_str());
00577     return false;
00578   }
00579 
00580   CrazyflieROS* cf = new CrazyflieROS(
00581     req.uri,
00582     req.tf_prefix,
00583     req.roll_trim,
00584     req.pitch_trim,
00585     req.enable_logging,
00586     req.enable_parameters,
00587     req.log_blocks,
00588     req.use_ros_time,
00589     req.enable_logging_imu,
00590     req.enable_logging_temperature,
00591     req.enable_logging_magnetic_field,
00592     req.enable_logging_pressure,
00593     req.enable_logging_battery,
00594     req.enable_logging_packets);
00595 
00596   crazyflies[req.uri] = cf;
00597 
00598   return true;
00599 }
00600 
00601 bool remove_crazyflie(
00602   crazyflie_driver::RemoveCrazyflie::Request  &req,
00603   crazyflie_driver::RemoveCrazyflie::Response &res)
00604 {
00605 
00606   if (crazyflies.find(req.uri) == crazyflies.end()) {
00607     ROS_ERROR("Cannot remove %s, not connected.", req.uri.c_str());
00608     return false;
00609   }
00610 
00611   ROS_INFO("Removing crazyflie at uri %s.", req.uri.c_str());
00612 
00613   crazyflies[req.uri]->stop();
00614   delete crazyflies[req.uri];
00615   crazyflies.erase(req.uri);
00616 
00617   ROS_INFO("Crazyflie %s removed.", req.uri.c_str());
00618 
00619   return true;
00620 }
00621 
00622 int main(int argc, char **argv)
00623 {
00624   ros::init(argc, argv, "crazyflie_server");
00625   ros::NodeHandle n;
00626 
00627   ros::ServiceServer serviceAdd = n.advertiseService("add_crazyflie", add_crazyflie);
00628   ros::ServiceServer serviceRemove = n.advertiseService("remove_crazyflie", remove_crazyflie);
00629   ros::spin();
00630 
00631   return 0;
00632 }


crazyflie_driver
Author(s): Wolfgang Hoenig
autogenerated on Sun Oct 8 2017 02:48:03