Design of Akaman Steering for ROS Car

Keywords: angular

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?

  1. Firstly, the problem of moving direction conversion is solved.
  2. 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.

https://www.docin.com/p-966015827.html

/*

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,....

Posted by pentinat on Tue, 24 Sep 2019 21:11:20 -0700