提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
2文章目录
- 前言
-
一、机器狗内部传感器
- 1.机器狗不能走直线怎么解决?
- 2.如何使机器狗到达固定的位置
- 3.对于使用树莓派时图像数据时机器狗可能遇到的问题
- 4.树莓派和X86的通信
- 总结
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、机器狗内部哪些数据对于调试有用?
对于学习宇树A1机器狗,机器狗分为两种模式一种为底层模式,一种为高层模式一般在比赛或者应用中我们都使用高层模式,直接调用封装好的接口,通过给速度直接赋值来实现机器狗的运动,但是由于官方的SDK里的底层模式准确度并不高,比如给变量cmd.forwardSpeed直接赋值机器狗并不能做到走直线那么我们就要通过传感器返回的值来自己重新写控制程序
1.机器狗不能走直线怎么解决?
我们使用高层模式的HighState.imu.rpy[2]变量可以得到机器狗左右方向的偏航角,通过给HighCmd.rotateSpeed赋值可以修正左右偏航角使得机器狗可以大致走直线,下面是我们在比赛中写的代码
修正机器狗偏航角代码使机器狗能够走直线:
if(state.imu.rpy[2]*180/3.1415926 > 0.5){
cmd.mode = 2;
cmd.rotateSpeed=-0.1f;
}
if(state.imu.rpy[2]*180/3.1415926 < -0.5){
cmd.mode = 2;
cmd.rotateSpeed=0.1f;
}
这个代码在使用过程如果机器狗被摔了内部陀螺仪受到影响可能会出现一启动机器狗就失控直接摔地上的情况对于这种情况我们的解决方法是1.在机器狗行走一段时间以后再加入修正代码2.把if语句里的判断的角度给大一点,对于这种情况我的猜测可能是因为陀螺仪初始值会受到影响,可能初始值不为0并且比我们给的角度限制的范围大很多,那么机器狗就一直调整不回来就会前进和偏航角调整一起进行然后导致失控
2.如何使机器狗到达固定的位置
对于HighState反馈回来的forwardPosition和sidePosition我们可以使用但是在官方给出的SDK里面明确说了这个可能会不准,并且这个会随着时间的增加和行走距离的增加误差会变得很大,那么对于要走到精确的位置就不太可能,官方说好像机器狗前面有一个深度相机但是可以测距但是我们还不确定能不能使用,对于ROBOCOM今年赛题是要走一段距离然后去扎气球,我们的方法是通过程序内部计数然后给机器狗速度,通过计数次数和速度大小来控制前进的距离,这样比较准确
motiontime += 2;
if(motiontime>9000&&motiontime<11000){
cmd.mode=2;
cmd.forwardSpeed=0.3;
}
3对于使用树莓派时图像数据时机器狗可能遇到的问题
对于ROBOCOM省赛赛题有两个,第一是给板子盖章,第二是从起始线的中心点位置出发去扎破气球这就需要使用树莓派识别,我们是通过返回要识别的物体的中心点的像素点位置,机器狗拍取一张照片的从最左到最右一共640个像素点我们通过比较识别物体中心点的像素点位置和图片中心点像素点位置来判断机器狗偏左还是偏右然后在X86上根据像素点误差写PID控制程序由于我们这里PID控制的对象是像素点误差这里就有个问题,下面两张图是气球和狗的左右相对位置不变只是距离不一样,在不同的距离我们要调整同样的左右距离,左图气球中心点的像素点位置和图片中心点位置误差明显比右边大,所以我们写PID控制时需要根据不同的远近距离调整kp的值,而不是kp一成不变,如果kp不调整在快靠近气球的时候像素点误差波动会非常大那么这是机器狗就会出现很大的左右晃动甚至失控,一直振荡,一般随着距离靠近我们会减小kp的值避免出现较大的振荡
if(motiontime>0&&motiontime<4000){// 基于像素点误差的pid控制
cmd.mode=2;
cmd.forwardSpeed=0.6;
cmd.sideSpeed=-0.01*error;//kp=0.005
}
if(motiontime>4000&&motiontime<4500){
cmd.mode=2;
cmd.forwardSpeed=0.3;
cmd.sideSpeed=-0.005*error;
}
4.树莓派和X86的通信
对于两块板子的通信使用socket通信下面实例代码先定义一个UdpReceiver类,这是X86里通信代码
const int PC_PORT = 32000;
const char PC_IP_ADDR[] = "192.168.123.161";
const int UDP_BUFFER_SIZE = 128;
// Globals
bool RUNNING = true; // indicating whether the main loop is running
std::string movement;
double command_number = 0.0;
class UdpReceiver
{
public:
std::deque<std::string> msg_queue;
/* udp receiver thread */
void run()
{
/********************UDP_Receiving_Initializing********************/
socklen_t fromlen;
struct sockaddr_in server_recv;
struct sockaddr_in hold_recv;
int sock_recv = socket(AF_INET, SOCK_DGRAM, 0);
int sock_length_recv = sizeof(server_recv);
bzero(&server_recv, sock_length_recv);
server_recv.sin_family = AF_INET;
server_recv.sin_addr.s_addr = INADDR_ANY;
server_recv.sin_port = htons(PC_PORT); // Setting port of this program
bind(sock_recv, (struct sockaddr *)&server_recv, sock_length_recv);
fromlen = sizeof(struct sockaddr_in);
char buf_UDP_recv[UDP_BUFFER_SIZE]; // for holding UDP data
/******************************************************************/
while (RUNNING)
{
memset(buf_UDP_recv, 0, sizeof buf_UDP_recv);
int datalength = recvfrom(sock_recv, buf_UDP_recv, UDP_BUFFER_SIZE, 0, (struct sockaddr *)&hold_recv, &fromlen);
std::string str(buf_UDP_recv);
msg_queue.push_back(buf_UDP_recv);
while(msg_queue.size()>1){
msg_queue.pop_front();
}
movement = msg_queue[0];
}
}
};
int main(void) {
while(1){
sleep(10);
};
if (udp_recv_thread.joinable())
{
udp_recv_thread.joinable();
}
return 0;
}
树莓派通信代码
ip_remote = '192.168.123.161' # upboard IP
port_remote = 32000 # port
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # setup socket
udp_socket.sendto(str(max_center).encode("utf8"), (ip_remote, port_remote))
总结
后期将会把ROBOCOM国赛和省赛完整代码写出,欢迎大家一起交流学习,后期还会继续更新宇树A1机器狗学习过程