为了进行循环测试,我从网上找了timer函数:
#pragma once
#include <functional>
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>
class Timer
{
public:
Timer(): _expired(true), _try_to_expire(false)
{}
Timer(const Timer& timer)
{
_expired = timer._expired.load();
_try_to_expire = timer._try_to_expire.load();
}
~Timer()
{
stop();
}
void start(int interval, std::function<void()> task)
{
// is started, do not start again
if (_expired == false)
return;
// start async timer, launch thread and wait in that thread
_expired = false;
std::thread([this, interval, task]() {
while (!_try_to_expire)
{
// sleep every interval and do the task again and again until times up
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
task();
}
{
// timer be stopped, update the condition variable expired and wake main thread
std::lock_guard<std::mutex> locker(_mutex);
_expired = true;
_expired_cond.notify_one();
}
}).detach();
}
void startOnce(int delay, std::function<void()> task)
{
std::thread([delay, task]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
task();
}).detach();
}
void stop()
{
// do not stop again
if (_expired)
return;
if (_try_to_expire)
return;
// wait until timer
_try_to_expire = true; // change this bool value to make timer while loop stop
{
std::unique_lock<std::mutex> locker(_mutex);
_expired_cond.wait(locker, [this] {return _expired == true; });
// reset the timer
if (_expired == true)
_try_to_expire = false;
}
}
private:
std::atomic<bool> _expired; // timer stopped status
std::atomic<bool> _try_to_expire; // timer is in stop process
std::mutex _mutex;
std::condition_variable _expired_cond;
};
然后将硬件交互部分代码进行更新,
#pragma once
#include "hardware/motor_spi.h"
#include <cstdint>
const uint32_t LEFT = 0;
const uint32_t RIGHT = 1;
const uint32_t FRONT = 0;
const uint32_t REAR = 1;
const uint32_t ABAD = 0;
const uint32_t HIP = 1;
const uint32_t KNEE = 2;
class HardwareBridge
{
struct convert_motor
{
float sign;
float offset;
};
struct convert_leg
{
convert_motor motor[3];
};
struct convert_spi
{
convert_leg leg[2];
};
public:
void initialize(); // init communication
void finalize();
void update(); // send cmd to motor and get state
void start(); // enable motor
void stop(); // disable motor
void printInfo();
// i: spi, LEFT, RIGHT, j: leg, FRONT, REAR, k:motor, ABAD, HIP, KNEE, motor cmd
void setJoint(int i, int j, int k, float p_des, float v_des, float kp, float kd, float t_ff);
public:
int m_spi_fd[2];
spine_cmd_t m_cmd[2];
spine_state_t m_state[2];
convert_spi m_converter[2];
};
#include "bridge/hardware_bridge.h"
#include "hardware/motor_spi.h"
#include "rt/rt_util.h"
#include "util/crc.h"
#include <iostream>
const char* name[] = {"/dev/spidev0.0", "/dev/spidev0.1"};
const uint32_t spi_count = 2;
const uint32_t leg_count = 2;
const uint32_t motor_count = 3;
// init communication
void HardwareBridge::initialize()
{
spi_open(m_spi_fd[0], name[0]);
spi_open(m_spi_fd[1], name[1]);
// init a very soft behavior
this->setJoint(LEFT, FRONT, ABAD, 0, 0, 5, 0, 0);
this->setJoint(LEFT, FRONT, HIP, 0, 0, 5, 0, 0);
this->setJoint(LEFT, FRONT, KNEE, 0, 0, 5, 0, 0);
this->setJoint(LEFT, REAR, ABAD, 0, 0, 5, 0, 0);
this->setJoint(LEFT, REAR, HIP, 0, 0, 5, 0, 0);
this->setJoint(LEFT, REAR, KNEE, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, FRONT, ABAD, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, FRONT, HIP, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, FRONT, KNEE, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, REAR, ABAD, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, REAR, HIP, 0, 0, 5, 0, 0);
this->setJoint(RIGHT, REAR, KNEE, 0, 0, 5, 0, 0);
}
void HardwareBridge::finalize()
{
spi_close(m_spi_fd[0]);
spi_close(m_spi_fd[1]);
}
// send cmd to motor and get state
void HardwareBridge::update()
{
static uint8_t tx[sizeof(spine_cmd_t)] = {0};
static uint8_t rx[sizeof(spine_cmd_t)] = {0};
spine_state_t temp_state;
for(int i = 0; i < spi_count; ++i)
{
m_cmd[i].crc = calculate((uint8_t*)&m_cmd[i], sizeof(spine_cmd_t) - 4);
memcpy(tx, &m_cmd[i], sizeof(spine_cmd_t));
int rv = transfer(m_spi_fd[i], tx, rx, sizeof(spine_cmd_t));
(void)rv;
memcpy(&temp_state, rx, sizeof(spine_state_t));
uint32_t crc = calculate((uint8_t*)&temp_state, sizeof(spine_state_t) - 4);
if(crc == temp_state.crc)
{
memcpy(&m_state[i], rx, sizeof(spine_state_t));
std::cout << "[CRC] crc correct" << std::endl;
}
else
{
memcpy(&m_state[i], rx, sizeof(spine_state_t));
std::cout << "[CRC] crc error" << std::endl;
}
}
}
// enable motor
void HardwareBridge::start()
{
for(int i = 0; i < spi_count; ++i)
{
for(int j = 0; j < leg_count; ++j)
{
m_cmd[i].leg[j].flag = 1;
}
}
}
// disable motor
void HardwareBridge::stop()
{
for(int i = 0; i < spi_count; ++i)
{
for(int j = 0; j < leg_count; ++j)
{
m_cmd[i].leg[j].flag = 0;
}
}
}
void HardwareBridge::printInfo()
{
std::cout << "[Robot State]" << std::endl;
for(int i = 0; i < 2; ++i)
{
for(int j = 0; j < 2; ++j)
{
for(int k = 0; k < 3; ++k)
{
motor_data_t& m = m_state[i].leg[j].motor[k];
std::cout << "spi: " << j << " leg: " << j << " id: " << k << " : " << m.p << ", " << m.v << ", " << m.t << std::endl;
}
}
}
std::cout << std::endl;
}
// i: spi, LEFT, RIGHT, j: leg, FRONT, REAR, k:motor, ABAD, HIP, KNEE, motor cmd
void HardwareBridge::setJoint(int i, int j, int k, float p_des, float v_des, float kp, float kd, float t_ff)
{
m_cmd[i].leg[j].motor[k].p_des = p_des;
m_cmd[i].leg[j].motor[k].v_des = v_des;
m_cmd[i].leg[j].motor[k].kp = kp;
m_cmd[i].leg[j].motor[k].kd = kd;
m_cmd[i].leg[j].motor[k].t_ff = t_ff;
}
测试的循环控制代码如下:
#include "hardware/motor_spi.h"
#include "bridge/hardware_bridge.h"
#include "util/crc.h"
#include "util/timer.h"
#include <cmath>
#include <iostream>
#include <unistd.h>
HardwareBridge bridge;
float angle = 0;
void simple_control()
{
std::cout << "trigger func1" << std::endl;
angle += 0.1;
bridge.setJoint(LEFT, FRONT, ABAD, 0.5 * sin(angle), 0, 5, 0, 0);
bridge.update();
bridge.printInfo();
}
int main()
{
//prefaultStack();
//setupScheduler();
create_lookup_table();
bridge.initialize();
bridge.start();
Timer timer;
std::cout << "--- start period timer ----" << std::endl;
timer.start(100, std::bind(simple_control));
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
timer.stop();
std::cout << "--- stop period timer ----" << std::endl;
bridge.finalize();
return 0;
}
运行代码后一直CRC校验错误,感觉还是STM32部分存在问题,在反复的SPI通信调用中存在bug。