如何将C++类成员函数作为参数传递,实现机器人自主循环调度?
机器人自主循环功能实现方案
需求描述
制作一款趣味机器人,需要添加**自主循环(Autonomous loop)**功能,让机器人遵循预设命令自主运行。核心需求是实现一个类似如下的函数:
DoFunctionForMe(start_time, end_time, function, start_arguments, end_arguments);
该函数需要在start_time时刻使用start_arguments执行传入的function,并在end_time时刻使用end_arguments执行该函数。
例如期望实现的逻辑:在1到8秒期间驱动机器人,同时在7到9秒期间移动机械臂,代码示例如下:
void Robot::AutoMainLoop(){ DoFunctionForMe(1,8,Drive,{12,12},{0,0}); DoFunctionForMe(7,9,Move,{1,0},{0,0}); }
当前代码结构
class Drivetrain{ public: Drivetrain(); void Drive(int forward, int left){ drive(forward, left); // 实际驱动逻辑更复杂 } }; class Mechanism{ public: Mechanism(); void Move(int up, int down){ move(up, down); // 实际机械臂移动逻辑更复杂 } }; class Robot{ public: void DrivenMainLoop(); // 手动控制循环 void AutoMainLoop(); // 自主运行循环 private: Drivetrain m_drivetrain{}; Mechanism m_mechanism{}; }; void Robot::DrivenMainLoop(){ m_drivetrain.Drive(2,12); m_mechanism.Move(1,0); } void Robot::AutoMainLoop(){ // 需要实现自主逻辑 }
遇到的问题
尝试使用函数指针实现时出现错误,错误信息翻译为:
指向绑定函数的指针只能用于调用函数
可行实现方案
核心思路
由于涉及不同类的成员函数,直接用函数指针会因为绑定对象的问题报错。我们可以用std::function和std::tuple封装任务,同时维护一个任务队列,在AutoMainLoop的循环中根据当前时间处理每个任务的触发和结束动作。
具体实现步骤
- 定义任务结构体:存储任务的时间参数、要执行的函数以及参数
- 封装
DoFunctionForMe函数:将任务添加到队列中 - 在
AutoMainLoop中循环处理任务:根据当前时间判断是否触发任务的开始或结束动作
代码实现
首先添加必要的头文件:
#include <vector> #include <functional> #include <tuple> #include <chrono> #include <thread>
然后修改Robot类,添加任务队列和相关函数:
class Robot{ public: void DrivenMainLoop(); void AutoMainLoop(); // 封装DoFunctionForMe,支持不同成员函数和参数 template<typename Func, typename... Args> void DoFunctionForMe(double start_time, double end_time, Func&& func, std::tuple<Args...> start_args, std::tuple<Args...> end_args){ m_tasks.emplace_back(start_time, end_time, [func, start_args](){ std::apply(func, start_args); }, [func, end_args](){ std::apply(func, end_args); }); } private: Drivetrain m_drivetrain{}; Mechanism m_mechanism{}; // 任务结构体 struct AutoTask{ double start_time; double end_time; std::function<void()> start_action; std::function<void()> end_action; bool started; bool finished; AutoTask(double st, double et, std::function<void()> sa, std::function<void()> ea) : start_time(st), end_time(et), start_action(std::move(sa)), end_action(std::move(ea)), started(false), finished(false) {} }; std::vector<AutoTask> m_tasks; // 获取自主循环运行的当前时间(单位:秒) double GetCurrentAutoTime(){ static auto start = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now(); return std::chrono::duration<double>(now - start).count(); } };
实现AutoMainLoop逻辑:
void Robot::AutoMainLoop(){ // 初始化任务队列(每次进入自主循环时清空并重新添加任务) m_tasks.clear(); // 绑定成员函数和对象,传入参数 DoFunctionForMe(1.0, 8.0, std::bind(&Drivetrain::Drive, &m_drivetrain, std::placeholders::_1, std::placeholders::_2), std::make_tuple(12, 12), std::make_tuple(0, 0)); DoFunctionForMe(7.0, 9.0, std::bind(&Mechanism::Move, &m_mechanism, std::placeholders::_1, std::placeholders::_2), std::make_tuple(1, 0), std::make_tuple(0, 0)); // 自主循环主逻辑 while(true){ // 根据实际机器人的循环条件调整,比如直到自主模式结束 double current_time = GetCurrentAutoTime(); for(auto& task : m_tasks){ if(!task.started && current_time >= task.start_time){ task.start_action(); task.started = true; } if(task.started && !task.finished && current_time >= task.end_time){ task.end_action(); task.finished = true; } } // 检查是否所有任务完成,退出循环 bool all_finished = true; for(const auto& task : m_tasks){ if(!task.finished){ all_finished = false; break; } } if(all_finished){ break; } // 添加适当的延迟,避免占用过多CPU std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }
方案说明
- 使用
std::bind将成员函数和对象绑定,生成可调用的函数对象,解决了成员函数需要对象实例的问题 - 用
std::tuple存储参数,通过std::apply将参数传递给函数 - 任务队列跟踪每个任务的状态(是否已开始、是否已结束),确保每个动作只执行一次
GetCurrentAutoTime函数需要根据机器人的实际计时系统调整,比如从自主模式启动时开始计时
内容的提问来源于stack exchange,提问作者ThatAlo
相关产品推荐
相关产品推荐

