Preface:
From undergraduate to sophomore year, after learning the ROS foundation, I like the upside-down ROS car because of the recommendation of the instructor. What 2-wheel differential type, 3-wheel omnidirectional wheel, 4-wheel McNam wheel, 4-wheel Ackerman steering car I've done.
In the past few days, I freely designed a self-sufficient Akman turning car. Although it was ugly, it also helped my friends to learn, design DIY, and learn the most basic ROS navigation mapping, learning slam, moving_base, learning other path planning algorithms such as A* and so on.
We know that:
As shown in the figure, the left still image and the right GIF are shown.
Based on the knowledge of mechanical design or mechanical principles, we consider:
If we design the Ackerman structure ourselves, how can we use the connecting rod skillfully and conveniently to achieve the horizontal turn?
- Firstly, the problem of moving direction conversion is solved.
- Connecting rods as few as possible
The following materials can be purchased from Taobao!
Here I adopt: big ball bearing + steering seat small ball bearing + coupling to install steering seat
Installation is as follows:
After installing the seats better, do the connecting rod again.
Choose the right length of connecting rod according to the size of connecting rod and car body!
Next, install the steering gear. The purpose of installing him is to convert the vertical speed into the horizontal speed through the connecting rod.
The installation position of the steering gear is arranged according to the length of the connecting rod and the length of the body.
Above the main parts can be purchased Taobao, go to DIY!
Code part
Written in C++, this article first briefly summarizes the main algorithms, you can see this article.
/* Time: 2009.9.25 (latest update time) Location: Three Gorges Dam Area, Yichang, Hubei Province */ //Define Variables long long LeftticksPerMeter = 0; long long rightticksPerMeter = 0; long long LeftticksPer2PI = 0; long long rightticksPer2PI = 0;
void dzactuator::run() { int run_rate = 50; //HZ ros::Rate rate(run_rate); double x = 0.0; double y = 0.0; double th = 0.0; ros::Time current_time, last_time; while(ros::ok()){ ros::spinOnce(); current_time = ros::Time::now(); velDeltaTime = (current_time - last_time).toSec(); last_time = ros::Time::now(); recvCarInfoKernel(); pub_9250(); #if 1 if(encoderLeft > 220 || encoderLeft < -220) encoderLeft = 0; if(encoderRight > 220 || encoderRight < -220) encoderRight = 0; encoderRight = -encoderRight; detEncode = (encoderLeft + encoderRight)/2; detdistance = detEncode/ticksPerMeter; detth = (encoderRight - encoderLeft)*2*PI/ticksPer2PI; linearSpeed = detdistance/velDeltaTime; angularSpeed = detth/velDeltaTime; if(detdistance != 0){ x += detdistance * cos(th); y += detdistance * sin(th); } if(detth != 0){ th += detth; } // printf("linearSpeed=%.2f,detEncode=%.2f\n",linearSpeed,detEncode); if(calibrate_lineSpeed == 1){ printf("x=%.2f,y=%.2f,th=%.2f,linearSpeed=%.2f,,detEncode=%.2f,LeftticksPerMeter = %lld,rightticksPerMeter = %lld\n",x,y,th,linearSpeed,detEncode,LeftticksPerMeter,rightticksPerMeter); } //send command to stm32 sendCarInfoKernel(); geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th); //next, we'll publish the odometry message over ROS nav_msgs::Odometry odom; odom.header.stamp = current_time; odom.header.frame_id = "odom"; odom.child_frame_id = "base_link"; //Odometer information, easy to calculate PID odom.pose.pose.position.x = x; odom.pose.pose.position.y = y; odom.pose.pose.position.z = 0.0; odom.pose.pose.orientation = odom_quat; odom.twist.twist.linear.x = linearSpeed; odom.twist.twist.linear.y = 0; odom.twist.twist.linear.z = 0; odom.twist.twist.angular.x = 0; odom.twist.twist.angular.y = 0; odom.twist.twist.angular.z = angularSpeed; if(encoderLeft == 0 && encoderRight == 0){ odom.pose.covariance = {1e-9, 0, 0, 0, 0, 0, 0, 1e-3, 1e-9, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e-9}; odom.twist.covariance = {1e-9, 0, 0, 0, 0, 0, 0, 1e-3, 1e-9, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e-9}; }else{ odom.pose.covariance = {1e-3, 0, 0, 0, 0, 0, 0, 1e-3, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e3}; odom.twist.covariance = {1e-3, 0, 0, 0, 0, 0, 0, 1e-3, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e6, 0, 0, 0, 0, 0, 0, 1e3}; } //publish the message pub_odom.publish(odom); geometry_msgs::Point32 point[4]; point[0].x = -0.18; point[0].y = -0.12; point[1].x = 0.18; point[1].y = -0.12; point[2].x = 0.18; point[2].y = 0.12; point[3].x = -0.18; point[3].y = 0.12; geometry_msgs::PolygonStamped poly; poly.header.stamp = current_time; poly.header.frame_id = "base_link"; poly.polygon.points.push_back(point[0]); poly.polygon.points.push_back(point[1]); poly.polygon.points.push_back(point[2]); poly.polygon.points.push_back(point[3]); poly_pub.publish(poly); #endif rate.sleep(); } }
Next time, I don't know when it will be, so I want to post the whole motion algorithm in the next article, and make comments for you to understand,....