PX4飞控试炼之路

2023-05-16

PX4飞控试炼之路

  • 一、地面站控制台上显示输出“Hello Sky!”
  • 二、利用飞控计算加法
  • 三、飞控按指定频率计数,并将计数结果反馈到控制台
    • 1. 利用while循环实现计数
    • 2. 利用`hrt_call_every`周期回调函数实现
    • 3. 飞控创建新线程
      • (1)task控制接口
      • (2)phreads方式创建线程
  • 四、uORB通信发布和订阅数据
    • 1.模块订阅数据
    • 2.模块发布数据

一、地面站控制台上显示输出“Hello Sky!”

  1. 找到目录:Firmware/src/examples/, 并新建一个子目录,可以命为:my_example_app;
  2. 在新建的目录下,新建一个 c文件,可以命名为:my_example_app.c;
  3. 打开新建的 c文件,并填入以下代码:
#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
__EXPORT int my_example_app_main(int argc, char *argv[]);
int my_example_app_main(int argc, char *argv[])
{
PX4_INFO("Hello Sky!");
return 0;
}

my_example_app是飞控运行过程中的一个指令,可在地面站端口输入,调用该条指令时,打印信息Hello Sky。需要注意的是主函数命名方式为 C文件的名称 + _main, 这样在控制台输入命令时,就可以从该入口进入函数;

  1. 仍在该目录下,新建一个 txt文件,名为:CMakeLists.txt,该文件用于编译,必不可少,里面写入以下代码:
px4_add_module(
	MODULE examples__my_example_app
	MAIN my_example_app
	STACK_MAIN 2000
	SRCS
		my_example_app.c
	DEPENDS
	)
  1. 如果想从仿真中查看的话,需要进入文件夹:Firmware/boards/px4/sitl/, 并打开文件:default.cmake,如果想从飞控中查看,需要进入文件夹:Firmware/boards/px4/fmu-v2/并打开文件:default.cmake, 在EXAMPLES类中添加my_example_app
    在这里插入图片描述该cmake文件中的命令,如果在使用过程中不需要可以注释掉,这样编译过程会更快;
    然后进入Firmware目录下的终端,输入指令:make px4_fmu-v2_default编译固件,输入指令:make px4_sitl_default gazebo打开gazebo仿真:
    在这里插入图片描述

接着在该终端中输入my_example_app,终端输出“Hello Sky!”
在这里插入图片描述
gazebo仿真界面如下图,中心位置出现一辆无人机。
在这里插入图片描述

二、利用飞控计算加法

  1. PX4中的任何一个功能模块或者说一个进程基本模式如下图所示:
    在这里插入图片描述要想用飞控实现两个数字的加法,需在控制台输入两个数字a和b,然后经飞控计算后,再返回控制台。按照上图的进程模式指令应包括:进程启动、进程停止、进程状态、进程帮助以及用户操作指令。
    用户的操作指令格式为:my_example_app test -a num1 -b num2,其中my_example_app 是指令的名称, -a num1 -b num2均是指令参数。例如,一个正常指令为:
my_example_app test -a 100 -b 2
  1. 考虑到源码主要使用C++进行变成,这里我们将.c文件改为.cpp文件,首先创建一个类,里面包含需要实现各个指令的公有公式和用于运算与状态标记的变量。
    对参数的提取主要利用了头文件#include <px4_getopt.h>中的int PX4_getopt(int argc, char *argv[], const char *options, int *myoptind, const char **myoptarg)函数。完整代码如下:
#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
#include <px4_defines.h>//含有OK等定义
#include <px4_getopt.h>//用于解析指令
#include <stdlib.h>//基本函数库,如字符串转浮点数据的函数: atof
#include <string.h>//用于字符串处理

extern "C" __EXPORT int my_example_app_main(int argc, char *argv[]);

class AddtionCall
{
public:
	/**
	* Constructor
	*/
	AddtionCall();//构造函数

	/**
	* Destructor, also kills task.
	*/
	~AddtionCall();//析构函数

	int start();
	int stop();
	void status();
	void help();
	void my_example_app(int argc, char *argv[]);

private:
	float _num1 ;//加数
	float _num2;//被加数
	float _result ;//结果
	bool _status; //状态标志
};
//构造函数
AddtionCall::AddtionCall():

_num1(0.0f),
_num2(0.0f),
_result(0.0f),
_status(false)
{
	;
}
//析构函数
AddtionCall::~AddtionCall()
{
_num1= 0.0f;
_num2= 0.0f;
_result = 0.0f;
_status=false;
}
//开始函数
int AddtionCall::start()
{
	if (_status)
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app");
	}
	else
	{
		_status=true;
		PX4_INFO("Task start successful!");
	}
	return 1;
}
//终止函数
int AddtionCall::stop()
{
	_status=false;
	PX4_INFO("Task has been stopped!");
	return 1;
}
//状态函数
void AddtionCall::status()
{
	if (!_status)
	{
		PX4_INFO("Task is is not running, please start it first ");
	}
	else
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app!");
	}
}
//帮助函数
void AddtionCall::help()
{
	PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
	PX4_INFO(" my_example_app start //---- start the module");
	PX4_INFO(" my_example_app stop //---- stop the module");
	PX4_INFO(" my_example_app status //---- show the current state of the module");
	PX4_INFO("my_example_app help //---- show what you can do in this module");
	PX4_INFO("my_example_app test -a num1 -b num2 //----caculate your data, There is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation.");
}
//运算函数
void AddtionCall::my_example_app(int argc, char *argv[])
{
	bool error_flag=false;
	int myoptind = 1;
	int ch;
	const char *myoptarg = nullptr;

	while ((ch = px4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF)
	{
		switch (ch) {
		case 'a':
			_num1= atof(myoptarg);
			break;
		case 'b':
			_num2 = atof(myoptarg);
			break;
		default:
			error_flag = true;
			break;
					}
	}
	if (error_flag)
	{
		PX4_WARN("unrecognized flag");
	}
	else
	{
		if (_num1<=10000&&_num1>=-10000&&_num2<=10000&&_num2>=-10000)
			{
				_result=_num1+_num2;
				PX4_INFO("The result is: %f",(double)_result);
			}
		else
			{
				PX4_WARN("Your data is beyond the limitation!");
			}
	}
}
//入口函数
int my_example_app_main(int argc, char *argv[])
{
	//PX4_INFO("Hello Sky!");
	//static bool module_start=false;
	static AddtionCall add_num;
	if (argc < 2)
		{
			PX4_WARN( "AddtionCall: my_example_app {start|stop|status|help|test -a num1 -b num2}");
		}
	if (!strcmp(argv[1], "start")&&argc==2)
		{
			//if(!module_start)
			{
				add_num.start();
			}
		_exit(0);
		}
	else if (!strcmp(argv[1], "stop")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.stop();
			// delete add_num;
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "status")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.status();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "help")&&argc==2)
		{
		//if (add_num!=NULL)
		{
			add_num.help();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "test")&&argc>2)
	{
		//if (add_num!=NULL)
		{
			add_num.my_example_app(argc, argv);
		}
		_exit(0);
	}
	else
	{
		PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
		PX4_INFO(" my_example_app start //---- start the module");
		PX4_INFO(" my_example_app stop //---- stop the module");
		PX4_INFO(" my_example_app status //---- show the current state of the module");
		PX4_INFO("my_example_app help //---- show what you can do in this module");
		PX4_INFO("my_example_app test -a num1 -b num2 //----caculate your data, there is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation. ");
	}
	return OK;
}

编译固件,打开终端输入:make px4_sitl_default gazebo,打开gazebo仿真,在终端继续输入my_example_app test -a 5 -b 5,计算得到两个数的加法。
也可以通过数据线连接飞控板,打开QGC地面站,在MAVLink Console命令端口输入my_example_app test -a 5 -b 5,也可得到返回的计算结果。
在这里插入图片描述

三、飞控按指定频率计数,并将计数结果反馈到控制台

1. 利用while循环实现计数

在类中加入void run()函数专门处理计数任务,代码如下:

void AddtionCall::run()
{
	hrt_abstime time_start = hrt_absolute_time();//取出系统当前时刻,时刻函数hrt_absolute_time()位于头文件#include <drivers/drv_hrt.h>中
	float time_count=0;
	while (_status)
		{
			usleep(WAITING_1S);//宏定义WAITING_1S=1000000,usleep(WAITING_1S)表示休眠1000000us也就是休眠1s
			time_count=(float)hrt_elapsed_time(&time_start)/WAITING_1S;//计算当前时刻距离time_start时刻的时间间隔
			PX4_INFO("count from start:%3.6f",(double)time_count);
		}
}

start()函数中调用run()函数。
在原代码中修改得到的完整程序为:

#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
#include <px4_defines.h>//含有OK等定义
#include <px4_getopt.h>//用于解析指令
#include <stdlib.h>//基本函数库,如字符串转浮点数据的函数: atof
#include <string.h>//用于字符串处理
#include <drivers/drv_hrt.h>

extern "C" __EXPORT int my_example_app_main(int argc, char *argv[]);

#define WAITING_1S 1000000
class AddtionCall
{
public:
	/**
	* Constructor
	*/
	AddtionCall();//构造函数

	/**
	* Destructor, also kills task.
	*/
	~AddtionCall();//析构函数

	int start();
	int stop();
	void status();
	void help();
	void my_example_app(int argc, char *argv[]);
	void run();


private:
	float _num1 ;//加数
	float _num2;//被加数
	float _result ;//结果
	bool _status; //状态标志
	hrt_abstime _time_start;
};
//构造函数
AddtionCall::AddtionCall():

_num1(0.0f),
_num2(0.0f),
_result(0.0f),
_status(false)
{
	;
}
//析构函数
AddtionCall::~AddtionCall()
{
_num1= 0.0f;
_num2= 0.0f;
_result = 0.0f;
_status=false;
}

void AddtionCall::run()

{
	_time_start = hrt_absolute_time();
	PX4_INFO("Time _start is:%3.6f",(double)_time_start);
	float time_count=0;
	while (_status)
		{
			usleep(WAITING_1S);//宏定义WAITING_1S=1000000,usleep(WAITING_1S)表示休眠1000000us也就是休眠1s
			time_count=(float)hrt_elapsed_time(&_time_start)/WAITING_1S;//计算当前时刻距离time_start时刻的时间间隔
			PX4_INFO("count from start:%3.6f",(double)time_count);
		}


}

//开始函数
int AddtionCall::start()
{

	if (_status)
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app");
	}
	else
	{
		_status=true;
		PX4_INFO("Task start successful!");
	}
	run();
	return 1;

}
//终止函数
int AddtionCall::stop()
{
	_status=false;
	PX4_INFO("Task has been stopped!");
	return 1;
}
//状态函数
void AddtionCall::status()
{
	if (!_status)
	{
		PX4_INFO("Task is is not running, please start it first ");
	}
	else
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app!");
	}
}
//帮助函数
void AddtionCall::help()
{
	PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
	PX4_INFO(" my_example_app start //---- start the module");
	PX4_INFO(" my_example_app stop //---- stop the module");
	PX4_INFO(" my_example_app status //---- show the current state of the module");
	PX4_INFO("my_example_app help //---- show what you can do in this module");
	PX4_INFO("my_example_app test -a num1 -b num2 //----caculate your data, There is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation.");
}
//运算函数
void AddtionCall::my_example_app(int argc, char *argv[])
{
	bool error_flag=false;
	int myoptind = 1;
	int ch;
	const char *myoptarg = nullptr;

	while ((ch = px4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF)
	{
		switch (ch) {
		case 'a':
			_num1= atof(myoptarg);
			break;
		case 'b':
			_num2 = atof(myoptarg);
			break;
		default:
			error_flag = true;
			break;
					}
	}
	if (error_flag)
	{
		PX4_WARN("unrecognized flag");
	}
	else
	{
		if (_num1<=10000&&_num1>=-10000&&_num2<=10000&&_num2>=-10000)
			{
				_result=_num1+_num2;
				PX4_INFO("The result is: %f",(double)_result);
			}
		else
			{
				PX4_WARN("Your data is beyond the limitation!");
			}
	}
}
//入口函数
int my_example_app_main(int argc, char *argv[])
{
	//PX4_INFO("Hello Sky!");
	//static bool module_start=false;
	static AddtionCall add_num;
	if (argc < 2)
		{
			PX4_WARN( "AddtionCall: my_example_app {start|stop|status|help|test -a num1 -b num2}");
		}
	if (!strcmp(argv[1], "start")&&argc==2)
		{
			//if(!module_start)
			{
				add_num.start();
			}
		_exit(0);
		}
	else if (!strcmp(argv[1], "stop")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.stop();
			// delete add_num;
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "status")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.status();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "help")&&argc==2)
		{
		//if (add_num!=NULL)
		{
			add_num.help();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "test")&&argc>2)
	{
		//if (add_num!=NULL)
		{
			add_num.my_example_app(argc, argv);
		}
		_exit(0);
	}
	else
	{
		PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
		PX4_INFO(" my_example_app start //---- start the module");
		PX4_INFO(" my_example_app stop //---- stop the module");
		PX4_INFO(" my_example_app status //---- show the current state of the module");
		PX4_INFO("my_example_app help //---- show what you can do in this module");
		PX4_INFO("my_example_app test -a num1 -b num2 //----caculate your data, there is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation. ");
	}
	return OK;
}

上传固件到飞控后,在地面站控制台输入my_example_app start,飞控开始计数:
在这里插入图片描述

2. 利用hrt_call_every周期回调函数实现

该函数为:void hrt_call_every(struct hrt_call *entry, hrt_abstime delay, hrt_abstime interval, hrt_callout callout, void *arg)
位于头文件 #include <drivers/drv_hrt.h>中。其作用是以周期 interval (us) 循环调用函数 callout. 调用时间最多不超过 delay(单位 us,delay 要略大于interval), 且函数callout调用期间禁止中断。传感器 mpu6000的驱动中就使用了该函数,如下:

hrt_call_every(&_call,
				1000,
				_call_interval - MPU6000_TIMER_REDUCTION,
				(hrt_callout)&MPU6000::measure_trampoline, this);

仍旧在my_example_app原代码中进行修改,得到该部分完整代码。

3. 飞控创建新线程

多线程所需要使用的函数在Nuttx中,相关函数的具体说明可参考Nuttx系统Porting Guide;
Nuttx可以运行两种线程:
tasks: 各线程相互独立,每创建一个tasks都会产生一组新的描述符包括输入、输出和错误,子进程可以自由使用自己的描述符而不影响父进程。
pthreads: 与父进程共享相同的描述符,子进程的操作将影响到父进程和父进程包含的所有子进程,互相共享资源。

(1)task控制接口

task_create函数:

task_create(char *name, int priority, int stack_size, main_t entry, char *
const argv[]);

--------------函数说明------------------
name:/任务名称
priority:/任务优先级,有:SCHED_PRIORITY_MAX/SCHED_PRIORITY_MAX/
SCHED_PRIORITY_MIN/SCHED_PRIORITY_IDLE等,具体参考头文件platforms/nuttx/NuttX/nuttx/include/sys/types.h
stack_size:/栈大小,一般取2048
entry:/线程主函数入口
argv[]:/主函数入口参数

通过创建task_create函数来解决上面无法在控制台打印信息的问题,主要过程如下:
hrt_call_every函数定时精度较高,我们使用该函数计时,每隔1s,给相应的计时标志位;
利用task_creat创建的主线程执行while循环任务,检测到计时标志位时(可以理解为中断),调用打印函数将计数信息打印到控制台。

在类中定义新的打印信息函数和用于标志计数的变量,该变量定义为了静态变量,不能在构造函数内部初始化,只能在外面初始化赋值。

int AddtionCall::run_info_print(int argc,char *argv[])
{
	while(_status)
	{
		if(!_tick_arrived)
		{
			_tick_arrived = true ;
					PX4_INFO("count from start: %3.6f",(double)_time_count);
			usleep(WAITING_1S-50);
		}
	}
	return OK;
}
//新布尔变量类内定义
...
static bool _tick_arrived;//注意是静态变量
...
//类外初始化
bool AddtionCall::_tick_arrived=false;

  1. start()函数修改:
//开始函数
int AddtionCall::start()
{

	if (_status)
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app");
		return 0;
	}

	else
	{
		_status=true;
		PX4_INFO("Task start successful!");
	}
	pid_t task_pid=task_create("addtion_call",
						SCHED_PRIORITY_DEFAULT,
						2048,
						run_info_print,
						NULL);
	if(task_pid<0)
	{
		int errcode =errno;
		PX4_INFO("ERROR:Failed to start addtion_call: \n");
		return -errcode;
	}
	hrt_cancel(&_call);
	_call_interval=WAITING_1S;
	_time_start=hrt_absolute_time();

	hrt_call_every(&_call,WAITING_1S+200,WAITING_1S,(hrt_callout)&AddtionCall::run_trampoline, this);

	return 1;

}

完整程序如下:

#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
#include <px4_defines.h>//含有OK等定义
#include <px4_getopt.h>//用于解析指令
#include <px4_log.h>
#include <stdlib.h>//基本函数库,如字符串转浮点数据的函数: atof
#include <string.h>//用于字符串处理
#include <drivers/drv_hrt.h>
#include  <sched.h>
#include <errno.h>

extern "C" __EXPORT int my_example_app_main(int argc, char *argv[]);

#define WAITING_1S 1000000
#define RUN_TIMER_REDUCTION 200000
class AddtionCall
{
public:
	/**
	* Constructor
	*/
	AddtionCall();//构造函数

	/**
	* Destructor, also kills task.
	*/
	~AddtionCall();//析构函数

	int start();
	int stop();
	void status();
	void help();
	void my_example_app(int argc, char *argv[]);
	void run();
	void calculate(int argc,char *argv[]);
	static void  run_trampoline(void *arg);
	static int run_info_print(int argc,char *argv[]);


private:
	float _num1 ;//加数
	float _num2;//被加数
	float _result ;//结果
	static bool _status; //状态标志

	struct hrt_call  _call;
	unsigned   _call_interval;
	hrt_abstime _time_start;
	static float _time_count;
	static bool _tick_arrived;

};

bool AddtionCall::_status=false;
float AddtionCall::_time_count=0;
bool AddtionCall::_tick_arrived= false;

//构造函数
AddtionCall::AddtionCall():

_num1(0.0f),
_num2(0.0f),
_result(0.0f),
//_status(false)
_call{},
_call_interval(1000),
_time_start(0)
{
	memset(&_call,0,sizeof(_call));
}
//析构函数
AddtionCall::~AddtionCall()
{
_num1= 0.0f;
_num2= 0.0f;
_result = 0.0f;
_status=false;
}

void AddtionCall::run()

{
	_time_count=(float)hrt_elapsed_time(&_time_start)/WAITING_1S;
	_tick_arrived=false;
}
void AddtionCall::run_trampoline(void *arg)
{
	AddtionCall *dev=(AddtionCall*)arg;
	dev->run();
}
int AddtionCall::run_info_print(int argc,char *argv[])
{
	while(_status)
	{
		if(!_tick_arrived)
		{
			_tick_arrived = true ;
					PX4_INFO("count from start: %3.6f",(double)_time_count);
			usleep(WAITING_1S-50);
		}
	}
	return OK;
}
//开始函数
int AddtionCall::start()
{

	if (_status)
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app");
		return 0;
	}

	else
	{
		_status=true;
		PX4_INFO("Task start successful!");
	}
	pid_t task_pid=task_create("addtion_call",
						SCHED_PRIORITY_DEFAULT,
						2048,
						run_info_print,
						NULL);
	if(task_pid<0)
	{
		int errcode =errno;
		PX4_INFO("ERROR:Failed to start addtion_call: \n");
		return -errcode;
	}
	hrt_cancel(&_call);
	_call_interval=WAITING_1S;
	_time_start=hrt_absolute_time();

	hrt_call_every(&_call,WAITING_1S+200,WAITING_1S,(hrt_callout)&AddtionCall::run_trampoline, this);

	return 1;

}
//终止函数
int AddtionCall::stop()
{
	_status=false;
	hrt_cancel(&_call);
	PX4_INFO("Task has been stopped!");
	return 1;
}
//状态函数
void AddtionCall::status()
{
	if (!_status)
	{
		PX4_INFO("Task is is not running, please start it first ");
	}
	else
	{
		PX4_INFO("count from start: %3.6f",(double)_time_count);
		PX4_INFO("Task is already running, now is waiting for data to calculate!");
	}
}
//帮助函数
void AddtionCall::help()
{
	PX4_INFO("This is calculate module, using for addition opreation. You can use the following command:");
	PX4_INFO(" calculate start //---- start the module");
	PX4_INFO(" calculate stop //---- stop the module");
	PX4_INFO(" calculate status //---- show the current state of the module");
	PX4_INFO("calculate help //---- show what you can do in this module");
	PX4_INFO("calculate test -a num1 -b num2 //----caculate your data, There is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation.");
}
//运算函数
void AddtionCall::calculate(int argc, char *argv[])
{
	bool error_flag=false;
	int myoptind = 1;
	int ch;
	const char *myoptarg = nullptr;

	while ((ch = px4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF)
	{
		switch (ch) {
		case 'a':
			_num1= atof(myoptarg);
			break;
		case 'b':
			_num2 = atof(myoptarg);
			break;
		default:
			error_flag = true;
			break;
					}
	}
	if (error_flag)
	{
		PX4_WARN("unrecognized flag");
	}
	else
	{
		if (_num1<=10000&&_num1>=-10000&&_num2<=10000&&_num2>=-10000)
			{
				_result=_num1+_num2;
				PX4_INFO("The result is: %f",(double)_result);
			}
		else
			{
				PX4_WARN("Your data is beyond the limitation!");
			}
	}
}
//入口函数
int my_example_app_main(int argc, char *argv[])
{
	//PX4_INFO("Hello Sky!");
	//static bool module_start=false;
	static AddtionCall add_num;
	if (argc < 2)
		{
			PX4_WARN( "AddtionCall: calculate {start|stop|status|help|test -a num1 -b num2}");
		}
	if (!strcmp(argv[1], "start")&&argc==2)
		{
			//if(!module_start)
			{
				add_num.start();
			}
		_exit(0);
		}
	else if (!strcmp(argv[1], "stop")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.stop();
			// delete add_num;
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "status")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.status();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "help")&&argc==2)
		{
		//if (add_num!=NULL)
		{
			add_num.help();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "test")&&argc>2)
	{
		//if (add_num!=NULL)
		{
			add_num.calculate(argc, argv);
		}
		_exit(0);
	}
	else
	{
		PX4_INFO("This is calculate module, using for addition opreation. You can use the following command:");
		PX4_INFO(" calculate start //---- start the module");
		PX4_INFO(" calculate stop //---- stop the module");
		PX4_INFO(" calculate status //---- show the current state of the module");
		PX4_INFO("calculate help //---- show what you can do in this module");
		PX4_INFO("calculate test -a num1 -b num2 //----caculate your data, there is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation. ");
	}
	return OK;
}

运行结果如下,计数可通过my_example_app stop命令结束:
在这里插入图片描述

(2)phreads方式创建线程

该方式以pthread.h文件和线程函数库实现,主要函数有:
线程操纵函数:pthread_create()创建一个线程,pthread_exit()终止当前线程等;
线程属性函数:pthread_attr_init()初始化线程属性变量等;
在tasks线程基础上修改代码,完整程序如下:

#include <px4_posix.h>//包含了打印信息函数:PX4_INFO
#include <px4_defines.h>//含有OK等定义
#include <px4_getopt.h>//用于解析指令
#include <px4_log.h>
#include <stdlib.h>//基本函数库,如字符串转浮点数据的函数: atof
#include <string.h>//用于字符串处理
#include <drivers/drv_hrt.h>
#include  <sched.h>
#include <errno.h>
#include <pthread.h>

extern "C" __EXPORT int my_example_app_main(int argc, char *argv[]);

#define WAITING_1S 1000000
#define RUN_TIMER_REDUCTION 200000
class AddtionCall
{
public:
	/**
	* Constructor
	*/
	AddtionCall();//构造函数

	/**
	* Destructor, also kills task.
	*/
	~AddtionCall();//析构函数

	int start();
	int stop();
	void status();
	void help();
	void my_example_app(int argc, char *argv[]);
	void run();
	void calculate(int argc,char *argv[]);
	static void  run_trampoline(void *arg);
	static int run_info_print(int argc,char *argv[]);
	static void *run_phreads(void *context);
	static void phreads_define();


private:
	float _num1 ;//加数
	float _num2;//被加数
	float _result ;//结果
	static bool _status; //状态标志

	struct hrt_call  _call;
	unsigned   _call_interval;
	hrt_abstime _time_start;
	static float _time_count;
	static bool _tick_arrived;
	static pthread_t _thread;//线程ID
	static pthread_mutex_t _value_mutex;//互斥锁

};

bool AddtionCall::_status=false;
float AddtionCall::_time_count=0;
bool AddtionCall::_tick_arrived= false;
pthread_t  AddtionCall::_thread = {};
pthread_mutex_t  AddtionCall::_value_mutex={};

//构造函数
AddtionCall::AddtionCall():

_num1(0.0f),
_num2(0.0f),
_result(0.0f),
//_status(false)
_call{},
_call_interval(1000),
_time_start(0)
{
	memset(&_call,0,sizeof(_call));
}
//析构函数
AddtionCall::~AddtionCall()
{
_num1= 0.0f;
_num2= 0.0f;
_result = 0.0f;
_status=false;
}

void AddtionCall::run()

{
	_time_count=(float)hrt_elapsed_time(&_time_start)/WAITING_1S;
	_tick_arrived=false;
}
void AddtionCall::run_trampoline(void *arg)
{
	AddtionCall *dev=(AddtionCall*)arg;
	dev->run();
}
int AddtionCall::run_info_print(int argc,char *argv[])
{
	while(_status)
	{
		if(!_tick_arrived)
		{
			_tick_arrived = true ;
					PX4_INFO("count from start1: %3.6f",(double)_time_count);
			usleep(WAITING_1S-50);
		}
	}
	return OK;
}
void  *AddtionCall::run_phreads(void *context)
{
	while(_status)
	{
		if(!_tick_arrived)
		{
			pthread_mutex_lock(&_value_mutex);

			pthread_mutex_unlock(&_value_mutex);
					PX4_INFO("count from start2: %3.6f",(double)_time_count);
			usleep(WAITING_1S-50);
		}
	}
	return OK;
}
void AddtionCall::phreads_define()
{
	//属性设置
	pthread_attr_t  runloop_attr;
	pthread_attr_init(&runloop_attr);

	struct sched_param param;
	(void)pthread_attr_getschedparam(&runloop_attr,&param);
	param.sched_priority=SCHED_PRIORITY_MAX -100;
	(void)pthread_attr_setschedparam(&runloop_attr,&param);

	pthread_attr_setstacksize(&runloop_attr,PX4_STACK_ADJUSTED(2840));
	//创建线程
	pthread_create(&_thread, &runloop_attr, AddtionCall::run_phreads, NULL);

}




//开始函数
int AddtionCall::start()
{

	if (_status)
	{
		PX4_INFO("Task is already running, now is waiting for data to my_example_app");
		return 0;
	}

	else
	{
		_status=true;
		PX4_INFO("Task start successful!");
	}
	pid_t task_pid=task_create("addtion_call",
						SCHED_PRIORITY_DEFAULT,
						2048,
						run_info_print,
						NULL);
	if(task_pid<0)
	{
		int errcode =errno;
		PX4_INFO("ERROR:Failed to start addtion_call: \n");
		return -errcode;
	}
	hrt_cancel(&_call);
	_call_interval=WAITING_1S;
	_time_start=hrt_absolute_time();

	hrt_call_every(&_call,WAITING_1S+200,WAITING_1S,(hrt_callout)&AddtionCall::run_trampoline, this);

	/* initialize send mutex */
	pthread_mutex_init(&AddtionCall::_value_mutex, nullptr);
	phreads_define();
	return 1;

}
//终止函数
int AddtionCall::stop()
{
	_status=false;
	hrt_cancel(&_call);
	pthread_join(_thread, nullptr);
	PX4_INFO("Task has been stopped!");
	return 1;
}
//状态函数
void AddtionCall::status()
{
	if (!_status)
	{
		PX4_INFO("Task is is not running, please start it first ");
	}
	else
	{
		PX4_INFO("count from start3: %3.6f",(double)_time_count);
		PX4_INFO("Task is already running, now is waiting for data to my_example_app!");
	}
}
//帮助函数
void AddtionCall::help()
{
	PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
	PX4_INFO(" my_example_app start //---- start the module");
	PX4_INFO(" my_example_app stop //---- stop the module");
	PX4_INFO(" my_example_app status //---- show the current state of the module");
	PX4_INFO("my_example_app help //---- show what you can do in this module");
	PX4_INFO("my_example_appte test -a num1 -b num2 //----caculate your data, There is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation.");
}
//运算函数
void AddtionCall::calculate(int argc, char *argv[])
{
	bool error_flag=false;
	int myoptind = 1;
	int ch;
	const char *myoptarg = nullptr;

	while ((ch = px4_getopt(argc, argv, "a:b:", &myoptind, &myoptarg)) != EOF)
	{
		switch (ch) {
		case 'a':
			_num1= atof(myoptarg);
			break;
		case 'b':
			_num2 = atof(myoptarg);
			break;
		default:
			error_flag = true;
			break;
					}
	}
	if (error_flag)
	{
		PX4_WARN("unrecognized flag");
	}
	else
	{
		if (_num1<=10000&&_num1>=-10000&&_num2<=10000&&_num2>=-10000)
			{
				_result=_num1+_num2;
				PX4_INFO("The result is: %f",(double)_result);
			}
		else
			{
				PX4_WARN("Your data is beyond the limitation!");
			}
	}
}
//入口函数
int my_example_app_main(int argc, char *argv[])
{
	//PX4_INFO("Hello Sky!");
	//static bool module_start=false;
	static AddtionCall add_num;
	if (argc < 2)
		{
			PX4_WARN( "AddtionCall: my_example_app {start|stop|status|help|test -a num1 -b num2}");
		}
	if (!strcmp(argv[1], "start")&&argc==2)
		{
			//if(!module_start)
			{
				add_num.start();
			}
		_exit(0);
		}
	else if (!strcmp(argv[1], "stop")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.stop();
			// delete add_num;
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "status")&&argc==2) {
		//if (add_num!=NULL)
		{
			add_num.status();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "help")&&argc==2)
		{
		//if (add_num!=NULL)
		{
			add_num.help();
		}
		_exit(0);
		}
	else if (!strcmp(argv[1], "test")&&argc>2)
	{
		//if (add_num!=NULL)
		{
			add_num.calculate(argc, argv);
		}
		_exit(0);
	}
	else
	{
		PX4_INFO("This is my_example_app module, using for addition opreation. You can use the following command:");
		PX4_INFO(" my_example_app start //---- start the module");
		PX4_INFO(" my_example_app stop //---- stop the module");
		PX4_INFO(" my_example_app status //---- show the current state of the module");
		PX4_INFO("my_example_app help //---- show what you can do in this module");
		PX4_INFO("my_example_app test -a num1 -b num2 //----caculate your data, there is a limit that \"a<=10000 and a>=-10000\", and ’b’ has the same limitation. ");
	}
	return OK;
}

完成程序包含tasks和pthreads两个线程的计数,所以控制台中两个计数进程同时打印计数信息。
在这里插入图片描述

四、uORB通信发布和订阅数据

飞控各个功能模块之间是独立运行的,但对整个系统来说,进程间需要有数据传输,这需要uORB通信机制来实现。
uORB机制包含两个部分,功能模块订阅和发布;

  • 模块订阅数据:订阅主题、设置订阅数据最小间隔、检查数据更新、拷贝数据;
  • 模块发布数据:公告主题、发布主题数据、取消订阅主题。
    在这里插入图片描述uORB消息A、B、C、D在源码中是预先定义的结构体变量,是由.msg文件编译自动生成的,所有可以订阅或者发布的数据均可以在Firmware/msg/文件夹下找到。这些消息我们可以理解为预先定义好的全局变量,任何功能模块都能去读写,但需要通过特定的订阅和发布过程。
    注意uORB通信不能用于进程内通信,一个功能模块不能既订阅某主题的同时又发布该主题。

1.模块订阅数据

  1. 订阅数据需要先订阅该数据主题,使用的函数格式为:
int orb_subscribe(const struct orb_metadata *meta)
参数:meta:uORB元对象,可以认为是主题id;
返回值:正确返回一个句柄,句柄是你自己定义的,初始化为-1,
eg:
int my_topic_sub=-1;
int my_topic_sub = orb_subscribe(ORB_ID(my_topic));
句柄在某种程度上可以理解为订阅主题的地址.

注意:函数都是直接对句柄操作,句柄的初始化放在进程的初始化部分。

  1. 设置订阅数据更新周期
    我们可以自己设定读取的频率,这个类似于多长时间去读一次,也有很多模块不设置就会一直检测。
    eg:orb_set_interval(sensor_sub_fd, 1000);
    参数:handle:orb_subscribe函数返回的句柄;
       interval:间隔时间,单位ms;
    该函数返回OK表示成功;返回ERROR表示错误。

  2. 检查数据更新
    订阅数据后需要知道数据是否更新,检查数据更新的方法有两种:第一是阻塞等待,就是程序卡在此处等着更新;第二种是使用while循环周期性的检查:
    阻塞等待函数格式:

int poll(struct pollfd fds[], nfds_t nfds, int timeout)
参数:fds[]:结构类型的数组,进程可以同时等待很多个主题数据,当有数据更新时,判断一下是谁;fd也经常被称为文件描述符
nfds:用于标记数组fds中的结构体元素的总数量;
timeout:阻塞时间,单位是ms,即如果timeout=1000,那么就是1000ms=1s。
eg:
//定义阻塞等待结构体,下面的定义中实际该结构体只有一个成员
px4_pollfd_struct_t fds[] =
{
	{ .fd = sensor_sub_fd,
 	 .events = POLLIN },//sensor_sub_fd是订阅主题返回的句柄
};
//阻塞等待
int poll_ret = px4_poll(fds, 1, 1000);

timeout是最长等待时间,返回为0表示超过最长等待时间没有等到数据更新;>0就是在时间内收到了数据,<0表示出错。如果正常拿到数据,函数返回的是拿到数据剩下的阻塞时间。
检查更新函数格式为:

int orb_check(int handle, bool *updated)
参数:handle:句柄
    *updated:是否更新的标志位,如果主题数据已经更新了,那么updated会被设置为true。
eg:下面是一个从订阅到检查更新到拷贝数据到最后关闭订阅的完整过程
int subsys_sub = orb_subscribe(ORB_ID(subsystem_info));//订阅数据
	struct subsystem_info_s info;
	memset(&info, 0, sizeof(info));
	...
orb_check(subsys_sub, &updated);//检查数据有没有更新
	if (updated) {
		orb_copy(ORB_ID(subsystem_info), subsys_sub, &info);//拷贝数据
		}
...
//px4_close(subsys_sub);//一般在最后线程退出后,飞控不再工作后才需要
orb_unsubscribe(subsys_sub);//一般在析构函数里会使用

调用orb_copy函数接收、处理数据后,updated会自动设置为false,所以这种方式在被第一次检测到后,后面再检测时就不再算是新数据了。

  1. 拷贝数据
    函数格式为:
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
参数:meta:消息ID
     handle:句柄
     buffer: 存放拷贝出的数据,是自己事先定义的数据结构类型
返回值:拷贝成功,返回OK(0)
eg:
sensor_combined_s raw ;//结构体变量,和发布主题数据的格式一致
orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
  1. 取消订阅数据
int orb_unsubscribe(int handle)
参数:handle:主题句柄;
返回值:OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
ret = orb_unsubscribe(handle);

2.模块发布数据

发布数据主要分为公告主题和发布主题两部分:

  1. 公告主题:
1.orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)
功能:先公告主题,相当于注册,只要一次就行了。meta为公告的消息ID,data为公告的原始数据。
说明:在发布主题之前是必须的;否则订阅者虽然能订阅,但是得不到数据;
eg:
orb_advert_t _to_system_power;
_to_system_power(nullptr)//一般要先在构造函数中初始化成nullptr
_to_system_power = orb_advertise(ORB_ID(system_power), &system_power);
2.orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void
*data, int *instance,int priority)
说明:主要用于同一主题的多个数据来源的发布,比如飞控连接了多个超声波传感器,那么可
以发布到同一主题下;同时,需要订阅的时候,也是用:
int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance)进行订阅。
eg:_distance_sensor_topic = orb_advertise_multi(ORB_ID(distance_sensor), &ds_report,&_orb_class_instance, ORB_PRIO_LOW);
  1. 发布主题数据
int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)
功能:公告之后就可以发布主题数据。meta为公告的消息ID,handle为公告返回的句柄,这个句柄初始化为null,data为发布的数据。
  1. 新建主题
    首先如果我们需要订阅和公告的主题已经存在,通过上面的方式就能完成数据的订阅或发布。否则需要新建这个主题,新建主题的步骤如下:
    在目录Firmware/msg/下,新建文件my_test_data.msg,在其中写入:
#Date:2020年11月16日
#File name:my_test_data.msg
#Author:李俊龙
#说明:新建一个消息主题
uint64 timestamp	# time since system start (microseconds)
uint8  my_test_data     #测试数据   

.msg必须定义时间戳变量,否则无法编译。
接着在Firmware/msg/下的CMakeList.txt文件中,添加my_test_data.msg
在这里插入图片描述打开终端编译固件,系统会在Firmware/build/px4_fmu-v3_default/uORB/topics/文件夹中自动生成对应的头文件my_test_data.h
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PX4飞控试炼之路 的相关文章

  • pixhawk px4 spi设备驱动

    此篇blog是以nuttx官网介绍为出发点 xff0c 先分析如何初始化的 xff0c 再分析如何读取传感器数据的 xff0c 最后对比了字符型设备操作和spi驱动的实现方式的差别 如有错误还请指正 6 字符型设备 所有的结构体和API都在
  • pixhawk: px4代码初学分析:追溯电机控制--pwm输出

    追溯电机控制 pwm输出 正常工作状态下pwm输出过程简述 xff1a 其他状态下pwm输出 xff1a 正常工作状态下pwm输出过程简述 xff1a 姿态解算部分得出姿态控制量通过px4io cpp把姿态控制量发送给IOIO串口读取姿态控
  • PX4使用I2C方式添加自定义传感器(1)

    PX4使用I2C方式添加自定义传感器 xff08 1 xff09 前言 毕业设计就是要在PX4上添加一个传感器 xff08 角度传感器 xff09 xff0c 由于板子上的接口数量很少 xff0c 很是宝贵 最后只能选择通过I2C通信方式
  • PX4通过I2C方式添加自定义传感器(2)

    PX4 I2C通信方式传感器驱动分析 xff08 以ets airspeed为例 xff09 1 说明 这篇文章我们就来看看I2C传感器的驱动过程 xff0c 当然里面也有很多东西我不是很理解 xff0c 所以仅谈我领悟的一些东西 我就以e
  • PX4通过I2C方式添加自定义传感器(3)

    添加自定义传感器并实现数据的发送和订阅 1 前期准备 1 1 建立文件夹和相关文件配置 我是在src drivers distance sensor文件夹下操作的 xff0c 当然其他文件夹下都类似 首先建立了两个文件夹angle sour
  • px4自定义mavlink收不到消息的问题

    px4版本1 12稳定版 最近在做px4二次开发相关工作 按照网上的一些教程自定义了一个mavlink消息用来控制无人机 按照教程里面的单独开了一个xml来定义消息 最后生成的消息在px4端通过流传输的方式自己写的客户端可以收到消息 但是客
  • 【2020-8-9】APM,PX4,GAZEBO,MAVLINK,MAVROS,ROS之间的关系以及科研设备选型

    0 概述 无人机自主飞行平台可以分为四个部分 xff1a 动力平台 xff0c 飞行控制器 xff0c 机载电脑和模拟平台 动力平台 xff1a 负责执行飞行任务 xff0c 包括螺旋桨 电机 机架等 xff0c 用于科研的一般都是F380
  • PX4/Pixhawk---uORB深入理解和应用

    The Instructions of uORB PX4 Pixhawk 软件体系结构 uORB 主题发布 主题订阅 1 简介 1 1 PX4 Pixhawk的软件体系结构 PX4 Pixhawk的软件体系结构主要被分为四个层次 xff0c
  • PX4 GAZEBO无人机添加相机并进行图像识别

    PX4 GAZEBO无人机添加摄像头并进行图像识别 在之前完成了ROS的安装和PX4的安装 xff0c 并可以通过roslaunch启动软件仿真 接下来为无人及添加相机 xff0c 并将图像用python函数读取 xff0c 用于后续操作
  • PX4飞控之PWM输出控制

    PX4飞控之PWM输出控制 多旋翼电调如好盈XRotor xff0c DJI通用电调等都支持PWM信号来传输控制信号 常用的400Hz电调信号对应周期2500us xff0c 一般使用高电平时间1000us 2000us为有效信号区间 xf
  • 无人机仿真—PX4编译,gazebo仿真及简单off board控制模式下无人机起飞

    无人机仿真 PX4编译 xff0c gazebo仿真及简单off board控制模式下无人机起飞 前言 在上篇记录中 xff0c 已经对整体的PX4仿真环境有了一定的了解 xff0c 现如今就要开始对无人机进行起飞等仿真环境工作 xff0c
  • PX4模块设计之二十一:uORB消息管理模块

    PX4模块设计之二十一 xff1a uORB消息管理模块 1 uORB模块构建模式2 uORB消息管理函数2 1 状态查询2 2 资源利用2 3 模块启动2 4 模块停止3 uORB消息接口3 1 消息主题注册3 2 消息主题去注册3 3
  • PX4模块设计之三十:Hysteresis类

    PX4模块设计之三十 xff1a Hysteresis类 1 Hysteresis类简介2 Hysteresis类成员变量介绍3 Hysteresis类迟滞逻辑4 Hysteresis类重要方法4 1 Hysteresis bool ini
  • PX4模块设计之三十六:MulticopterPositionControl模块

    PX4模块设计之三十六 xff1a MulticopterPositionControl模块 1 MulticopterPositionControl模块简介2 模块入口函数2 1 主入口mc pos control main2 2 自定义
  • PX4模块设计之四十五:param模块

    PX4模块设计之四十五 xff1a param模块 1 param模块简介2 模块入口函数param main3 重要函数列表4 总结5 参考资料 1 param模块简介 Description Command to access and
  • PX4-4-任务调度

    PX4所有的功能都封装在独立的模块中 xff0c uORB是任务间数据交互和同步的工具 xff0c 而管理和调度每个任务 xff0c PX4也提供了一套很好的机制 xff0c 这一篇我们分享PX4的任务调度机制 我们以PX4 1 11 3版
  • px4仿真无法起飞问题(Failsafe enabled: no datalink)

    报错信息 问题描述 xff1a 使用JMAVSim和gazebo仿真px4起飞时报错如下 xff1a WARN commander Failsafe enabled no datalink 说不安全 解决方法 打开QGC 就可以起飞了
  • 【px4】运行mavsdk中的offboard example

    运行MAVSDK中的offboard例子时无人机不执行 想控制无人机前后左右移动 xff0c 在按照官方教程实现offboard 插件的时候 发现用action插件能正常起飞和降落 但是一旦执行到offboard的插件代码的时候就会自动降落
  • PX4 OffBoard Control

    终于还是走上了这一步 xff0c 对飞控下手 xff0c 可以说是一张白纸了 记录一下学习的过程方便以后的查阅 目录 一 ubuntu18 04配置px4编译环境及mavros环境 二 PX4的OffBoard控制 1 搭建功能包 2 编写
  • 无人机PX4使用动捕系统mocap的位置实现控制+MAVROS

    动捕系统Optitrack xff0c 有很高的定位精度 xff0c 能够给无人机提供比较精确的位置信息 xff0c 因此如果实验室有条件 xff0c 都可以买一套动捕系统 动捕系统的原理 xff1a 光学式动作捕捉依靠一整套精密而复杂的光

随机推荐

  • 《动手学ROS2》6.5Gazebo介绍

    本系列教程作者 xff1a 小鱼 公众号 xff1a 鱼香ROS QQ交流群 xff1a 139707339 教学视频地址 xff1a 小鱼的B站 完整文档地址 xff1a 鱼香ROS官网 版权声明 xff1a 如非允许禁止转载与商业用途
  • Pixhawk无人机精准降落

    转自 xff1a http blog sina com cn s blog 6266a8840102xa77 html 多标记识别 xff0c 比官方的IR LOCK还要灵活 xff1a
  • 三位数的IMU长什么样?二位数的呢?不要钱的呢?| 为FishBot配置IMU惯性测量单元

    大家好 xff0c 我是小鱼 xff0c 上节课通过配置两轮差速控制器我们已经成功的让fishbot在gazebo中动了起来 xff0c 本节课我们通过给fishbot的URDF配置IMU传感器插件 xff0c 让IMU模块工作起来 1 惯
  • 本文之后,再无ROS安装问题 | 10分钟在Windows搭建好ROS开发环境

    很多新手刚开始学习ROS的时候因为用不惯Linux xff0c 加上安装虚拟机的各种和安装ROS的时候各种难受的折腾 xff0c 看着挺让人心疼 xff0c 所以本篇文章主要针对Windows上利用Docker和各种奇淫技巧让你轻松在Win
  • YoloV5+ROS2直接发布出物品坐标

    大家好 xff0c 我是秃头了依然在敲代码的小鱼 最近小鱼又整了一个开源库 xff0c 结合YOLOV5订阅图像数据和相机参数 xff0c 直接给出一个可以给出识别物品的坐标信息 xff0c 方便进行识别和抓取 xff0c 目前适配完了2D
  • IKFAST太难配?小鱼OpenRave的Docker镜像分享给你!

    大家好 xff0c 我是倒腾了一天机械臂的小鱼 众所周知 xff0c moveit自带的求解器 xff0c 使用的是数值解 xff0c 求解结果稳定性是要看人品的 xff0c 不过Moveit支持通过配置来更换求解器 xff0c 目前比较公
  • 小鱼的一键安装系列

    小鱼的一键安装系列 鱼香ROS介绍 xff1a 鱼香ROS是由机器人爱好者共同组成的社区 xff0c 欢迎一起参与机器人技术交流 进交流群请加机器人小伊微信 xff1a fishros2048 文章信息 xff1a 标题 xff1a 小鱼的
  • ubuntu18.04 ros-melodic 在安装ros依赖包时总是出现E软件包无法定位

    ubuntu18 04 ros melodic 在安装ros依赖包时总是出现E软件包无法定位 鱼香ROS介绍 xff1a 鱼香ROS是由机器人爱好者共同组成的社区 xff0c 欢迎一起参与机器人技术交流 进交流群请加机器人小伊微信 xff1
  • 树莓派4B(Pi buster)一键安装apt存在问题,WARNING: apt does not have a stable CLI interface

    树莓派4B xff08 Pi buster xff09 一键安装apt存在问题 xff0c WARNING apt does not have a stable CLI interface 鱼香ROS介绍 xff1a 鱼香ROS是由机器人爱
  • ROS1/ROS2版本代号

    ROS1 ROS2版本代号 鱼香ROS介绍 xff1a 鱼香ROS是由机器人爱好者共同组成的社区 xff0c 欢迎一起参与机器人技术交流 进群加V xff1a fishros2048 文章信息 xff1a 标题 xff1a ROS1 ROS
  • 一键安装失败

    一键安装失败 鱼香ROS介绍 xff1a 鱼香ROS是由机器人爱好者共同组成的社区 xff0c 欢迎一起参与机器人技术交流 进群加V xff1a fishros2048 文章信息 xff1a 标题 xff1a 一键安装失败 原文地址 xff
  • 在ROS 2中使用QT,显示话题内容,详细配置代码

    功能包类型选ament cmake xff0c 核心代码是这两句 xff0c 把spin单独出来 xff0c 不然接收不到话题数据 std thread spin thread listener gt void rclcpp spin li
  • 进程切换-

    实验课之前有一些问题 中断机制 第一个问题 xff1a interrupt的两个状态源头 xff1a 外中断多由随机中断 xff08 异步中断 xff09 造成 xff0c 如鼠标点击 xff0c 键盘输入 xff1b 内终端多由故障终端
  • 前后端分离项目 — 基于SpringSecurity OAuth2.0用户认证

    1 前言 现在的好多项目都是基于APP移动端以及前后端分离的项目 xff0c 之前基于Session的前后端放到一起的项目已经慢慢失宠并淡出我们视线 xff0c 尤其是当基于SpringCloud的微服务架构以及Vue React单页面应用
  • Android Studio com.android.support:percent 导入错误

    看第一行代码 xff08 第二版的 xff09 书 xff0c 讲了一个关于PercentFrameLayout和PercentRelativeLayout的部分 xff0c 书上在build gradle中导入了com android s
  • BS_TYPEMASK 重定义

    问题来源 CButtonST从VC6移植到VS2008的时候 xff0c 会提示下面语句重定义 span class token macro property span class token directive keyword defin
  • Ubuntu安装python

    CentOS 安装 Python3 没什么坑 xff0c 按照步骤一步步来就可以了 但 Ubuntu 安装 Python3 的坑却不少 xff0c 这里总结一下 xff0c 避免以后继续踩坑 我用的是 ubuntu16 04 xff0c 安
  • 【深度学习】Pytorch中cv2的错误 TypeError: Expected Ptr<cv::UMat> for argument ‘img‘

    错误出现的原因应该为数据之间出了冲突 xff0c 需要在某些地方调用 copy 函数 当我查看画框后的原图和旋转后的图片时 xff0c 发现它只是旋转的原图 xff0c 而不是将原图直接拷贝一份再旋转 下面为代码 xff1a import
  • PX4飞控开发——环境配置及编译ubuntu18.04

    PX4飞控学习过程 xff08 随着学习不断更新 xff0c 欢迎讨论 xff09 一 安装时遇到的问题二 添加环境变量及编译三 解決E 无法获得锁 var lib dpkg lock frontend open 11 资源暂时不可用 的問
  • PX4飞控试炼之路

    PX4飞控试炼之路 一 地面站控制台上显示输出 Hello Sky 二 利用飞控计算加法三 飞控按指定频率计数 xff0c 并将计数结果反馈到控制台1 利用while循环实现计数2 利用 96 hrt call every 96 周期回调函