赞
踩
首先应该是对传感器数据的获取,对于传感器数据的获取其实在上一章的样例中已经封装完成了,所以在这里我会对所买开发板里的代码简单解释一下。
//路径 Sense-HAT-B-Demo\ICM-20948\Raspberry Pi\bcm2835 //main.c 原始代码 #include "ICM20948.h" int main(int argc, char* argv[]) { IMU_EN_SENSOR_TYPE enMotionSensorType; IMU_ST_ANGLES_DATA stAngles; IMU_ST_SENSOR_DATA stGyroRawData; IMU_ST_SENSOR_DATA stAccelRawData; IMU_ST_SENSOR_DATA stMagnRawData; imuInit(&enMotionSensorType); if(IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) { printf("Motion sersor is ICM-20948\n" ); } else { printf("Motion sersor NULL\n"); } while(1) { imuDataGet( &stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData); printf("\r\n /-------------------------------------------------------------/ \r\n"); printf("\r\n Roll: %.2f Pitch: %.2f Yaw: %.2f \r\n",stAngles.fRoll, stAngles.fPitch, stAngles.fYaw); printf("\r\n Acceleration: X: %d Y: %d Z: %d \r\n",stAccelRawData.s16X, stAccelRawData.s16Y, stAccelRawData.s16Z); printf("\r\n Gyroscope: X: %d Y: %d Z: %d \r\n",stGyroRawData.s16X, stGyroRawData.s16Y, stGyroRawData.s16Z); printf("\r\n Magnetic: X: %d Y: %d Z: %d \r\n",stMagnRawData.s16X, stMagnRawData.s16Y, stMagnRawData.s16Z); bcm2835_delay(100); } return 0; }
这里以c程序为例,样例中有python的代码逻辑相同;
imuDataGet( &stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData);
上边这一句是获取传感器上所有数据的函数封装,会将所有获取到的数据写入传入变量中。我所用到的俯仰翻转数据在stAngles
中,定义数据结构如下:
typedef struct imu_st_angles_data_tag
{
float fYaw;
float fPitch;
float fRoll;
}IMU_ST_ANGLES_DATA;
数据的获取是通过对IIC接口读取传感器原始数据处理后的来的,原始处理函数附在下边,有兴趣的可以看一下:
float q0, q1, q2, q3; void imuDataGet(IMU_ST_ANGLES_DATA *pstAngles, IMU_ST_SENSOR_DATA *pstGyroRawData, IMU_ST_SENSOR_DATA *pstAccelRawData, IMU_ST_SENSOR_DATA *pstMagnRawData) { float MotionVal[9]; int16_t s16Gyro[3], s16Accel[3], s16Magn[3]; icm20948AccelRead(&s16Accel[0], &s16Accel[1], &s16Accel[2]); icm20948GyroRead(&s16Gyro[0], &s16Gyro[1], &s16Gyro[2]); icm20948MagRead(&s16Magn[0], &s16Magn[1], &s16Magn[2]); MotionVal[0]=s16Gyro[0]/32.8; MotionVal[1]=s16Gyro[1]/32.8; MotionVal[2]=s16Gyro[2]/32.8; MotionVal[3]=s16Accel[0]; MotionVal[4]=s16Accel[1]; MotionVal[5]=s16Accel[2]; MotionVal[6]=s16Magn[0]; MotionVal[7]=s16Magn[1]; MotionVal[8]=s16Magn[2]; //这后边的6句是重点 imuAHRSupdate((float)MotionVal[0] * 0.0175, (float)MotionVal[1] * 0.0175, (float)MotionVal[2] * 0.0175, (float)MotionVal[3], (float)MotionVal[4], (float)MotionVal[5], (float)MotionVal[6], (float)MotionVal[7], MotionVal[8]); pstAngles->fPitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch pstAngles->fRoll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll pstAngles->fYaw = atan2(-2 * q1 * q2 - 2 * q0 * q3, 2 * q2 * q2 + 2 * q3 * q3 - 1) * 57.3; pstGyroRawData->s16X = s16Gyro[0]; pstGyroRawData->s16Y = s16Gyro[1]; pstGyroRawData->s16Z = s16Gyro[2]; pstAccelRawData->s16X = s16Accel[0]; pstAccelRawData->s16Y = s16Accel[1]; pstAccelRawData->s16Z = s16Accel[2]; pstMagnRawData->s16X = s16Magn[0]; pstMagnRawData->s16Y = s16Magn[1]; pstMagnRawData->s16Z = s16Magn[2]; return; } //通过下边这个函数计算刷新了俯仰数据 void imuAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { float norm; float hx, hy, hz, bx, bz; float vx, vy, vz, wx, wy, wz; float exInt = 0.0, eyInt = 0.0, ezInt = 0.0; float ex, ey, ez, halfT = 0.024f; float q0q0 = q0 * q0; float q0q1 = q0 * q1; float q0q2 = q0 * q2; float q0q3 = q0 * q3; float q1q1 = q1 * q1; float q1q2 = q1 * q2; float q1q3 = q1 * q3; float q2q2 = q2 * q2; float q2q3 = q2 * q3; float q3q3 = q3 * q3; norm = invSqrt(ax * ax + ay * ay + az * az); ax = ax * norm; ay = ay * norm; az = az * norm; norm = invSqrt(mx * mx + my * my + mz * mz); mx = mx * norm; my = my * norm; mz = mz * norm; // compute reference direction of flux hx = 2 * mx * (0.5f - q2q2 - q3q3) + 2 * my * (q1q2 - q0q3) + 2 * mz * (q1q3 + q0q2); hy = 2 * mx * (q1q2 + q0q3) + 2 * my * (0.5f - q1q1 - q3q3) + 2 * mz * (q2q3 - q0q1); hz = 2 * mx * (q1q3 - q0q2) + 2 * my * (q2q3 + q0q1) + 2 * mz * (0.5f - q1q1 - q2q2); bx = sqrt((hx * hx) + (hy * hy)); bz = hz; // estimated direction of gravity and flux (v and w) vx = 2 * (q1q3 - q0q2); vy = 2 * (q0q1 + q2q3); vz = q0q0 - q1q1 - q2q2 + q3q3; wx = 2 * bx * (0.5 - q2q2 - q3q3) + 2 * bz * (q1q3 - q0q2); wy = 2 * bx * (q1q2 - q0q3) + 2 * bz * (q0q1 + q2q3); wz = 2 * bx * (q0q2 + q1q3) + 2 * bz * (0.5 - q1q1 - q2q2); // error is sum of cross product between reference direction of fields and direction measured by sensors ex = (ay * vz - az * vy) + (my * wz - mz * wy); ey = (az * vx - ax * vz) + (mz * wx - mx * wz); ez = (ax * vy - ay * vx) + (mx * wy - my * wx); if(ex != 0.0f && ey != 0.0f && ez != 0.0f) { exInt = exInt + ex * Ki * halfT; eyInt = eyInt + ey * Ki * halfT; ezInt = ezInt + ez * Ki * halfT; gx = gx + Kp * ex + exInt; gy = gy + Kp * ey + eyInt; gz = gz + Kp * ez + ezInt; } q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT; q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT; q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT; q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT; norm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); q0 = q0 * norm; q1 = q1 * norm; q2 = q2 * norm; q3 = q3 * norm; }
本来在做控制时查了一些树莓派控制sg90的博客,但是当时没有记录下来,所以下边一方面我会把已经完成的代码发出来,一方面附上临时查的参考博客:
#include "ICM20948.h" #include <wiringPi.h> #include <softPwm.h> //===============Announce================================ bool initServo(); int checkAngle(int angle); //===============Define var========================= int pinRoll = 4; int pinPitch = 5; int rangeMin = 9; int rangeMax = 27; int stepDelay = 0; //================Main================================ int main(int argc, char* argv[]) { IMU_EN_SENSOR_TYPE enMotionSensorType; IMU_ST_ANGLES_DATA stAngles; IMU_ST_SENSOR_DATA stGyroRawData; IMU_ST_SENSOR_DATA stAccelRawData; IMU_ST_SENSOR_DATA stMagnRawData; imuInit(&enMotionSensorType); if(initServo() == true) printf("init successfully..\n"); else { printf("init unsuccessfully..\n"); return 0; } if(IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) { printf("Motion sersor is ICM-20948\n" ); } else { printf("Motion sersor NULL\n"); } int servoAngle = 0; while(1) { imuDataGet( &stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData); servoAngle = checkAngle((int)stAngles.fRoll); softPwmWrite(pinRoll, servoAngle); servoAngle = checkAngle((int)stAngles.fPitch); softPwmWrite(pinPitch, servoAngle); delay(stepDelay); } softPwmWrite(pinRoll, 0); softPwmWrite(pinPitch, 0); return 0; } bool initServo() { if(wiringPiSetup()==-1) printf("init unsuccessfully..\n"); else printf("init successfully..\n"); if(softPwmCreate(pinRoll,15,200) != 0) { printf("softPwmCreat pinRoll unsuccessfully..\n"); return false; } if(softPwmCreate(pinPitch,15,200) != 0) { printf("softPwmCreat pinPitch unsuccessfully..\n"); return false; } return true; } //这个函数的作用是将计算出来的-90° - 90°折算为舵机softPwmWrite INPUT的值 //我控制舵机旋转0 - 180度对应的值是 9 - 27 所以刚好用边方法可以折算过来 int checkAngle(int angle) { return((angle + 90)/10 + rangeMin); }
也尝试了直接使用树莓派gpio中的p1口直接输出pwm波,但是电机很抖可能是由于周期没有设对,如果有兴趣可以自己尝试下设置pwm产生的时间周期应该也能控制好舵机;
录了个小的gif,不太能看的清楚。
在接线的时候有如下注意:
1、最好通过外部电源来给舵机供电;
2、外部电源的地与树莓派地通过线连起来,否则舵机可能会抖动;
3、本文使用这款sg90线色的说明,咖色- 地,红色-9v,黄色-信号;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。