机器人领域C++/ROS/TCP编程
2020-12-03----------------------------------------------------------------------------------------------------------------------------------------------------------
ROS中,发布多个odo和laser frame之间的变换,并在odo frame下发布位姿和路径,在laser frame下发布点云
void publish(){geometry_msgs::Quaternion q = tf::createQuaternionMsgFromYaw(current_pose_(2));// 发布tf变换,rviz也依赖这里的变换去表示坐标系之间的相对位姿geometry_msgs::TransformStamped odo_transform;odo_transform.header.stamp = ros::Time::now();odo_transform.header.frame_id = "odo";// 当前transform所表示的父坐标系odo_transform.child_frame_id = "scan";// 当前transform的子坐标系odo_transform.transform.translation.x = current_pose_(0);odo_transform.transform.translation.y = current_pose_(1);odo_transform.transform.rotation = q;tf_.sendTransform(odo_transform);// tf::TransformBroadcaster tf_;// 发布里程计消息,在里程计坐标系下表示,rviz中的效果为三维坐标系/箭头的移动nav_msgs::Odometry odo;odo.header.stamp = ros::Time::now();odo.header.frame_id = "odo";odo.child_frame_id = "scan";// 这里的意义是什么?只有这个不发布tf不可以odo.pose.pose.position.x = current_pose_(0);odo.pose.pose.position.y = current_pose_(1);odo.pose.pose.orientation = q;laser_odo_pub_.publish(odo);// 发布path,即带时间戳的位姿的集合,在rviz中的效果就是路径
// geometry_msgs::PoseStamped pose;
// pose.header.frame_id = "scan";
// pose.pose.orientation.w = std::cos(current_pose_(2) * 0.5);
// pose.pose.orientation.z = std::sin(current_pose_(2) * 0.5);
// pose.pose.position.x = current_pose_(0);
// pose.pose.position.y = current_pose_(1);
// path_.poses.push_back(pose);// nav_msgs::Path path_;
// nav_pub.publish(path_);static int count = 0;
// 每隔一定时间在scan坐标系下发布当前的点云数据,调整decay time使之长时间在odo坐标系中保留
// 因此,如果想要在scan坐标系下发布,而没有往odo中认为做变换,就需要知道scan和odo frame之间的关系,即tf所发布的消息if(count % 10 == 0){// 发布的实际上是预处理后的点cloud_.points.clear();for(const auto& point : ref_->points){geometry_msgs::Point32 p;if(point.position.norm() < 0.3)// 预处理后的无效点为0,需要过滤掉continue;p.x = point.position(0);p.y = point.position(1);p.z = 0.0;cloud_.points.emplace_back(p);}cloud_pub_.publish(cloud_);}count++;}
2020-10-29----------------------------------------------------------------------------------------------------------------------------------------------------------
C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)
C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)
2020-10-28----------------------------------------------------------------------------------------------------------------------------------------------------------
层级关系:
workspace下有以下几个文件夹:
- src
- build
- devel
src文件夹下:
- package1
- package2
- CmakeLists.txt
package下:
- src
- include
- CmakeLists.txt
- package.xml
创建新package
创建一个package的方法为在src文件夹下执行
catkin_create_pkg [package_name] [dependes1] [dependes2] ...
这时会自动在src下生成该package,并带有以上四个文件夹.(一个类似的指令为roscreate-pkg,作用不一样,在这里不可以使用)
编译方法:
在workspace下执行:
catkin_make
等同于在workspace/src下执行:
cmake ..
make
release模式编译workspace下所有packages:
catkin_make -DCMAKE_BUILD_TYPE=RELEASE
其中relase可不全大写.注意这里是-DCMAKE_...(因为是给cmake指定的)
只编译指定package:
catkin_make -DCATKIN_WHITELIST_PACKAGES="package_name"
注意这里是-DCATKIN_.... (因为是给catkin_make指定的)
还有指定安装目录:
catkin_make -DCMAKE_INSTALL_PREFIX=...
2020-09-24----------------------------------------------------------------------------------------------------------------------------------------------------------
Eigen中使用auto可能存在的风险
void test(obj1, obj2){const auto value = ob1.method1() - ob2.method2();std::cout << value << std::endl;Eigen::Vector2d v = Eigen::Vector2d::Zeros();// 如果这句与Eigen无关,则不会有问题std::cout << value << std::endl;
}
上述method1和method的返回的都是Eigen::Vector2d形式的变量,但是obj1.method()的低层是:
const Eigen::Vector2d getPose()const{Eigen::Vector2d pose = state_post_.head(2);return pose;}
其中,state_post_是动态大小的即Xd,只不过在类的构造函数中初始化了.
令人震惊的是,上面两次std::cout输出的结果是不一样的,而且如果连续循环多次调用test并给定多个不同参数,不同循环间的第二次输出,结果是一样的,但是单次循环内的两次输出结果不同!!!
原因肯定跟obj1中的state_post_是动态大小有关,但具体细节想不明白,什么原因呢???
解决方法是不要使用auto而是显式定义:
const Eigen::Vector2d value = ob1.method1() - ob2.method2();
原因希望以后有机会弄清楚!!!!
2020-09-22-----------------------------------------------------------------------------------------------------------------------------------------------------------
ROS中的回调函数
1/可以定义成全局的callback函数
参考这里的文章https://www.cnblogs.com/tiderfang/p/8968124.html
需要注意的是回调函数的第一个参数的类型const sensor_msgs::XXXX::ConstPtr&或者const sensor_msgs::XXXXConstPtr&,这个是由ros的回调机制所决定的,如果这个参数要作为参数传递给别的函数,注意类型;
如果回调函数只需要这一个参数,可以缺省;
如果需要多个参数需要使用boost::bind,绑定成一个.
2/也可以使用类的成员函数作为回调函数
参考这里的文章https://blog.csdn.net/m0_38089090/article/details/81195776
需要注意的是,需要指定类的实例,即:
ros::Subscriber sub = n.subscribe<nav_msgs::Odometry>("chatter", 1000, &Listener::callback, &listener);
当然,类内可以使用this,可能需要使用bind
odom_sub = private_nh.subscribe<nav_msgs::Odometry>("odom", 1, boost::bind(&PatrolRobot::odomCB, this, _1));
需要注意的是,这里需要指定subscribe的话题类型,这与全局callback函数不同,
如果成员函数的输入参数不止一个,也需要bind.同样注意,这些成员函数的输入参数类型需要与ros回调机制所决定的一致.
笔者曾将尝试,将类或者类的成员函数作为参数bind后作为全局callback的回调函数,笔者之所以想调用callback再调用成员函数,而不是直接调用成员函数,原因在于想让成员函数执行后的结果返回到全局的callback函数中,从而进行可视化.
但是!!!其实类的内部也可以定义ros::NodeHandle和Publisher等,不需要再传递回到main函数,只要保证main函数里ros::init正常执行了就可以了.虽然没有解决把成员变量作为参数传递给全局callback的需求(可能还是跟我的类是模板类有关),但发现了这么做是完全没有必要的,在类内部发布就可以了,更加方便.
2020-09-20-----------------------------------------------------------------------------------------------------------------------------------------------------------
int**以及如何new二维数组
a) int a;表示一个内存空间,这个空间用来存放一个整数(int);
b) int* a;表示一个内存空间,这个空间用来存放一个指针,这个指针指向一个存放整数的空间,即a)中提到的空间;
c) int** a;表示一个内存空间,这个空间用来存放一个指针,这个指针指向一个存放指针的空间,并且指向的这个空间中的指针,指向一个整数。也简单的说,指向了一个b)中提到的空间;
d) int (*a)[4];表示一个内存空间,这个空间用来存放一个指针,这个指针指向一个长度为4、类型为int的数组;和int** a的区别在于,++、+=1之后的结果不一样,其他用法基本相同。
e) int (*a)(int);表示一个内存空间,这个空间用来存放一个指针,这个指针指向一个函数,这个函数有一个类型为int的参数,并且函数的返回类型也是int。
int* p[]:int*的数组,p的类型应该是int**
new多维数组的方法:
1/ int (* array2D)[5] = new int[n][5];也就是上面的d),需要知道第二个维度的大小,这样,array2D才知道要移动5个位置;
2/ int **array2D = new int *[height]; 然后再挨个new array2D[i],需要注意的是,这个在内容上是不连续的,访问防止越界;
3/ std::vector
举例:
int** q = new int*[10];// new出来了10个int*的空间,但是int*只是指针,其所指向的空间还没有new呢喔,需要逐个newint (*p)[5] = new int[10][5];// p实际上还是指向指针的指针,只不过这里的对一个指针指得是固定指向5个int大小的int*for(int row = 0; row < 10; ++row){for(int col = 0; col < 5; ++col){p[row][col] = row * 5 + col;}}std::cout << *(*p++) << std::endl;std::cout << *(*p++) << std::endl;std::cout << *(*p++) << std::endl;
参考:
https://blog.csdn.net/wang13342322203/article/details/85228415
https://blog.csdn.net/yuqinjh/article/details/79095787
2020-09-18----------------------------------------------------------------------------------------------------------------------------------------------------------
ROS中LaserScan转换成PointCloud以及PointCloud2:
首先是LaserScan->PointCloud:
有两种方法,一种是自己一开始的笨方法:
sensor_msgs::PointCloud cloud;int len = scan->ranges.size();cloud.header = scan->header;cloud.header.frame_id = "map";cloud.points.resize(len);cloud.channels.resize(1);cloud.channels[0].name = "intensity";cloud.channels[0].values.resize(len);for(int index = 0; index < len; ++index){geometry_msgs::Point32 p;double angle = scan->angle_min + scan->angle_increment * index;p.x = std::cos(angle) * scan->ranges[index];p.y = std::sin(angle) * scan->ranges[index];cloud.points[index] = p;cloud.channels[0].values[index] = scan->intensities[index];}
这里要注意channel的处理,当然不是必要的,但是如果需要intensity信息,就必须要设置.scan中默认存在intensity这个成员变量,转换成PointCloud的话,则需要自己定义这么个东西,具体方法上先resize这个channel数组成1,代表我只有一个channel(如果不resize可能会有问题,没法确定共有几个channel有意义).对于这个channel,名字是intensity, values数组连续填充即可.相对于scan,这样更省空间.当然这样操作比较繁琐.
另一种方法,调用库内置的函数方法:
// cmake中添加laser_gemotry这个catkin的模块// #include <laser_geometry/laser_geometry.h>sensor_msgs::PointCloud cloud;laser_geometry::LaserProjection project;project.projectLaser(*scan, cloud);cloud.header.frame_id = "map";
当然还有方法直接调用tf进行点云的畸变校正和坐标变换,参考http://wiki.ros.org/laser_pipeline/Tutorials/IntroductionToWorkingWithLaserScannerData
接下来,是PointCloud<->PointCloud2,直接调用sensor_msg空间下的方法:
sensor_msgs::PointCloud2 cloud_pub;sensor_msgs::PointCloud cloud;sensor_msgs::convertPointCloudToPointCloud2(cloud, cloud_pub);// 当然,sensor_msgs::convertPointCloud2ToPointCloud也是存在的
除此之外还有ROS和PCL中多种PointCloud之间的转换:
pcl::PointCloud<pcl::PointXYZ> pcl的点云pcl::PCLPointCloud2 pcl的第二种点云sensor_msgs::PointCloud2 ROS中的点云sensor_msgs::PointCloud ROS中的点云
参考:https://zhuanlan.zhihu.com/p/55958811
2020-09-01-----------------------------------------------------------------------------------------------------------------------------------------------------------
这里犯了一个很严重的错误,混淆了reserve和resize的概念.
reserve只是保留了足够的空间,并不影响size,因此reserv后没有经过push_back, emplace_back或者resize过,那么size==0, 不可直接使用[]进行索引;而resize改变了大小.同时默认赋值为0(不同编译器可能有差别).
std::vector<int> test;test.reserve(3);// 只是保留了空间,千万不可用[]索引,在知道大小的情况下resize后可以,因为resize后size变了test.resize(3);// size变为3,且前三位默认是0LOG(INFO) << "After resize: " << test.size();LOG(INFO) << test[0] << " " << test[1] << " " << test[2];test.emplace_back(1);// 此时size变成了4LOG(INFO) << test.size();test[1] = 2;LOG(INFO) << test.size();return 0;
2020-08-20----------------------------------------------------------------------------------------------------------------------------------------------------------
取余数,周期性的角度限制范围
#include <cmath>
#include <glog/logging.h>
int main(int argc, char** argv){LOG(INFO) << std::remainder(5.0, 3.0);LOG(INFO) << std::remainder(-5.0, 3.0);LOG(INFO) << std::fmod(5.0, 3.0);LOG(INFO) << std::fmod(-5.0, 3.0);return 0;
}
// 输出结果:
//I0820 15:51:18.592721 655 remainder.cpp:7] -1
//I0820 15:51:18.592793 655 remainder.cpp:8] 1
//I0820 15:51:18.592806 655 remainder.cpp:9] 2
//I0820 15:51:18.592810 655 remainder.cpp:10] -2
// 也就是说fmod的输出的符号与arg1相同
// remainder则有可能变号,输出范围为(-arg2/2, arg2/2)
2020-07-23----------------------------------------------------------------------------------------------------------------------------------------------------------ROS中workspace下,cmake文件,.xml文件,以及launch文件的关系:
cmake文件中包含project name,以及若干个executable name;
package.xml中只有一个package name标志了目前的这个文件夹属于这个package;
launch文件格式如下:
<launch><node pkg="rslidar_sdk" name="rslidar_sdk_node" type="rslidar_sdk_node" output="screen"></node><!-- rviz --><node pkg="rviz" name="rviz" type="rviz" args="-d $(find rslidar_sdk)/rviz/rviz.rviz" />
</launch>
第2行指定了package name,指的是带有该名字的.xml文件所在package(cmake文件中的project name要与此一致,一一对应),这里就是指的本package;
type指的是该package下的node,对应的是cmake文件中add的同名executable文件,所以两者的名字也必须一一对应(源文件中的ros::init(arc, argv, "node_name")最好与也与此一致);
而name则是将该可执行文件的名字remap成一个新的node名字。
2020-07-22----------------------------------------------------------------------------------------------------------------------------------------------------------
lower_bound(val):返回容器中第一个值【大于或等于】val的元素的iterator位置。
upper_bound(val): 返回容器中第一个值【大于】
真正的物理意义在于,lower_bound在于找到第一个可以插入的位置,即如果有等于val的返回第一个等于val位置,如果没有返回第一个大于val的;upper_bound可以找到最后一个可以插入的地方,也就是第一个大于val的位置.
2020-07-01----------------------------------------------------------------------------------------------------------------------------------------------------------sudo apt-get update不成功:
1. 设置源;
2. 报错内容与自己设置的某些第三方网站相关,在/etc/apt下是否有个apt.conf文件,内容应该与之相关,尝试注释或者删除
2020-06-03----------------------------------------------------------------------------------------------------------------------------------------------------------
左值引用vs右值引用https://blog.csdn.net/xuyuqingfeng953/article/details/51058236
左值引用和直接传递了实参有区别么?
tuple, forward_as_tuplehttps://www.cnblogs.com/LuckCoder/p/8632209.html
2020-04-03------------------------------------------------------------------------------------------------------------------------------------------------------------------
指针作为类的成员变量,我想要改变指针的内容,指针的指针和指针的引用还是有区别的,
2020-3-23-----------------------------------------------------------------------------------------------------------------------------------------------------------如果new了一片内存赋值给指针p,通过delete的方式回收了内存,这时内存回收了,但是指针p依然指向这片未分配的内存区域,成为野指针,结果是不确定的,因而在delete释放内存的同时,需要手动通过p=nullptr将p置空 。同样,定义的时候也需要初始化。
C/C++野指针
空指针与野指针
野指针(概念,产生原因,危害,避免方法)
2020-3-20-----------------------------------------------------------------------------------------------------------------------------------------------------------
class A{
A(input){temp = input;
}
private:
int temp;
}
A a(5);std::shared_ptr<A> p(new A);
std::shared_ptr<A> p = std::make_shared<A>(a);
std::shared_ptr<A> p = std::make_shared<A>(5);
make_shared后面跟A类的一个对象或者构造函数里的参数,都是可以的。
2020-3-17-----------------------------------------------------------------------------------------------------------------------------------------------------------vector可以assign(n, digit);可以resize(大小),改变size,过大capacity会自动扩容; 也可以 reserve主动扩容,改变capacity.
resize更大之后,好像默认赋值0;resize更小之后,size变小了,但是原来的位置可以访问,而且还是原来的值,物理内存上的内容并没有改变.
thread及时回收,防止资源泄露https://www.cnblogs.com/liangjf/p/9801496.html
多线程cmake设置:
find_package (Threads)
add_executable (myapp main.cpp ...)
target_link_libraries (myapp ${CMAKE_THREAD_LIBS_INIT})
互斥锁std::mutex和条件变量std::condition_variable
——> > > std::mutex: std::mutex是C++中最基本的互斥量,提供了独占所有权的特性,std::mutex提供了以下成员函数:
构造函数:std::mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象是处于unlocked状态的。
lock():调用线程将锁住该互斥量,线程调用该函数会发生以下3种情况:
(1)如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁。
(2)如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
(3)如果当前互斥量被当前调用线程锁住,则会产生死锁,,也就是说同一个线程中不允许锁两次。
unlock():解锁,释放对互斥量的所有权。
try_lock():尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞,线程调用该函数会出现下面3种情况:
(1)如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用unlock释放互斥量。
(2)如果当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞掉。
(3)如果当前互斥量被当前调用线程锁住,则会产生死锁。
条件变量std::condition_variable:
条件变量是并发程序设计中的一种控制结构。多个线程访问一个共享资源(或称临界区)时,不但需要用互斥锁实现独享访问以避免并发错误(称为竞争危害),在获得互斥锁进入临界区后还需要检验特定条件是否成立:C++11中引入了条件变量,其相关内容均在<condition_variable>中。这里主要介绍std::condition_variable类。
(1)、如果不满足该条件,拥有互斥锁的线程应该释放该互斥锁,把自身阻塞(block)并挂到(suspend)条件变量的线程队列中
(2)、如果满足该条件,拥有互斥锁的线程在临界区内访问共享资源,在退出临界区时通知(notify)在条件变量的线程队列中处于阻塞状态的线程,被通知的线程必须重新申请对该互斥锁加锁。
条件变量std::condition_variable用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程。std::condition_variable需要与std::unique_lock配合使用。std::condition_variable效果上相当于包装了pthread库中的pthread_cond_*()系列的函数。
当std::condition_variable对象的某个wait函数被调用的时候,它使用std::unique_lock(通过std::mutex)来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的std::condition_variable对象上调用了notification函数来唤醒当前线程。
std::condition_variable对象通常使用std::unique_lock<std::mutex>来等待,如果需要使用另外的lockable类型,可以使用std::condition_variable_any类。
std::condition_variable类的成员函数:
(1)、构造函数:仅支持默认构造函数,拷贝、赋值和移动(move)均是被禁用的。
(2)、wait:当前线程调用wait()后将被阻塞,直到另外某个线程调用notify_*唤醒当前线程;当线程被阻塞时,该函数会自动调用std::mutex的unlock()释放锁,使得其它被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(notify,通常是另外某个线程调用notify_*唤醒了当前线程),wait()函数也是自动调用std::mutex的lock()。
wait分为无条件被阻塞和带条件的被阻塞两种。
——> > > 无条件被阻塞:调用该函数前,当前线程应该已经对unique_lock<mutex> lck完成了加锁。所有使用同一个条件变量的线程必须在wait函数中使用同一个unique_lock<mutex>。该wait函数内部会自动调用lck.unlock()对互斥锁解锁,使得其他被阻塞在互斥锁上的线程恢复执行。使用本函数被阻塞的当前线程在获得通知(notified,通过别的线程调用 notify_*系列的函数)而被唤醒后,wait()函数恢复执行并自动调用lck.lock()对互斥锁加锁。
——> > > 带条件的被阻塞:wait函数设置了谓词(Predicate),只有当pred条件为false时调用该wait函数才会阻塞当前线程,并且在收到其它线程的通知后只有当pred为true时才会被解除阻塞。因此,等效于while (!pred()) wait(lck).
(3)、wait_for:与wait()类似,只是wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_for返回,剩下的步骤和wait类似。
(4)、wait_until:与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其它线程的通知,wait_until返回,剩下的处理步骤和wait类似。
(5)、notify_all: 唤醒所有的wait线程,如果当前没有等待线程,则该函数什么也不做。
(6)、notify_one:唤醒某个wait线程,如果当前没有等待线程,则该函数什么也不做;如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。
条件变化存在虚假唤醒的情况,因此在线程被唤醒后需要检查条件是否满足。无论是notify_one或notify_all都是类似于发出脉冲信号,如果对wait的调用发生在notify之后是不会被唤醒的,所以接收者在使用wait等待之前也需要检查条件是否满足。
std::condition_variable_any类与std::condition_variable用法一样,区别仅在于std::condition_variable_any的wait函数可以接受任何lockable参数,而std::condition_variable只能接受std::unique_lock<std::mutex>类型的参数。
std::notify_all_at_thread_exit函数:当调用该函数的线程退出时,所有在cond条件变量上等待的线程都会收到通知。
参考:
https://www.cnblogs.com/depend-wind/articles/10108048.html
https://www.cnblogs.com/zhanghu52030/p/9166737.html
2020-3-11------------------------------------------------------------------------------------------------------------------------------------------------------------
基类A,包含a, b两个方法,其中a调用了b,且b是virtual;
派生类B,重写了b,还新写了个c函数;
基类指针p指向B的对象,不能调用c,因为他也不知道派生类会有这么个函数;
p调用函数a的时候,a又调用了函数b,因为是virtual的且B中也重写了,因此会调用B中的b。
2020-3-6---------------------------------------------------------------------------------------------------------------------------------------------------------------------
std::atan(y/x)std::atan2(y, x)
atan的情况下,要保证x不会等于0;atan2允许x等于0,不需要特别考虑。做除法就要考虑分布会不会存在等于0的风险。
不要用abs(), fabs()这种c的库,统一用std::abs()
2020-3-5--------------------------------------------------------------------------------------------------------------------------------------------------------------------
enum 与enum class:
1.3 问题3:enum的作用域
enum的中的 ” { } ” 大括号并没有将枚举成员的可见域限制在大括号内,导致enum成员曝露到了上一级作用域(块语句)中。
例如:
#include <iostream>
enum color{red,blue};//定义拥有两个成员的enum,red和blue在enum的大括号外部可以直接访问,而不需要使用域运算符。
int main() {std::cout << blue << std::endl;std::cin.get();return 0;
}
就如上面的代码,我们可以在blue的大括号之外访问它,color的成员被泄露到了该文件的全局作用域中(虽然它尚不具备外部链接性)。可以直接访问,而不需要域运算符的帮助。
但是这不是关键,有时我们反而觉得非常方便。下面才是问题所在:
问题:无法定义同名的枚举成员
enum color { red, blue };
//enum MyEnum { red, yellow }; ERROR, 重定义;以前的定义是“枚举数”
如上面的代码所示:我们无法重复使用red这个标识符。因为它在color中已经被用过了。但是,它们明明就是不同的枚举类型,如果可以使用相同的成员名称,然后通过域运算符来访问的话,该有多好!就像下面这样:
color::red
但是这是旧版的enum无法做到的。
引入了域,要通过域运算符访问,不可以直接通过枚举体成员名来访问(所以我们可以定义相同的枚举体成员而不会发生重定义的错误)
#include <iostream>enum class color { red, green, yellow};
enum class colorX { red, green, yellow };int main() {//使用域运算符访问枚举体成员,强转后打印std::cout << static_cast<int>(color::red) << std::endl;std::cout << static_cast<int>(colorX::red) << std::endl;std::cin.get();return 0;
}
参考:https://blog.csdn.net/sanoseiichirou/article/details/50180533?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
尽量用using而不是typedef
//typedef std::shared_ptr<FeatureInfo> FeatureInfo_Ptr;using FeatureInfo_Ptr = std::shared_ptr<FeatureInfo>;
push_back(), 已知大小的情况下先reserve(),不需要随机访问的情况系下用list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
memset
memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,
c:是赋给buffer的值,
count:是buffer的长度.
int abs(int i); // 处理int类型的取绝对值
double fabs(double i); //处理double类型的取绝对值
float fabsf(float i); /处理float类型的取绝对值
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1、vector<Eigen::Matrix4d,Eigen::aligned_allocator<Eigen::Matrix4d>>
2、bitset
3、int*, static_cast<int*>, reinterpret_cast<int*>不一样;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
定义为虚函数,继承之后存在同名函数,但是行为不同,可以使用基类指针或者引用指向;
BCD都继承自A,可以使用A指针指向,从而做到用数组“保存”不同类型的BCD,不管是什么类型,如果他们都存在与A的同名虚函数,那么调用的就是BCD各自的方法,如果没有定义为虚函数,那么在任何情况下调用的都是基类的方法;
如果不需要使用基类指针指向不同class,并调用同名函数,也没必须定义虚函数,反正也不影响调用父类的函数功能
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------chrono计时:
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();LandmarkFeatureExtracter::extract(scan, features);GeometryFeatureExtracter::extract(scan, features);std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();std::chrono::steady_clock::duration used_time = end-start;std::cout << "used time: " << std::chrono::duration_cast<std::chrono::milliseconds>(used_time).count() << std::endl;
https://www.cnblogs.com/bianchengnan/p/9478638.html
机器人领域C++/ROS/TCP编程相关推荐
- 【机器人操作系统】ROS话题编程
文章目录 自定义消息的实现过程及说明 一.功能包的创建 二.自定义话题消息 (1)创建自定义消息文件步骤 (2)添加编译选项步骤 三.创建发布者 四.创建订阅者 五.添加编译选项 六.编译运行 (1) ...
- 干货:机器人开源操作系统ROS
导读:前不久,John 为大家介绍过物联网开源操作系统,或许大家还记忆犹新.今天,要介绍的是一款机器人领域的开源操作系统:ROS. 简介 ROS,英文全称 Robot Operating System ...
- socket中的TCP编程(调用免费聊天的机器人实现自动回复)
socket中的TCP编程(调用免费聊天的机器人实现自动回复) 在无聊时间复习了socket编程,写了这个简易代码!!注意要在联网状态下跑 1:服务器模块: import socket import ...
- python机器人编程教程入门_机器人操作系统(ROS)入门必备:机器人编程一学就会...
原标题:机器人操作系统(ROS)入门必备:机器人编程一学就会 ROS经过十几年的发展,已经得到了极大的推广和应用,尤其是在学术界.卡耐基梅隆大学机器人研究所的大部分实验室都是基于ROS编程的,现在所在 ...
- 机器人开发--Apollo ROS介绍
机器人开发--Apollo ROS介绍 1 介绍 1.1 概述 1.2 历程 2 ROS的不足 大数据传输性能瓶颈 单中心的网络存在单点风险(ROS2为分布式避免该问题) 数据格式缺乏向后兼容 3 A ...
- 世界机器人领域12个前沿技术趋势
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自 | 新机器视觉 MIT机器人实验室主任.IEEE.AAAI ...
- Java刷题知识点之TCP、UDP、TCP和UDP的区别、socket、TCP编程的客户端一般步骤、TCP编程的服务器端一般步骤、UDP编程的客户端一般步骤、UDP编程的服务器端一般步骤...
TCP和UDP是两个传输层协议,广泛应用于网络中不同主机之间传输数据.对任何程序员来说,熟悉TCP和UDP的工作方式都是至关重要的.这就是为什么TCP和UDP是一个流行的Java编程面试问题. Jav ...
- 客户案例|围观!卡耐基梅隆大学用上中国造?要玩转自主导航机器人领域?
近日,卡耐基梅隆大学 Safe AI Lab (人工智能安全实验室) 赵鼎教授团队分享了其在自主导航机器人领域的最新研究方向,该项目以开源自主连接和自动化研究车辆平台(OpenCAV Platform ...
- 桌面、平板、手机和机器人操作系统(ROS)市场占有率数据和趋势分享(附引用2019版)
机器人操作系统(ROS)国内用户排名第一!!! 排名第一 桌面和平板操作系统市场基本还是Windows垄断的天下(Desktop.tablet和console). computer 详细数据如下: 1 ...
最新文章
- shadow fight 1.6.0 内购
- iOS WKWebView ios9以上版本配置 与 设置UserAgent(用户代理), 解决点击web, 客户端接收不到web事件问题...
- VIJOS-P1192
- vscode如何设置自动保存时自动格式化代码
- MYSQL DELETE 别名
- golang后端php前端,Golang如何接收前端的参数
- C 主导、C++与 C# 为辅,揭秘 Windows 10 源代码!
- 【十五分钟Talkshow】如何理解并优化.NET应用程序对内存的使用
- MPCCI3.0.5\
- mysql合并两个表_MYSQL如何合并两个表
- 计算机经常自动关机怎么回事,教你电脑总是自动关机怎么办
- h5php大转盘抽奖,Vue.js实现大转盘抽奖总结及实现思路
- tkinter将图标写入py文件
- 巧用 Linux 定时任务
- seo是什么,干什么用,有什么作用(二)
- CentOS 7 系统部署之四:磁盘扩容与调整
- 【转】4G手机打电话为什么会断网 4G上网和通话不能并存原因分析
- GitLab将技术许可极狐公司-JH发行版来了
- 用Vue+Node从零开始实现拼多多前后端商城项目 — 记录踩坑之旅(上篇)
- 您的计算机无法启动f2,华硕笔记本电脑 开机按f2无法进入BIOS 无法进入安全模式 键盘键位失灵 仅开机键可用...
热门文章
- 996工作制职场人身体还能撑多久?
- Android8.1 添加修改默认壁纸
- 雷军卸任小米有品公司董事,仍持股70%;马斯克决定不加入推特董事会;一加10 Pro内核源代码公布|极客头条
- 通过 Colab 下载 Google Driver 上的大文件到内网服务器
- Prerender预渲染优化SEO
- 阿里云emas远程真机使用指南
- 怎么用算法把黄豆、绿豆以及红豆给区分出来,写出 代码。
- android 网络mp3格式,Android-使用MediaPlayer播放网络音频并且缓存
- java基本微信小程序的汉服租赁平台 uniapp 小程序
- CDCE913产生任意频率